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.
Files changed (4) hide show
  1. package/README.md +57 -14
  2. package/dist/cli.js +393 -199
  3. package/dist/index.js +572 -175
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4904,9 +4904,9 @@ function cloneIssues(issues) {
4904
4904
  }
4905
4905
  var recursive = /* @__PURE__ */ new WeakMap();
4906
4906
  function isRecursive(inst, stack) {
4907
- const cached3 = recursive.get(inst);
4908
- if (cached3 !== void 0)
4909
- return cached3;
4907
+ const cached4 = recursive.get(inst);
4908
+ if (cached4 !== void 0)
4909
+ return cached4;
4910
4910
  if (stack.has(inst))
4911
4911
  return true;
4912
4912
  stack.add(inst);
@@ -18662,15 +18662,15 @@ function visit(schema, fnOrHandlers) {
18662
18662
  };
18663
18663
  const cache = /* @__PURE__ */ new Map();
18664
18664
  function run2(s) {
18665
- const cached3 = cache.get(s);
18666
- if (cached3 === RESOLVING) {
18665
+ const cached4 = cache.get(s);
18666
+ if (cached4 === RESOLVING) {
18667
18667
  return new $ZodLazy({
18668
18668
  type: "lazy",
18669
18669
  getter: () => cache.get(s)
18670
18670
  });
18671
18671
  }
18672
- if (cached3 !== void 0)
18673
- return cached3;
18672
+ if (cached4 !== void 0)
18673
+ return cached4;
18674
18674
  cache.set(s, RESOLVING);
18675
18675
  const inner = mapInner(s);
18676
18676
  const mapped = fn(inner, inner !== s);
@@ -18919,7 +18919,10 @@ var pluginOptionsSchema = external_exports.object({
18919
18919
  runRoot: external_exports.string().min(1).default("/tmp"),
18920
18920
  opencodeBin: external_exports.string().min(1).prefault(discoverOpencodeBin),
18921
18921
  configSource: external_exports.string().prefault(() => join(homedir(), ".config", "opencode", "opencode.jsonc")),
18922
- roleTimeoutMs: external_exports.number().int().min(5e3).default(6e5)
18922
+ roleTimeoutMs: external_exports.number().int().min(5e3).default(6e5),
18923
+ /** Exam venue: absolute dirs tmpfs-masked from the candidate (grader files,
18924
+ * other runs). Empty = venue runs unsandboxed and SAYS SO on the voice face. */
18925
+ examMaskDirs: external_exports.array(external_exports.string().min(2)).default([])
18923
18926
  }).prefault({}),
18924
18927
  /** v1.1 E4/F1: seats only from allowed model-id prefixes; deny at launch. */
18925
18928
  modelPolicy: external_exports.object({
@@ -18986,7 +18989,36 @@ import { dirname, join as join2 } from "node:path";
18986
18989
  var SIBYL_STATE_FILE_ENV = "SIBYL_STATE_FILE";
18987
18990
  var RUN_KINDS = ["consult", "swarm"];
18988
18991
  var RUN_STATUSES = ["running", "done", "failed", "suspended"];
18992
+ var INDEPENDENCE_STATUSES = ["INDEPENDENT", "NOT-INDEPENDENT", "UNVERIFIABLE"];
18993
+ function isIndependenceShape(v) {
18994
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
18995
+ const o = v;
18996
+ if (!INDEPENDENCE_STATUSES.includes(o["status"])) return false;
18997
+ if (!Array.isArray(o["convenerChain"]) || !o["convenerChain"].every((s) => typeof s === "string" && s.length > 0)) return false;
18998
+ return typeof o["evidence"] === "string";
18999
+ }
19000
+ function rebuildIndependence(v) {
19001
+ return { status: v.status, convenerChain: [...v.convenerChain], evidence: v.evidence };
19002
+ }
18989
19003
  var VERDICT_TAGS = ["APPROVE", "REJECT", "CANNOT_ANSWER"];
19004
+ var INSTRUMENT_NAME = /^[a-z0-9-]{1,40}$/;
19005
+ var HEX64 = /^[0-9a-f]{64}$/;
19006
+ function isInstrumentShape(v) {
19007
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
19008
+ const o = v;
19009
+ if (typeof o["rulesHash"] !== "string" || !HEX64.test(o["rulesHash"])) return false;
19010
+ const comps = o["components"];
19011
+ if (typeof comps !== "object" || comps === null || Array.isArray(comps)) return false;
19012
+ return Object.entries(comps).every(
19013
+ ([name, hash3]) => INSTRUMENT_NAME.test(name) && typeof hash3 === "string" && HEX64.test(hash3)
19014
+ );
19015
+ }
19016
+ function rebuildInstrument(v) {
19017
+ return {
19018
+ rulesHash: v.rulesHash,
19019
+ components: Object.fromEntries(Object.entries(v.components).map(([name, hash3]) => [name, hash3]))
19020
+ };
19021
+ }
18990
19022
  function oneOf(v, opts) {
18991
19023
  return opts.some((o) => o === v);
18992
19024
  }
@@ -19062,6 +19094,20 @@ function validateEntry(raw) {
19062
19094
  if (typeof notes !== "string") return { ok: false, reason: "notes must be a string when present" };
19063
19095
  record3.notes = notes;
19064
19096
  }
19097
+ const instrument = field(raw, "instrument");
19098
+ if (instrument !== void 0) {
19099
+ if (!isInstrumentShape(instrument)) {
19100
+ return { ok: false, reason: "instrument must be {rulesHash: 64-hex, components: name->64-hex} when present" };
19101
+ }
19102
+ record3.instrument = rebuildInstrument(instrument);
19103
+ }
19104
+ const independence = field(raw, "independence");
19105
+ if (independence !== void 0) {
19106
+ if (!isIndependenceShape(independence)) {
19107
+ return { ok: false, reason: "independence must be {status: INDEPENDENT|NOT-INDEPENDENT|UNVERIFIABLE, convenerChain: string[], evidence: string} when present" };
19108
+ }
19109
+ record3.independence = rebuildIndependence(independence);
19110
+ }
19065
19111
  return { ok: true, record: record3 };
19066
19112
  }
19067
19113
  function byCreatedAt(a, b) {
@@ -19190,8 +19236,8 @@ var RunStore = class {
19190
19236
  };
19191
19237
 
19192
19238
  // src/tools/consult.ts
19193
- import { join as join3 } from "node:path";
19194
- import { writeFile as writeFile2 } from "node:fs/promises";
19239
+ import { join as join5 } from "node:path";
19240
+ import { appendFile, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
19195
19241
 
19196
19242
  // node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
19197
19243
  var external_exports2 = {};
@@ -31895,6 +31941,123 @@ async function runPersona(opts) {
31895
31941
  }
31896
31942
  }
31897
31943
 
31944
+ // src/independence.ts
31945
+ var MAX_CHAIN_DEPTH = 16;
31946
+ var MIN_INLINE_MATCH = 64;
31947
+ var DRAFTING_TOOLS = ["write", "edit", "multiedit", "patch", "apply_patch"];
31948
+ var PATH_KEYS = ["filePath", "path"];
31949
+ var CONTENT_KEYS = ["content", "text", "oldString", "newString"];
31950
+ function isDraftingTool(name) {
31951
+ return name !== void 0 && DRAFTING_TOOLS.includes(name);
31952
+ }
31953
+ function pathTargets(input2, artifactPath) {
31954
+ if (input2 === void 0) return false;
31955
+ for (const key of PATH_KEYS) {
31956
+ const v = input2[key];
31957
+ if (typeof v === "string" && v.trim() === artifactPath) return true;
31958
+ }
31959
+ return false;
31960
+ }
31961
+ function contentMatches(input2, needle) {
31962
+ if (input2 === void 0) return false;
31963
+ for (const key of CONTENT_KEYS) {
31964
+ const v = input2[key];
31965
+ if (typeof v !== "string") continue;
31966
+ if (v.trim() === needle) return true;
31967
+ if (needle.length >= MIN_INLINE_MATCH && v.includes(needle)) return true;
31968
+ if (needle.length >= MIN_INLINE_MATCH && v.length >= MIN_INLINE_MATCH && needle.includes(v.trim()) && v.trim().length > 0) {
31969
+ return true;
31970
+ }
31971
+ }
31972
+ return false;
31973
+ }
31974
+ function scanMessages(rows, convenerIndex, sid, artifact) {
31975
+ for (const [mi, m] of rows.entries()) {
31976
+ for (const p of m.parts ?? []) {
31977
+ if (p.type === "tool" && isDraftingTool(p.tool)) {
31978
+ if (artifact.kind === "path" && pathTargets(p.input, artifact.source)) {
31979
+ return `drafting evidence: ${p.tool} on ${artifact.source} in convener-chain session ${sid} (chain position ${String(convenerIndex)}, message ${String(mi)})`;
31980
+ }
31981
+ if (artifact.kind === "inline" && contentMatches(p.input, artifact.text.trim())) {
31982
+ return `drafting evidence: ${p.tool} content matches the inline artifact in convener-chain session ${sid} (chain position ${String(convenerIndex)}, message ${String(mi)})`;
31983
+ }
31984
+ }
31985
+ if (artifact.kind === "inline" && m.info?.role === "assistant" && p.type === "text" && typeof p.text === "string" && artifact.text.trim().length >= MIN_INLINE_MATCH && p.text.includes(artifact.text.trim())) {
31986
+ return `drafting evidence: assistant text in convener-chain session ${sid} (chain position ${String(convenerIndex)}, message ${String(mi)}) contains the inline artifact`;
31987
+ }
31988
+ }
31989
+ }
31990
+ return null;
31991
+ }
31992
+ async function assessIndependence(client, directory, convenerSessionID, artifact) {
31993
+ const noChain = { status: "UNVERIFIABLE", convenerChain: [], evidence: "" };
31994
+ if (convenerSessionID.length === 0) {
31995
+ return { ...noChain, evidence: "headless invocation: no calling session to resolve the convener chain against" };
31996
+ }
31997
+ if (!artifact.ok) {
31998
+ return { ...noChain, evidence: "artifact unreadable at assessment time" };
31999
+ }
32000
+ if (client.session.get === void 0) {
32001
+ return { ...noChain, evidence: "engine client exposes no session.get seam (chain cannot be walked; not a verdict of independence)" };
32002
+ }
32003
+ if (client.session.messages === void 0) {
32004
+ return { ...noChain, convenerChain: [convenerSessionID], evidence: "engine client exposes no session.messages seam (drafting scan impossible)" };
32005
+ }
32006
+ const chain = [convenerSessionID];
32007
+ const seen = new Set(chain);
32008
+ let walkIncomplete = false;
32009
+ let walkReason = "";
32010
+ let current = convenerSessionID;
32011
+ for (let depth = 0; depth < MAX_CHAIN_DEPTH; depth += 1) {
32012
+ const res = await client.session.get({ path: { id: current } });
32013
+ if (res.error !== void 0 || res.data === void 0) {
32014
+ walkIncomplete = true;
32015
+ walkReason = `session.get(${current}) refused (${res.error === void 0 ? "no data" : JSON.stringify(res.error).slice(0, 120)})`;
32016
+ break;
32017
+ }
32018
+ const parent = res.data.parentID ?? "";
32019
+ if (parent.length === 0) break;
32020
+ if (seen.has(parent)) {
32021
+ walkReason = `cycle at ${parent} (store anomaly); walk stopped`;
32022
+ break;
32023
+ }
32024
+ chain.push(parent);
32025
+ seen.add(parent);
32026
+ current = parent;
32027
+ }
32028
+ let scanFailed = "";
32029
+ for (const [i, sid] of chain.entries()) {
32030
+ const res = await client.session.messages({ path: { id: sid }, query: { directory } });
32031
+ if (res.error !== void 0 || res.data === void 0) {
32032
+ scanFailed = scanFailed.length > 0 ? scanFailed : `messages(${sid}) refused`;
32033
+ continue;
32034
+ }
32035
+ const hit = scanMessages(res.data, i, sid, artifact);
32036
+ if (hit !== null) {
32037
+ return { status: "NOT-INDEPENDENT", convenerChain: chain, evidence: hit };
32038
+ }
32039
+ }
32040
+ if (scanFailed.length > 0 || walkIncomplete) {
32041
+ const why = [walkIncomplete ? walkReason : "", scanFailed].filter((s) => s.length > 0).join("; ");
32042
+ return { status: "UNVERIFIABLE", convenerChain: chain, evidence: `partial read: ${why} \u2014 independence was NOT established` };
32043
+ }
32044
+ const note = walkReason.length > 0 ? `; walk note: ${walkReason}` : "";
32045
+ return {
32046
+ status: "INDEPENDENT",
32047
+ convenerChain: chain,
32048
+ evidence: `scanned ${String(chain.length)} session(s) of the convener chain to root; no drafting evidence within read scope${note}`
32049
+ };
32050
+ }
32051
+ function independenceLabel(v) {
32052
+ return v.status === "INDEPENDENT" ? "independence=INDEPENDENT" : `independence=${v.status} \u2014 ${v.evidence}`;
32053
+ }
32054
+
32055
+ // src/instrument.ts
32056
+ import { createHash } from "node:crypto";
32057
+
32058
+ // src/chamber/protocol.ts
32059
+ import { join as join4 } from "node:path";
32060
+
31898
32061
  // src/verdict/index.ts
31899
32062
  var OPEN_BRACE = 123;
31900
32063
  var CLOSE_BRACE = 125;
@@ -32046,6 +32209,135 @@ async function parseVerdict(text, repair) {
32046
32209
  return rejectVerdict(`${first.why} + repair-invalid: ${second.why}`);
32047
32210
  }
32048
32211
 
32212
+ // src/state/chamber.ts
32213
+ import { mkdir as mkdir2, readFile as readFile2, readdir, rename as rename2, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
32214
+ import { homedir as homedir3 } from "node:os";
32215
+ import { dirname as dirname2, isAbsolute, join as join3, resolve } from "node:path";
32216
+ var DEFAULT_CHAMBER_LEDGER = join3(homedir3(), ".sibyl", "chamber-ledger.jsonl");
32217
+ var TERMINAL_STATES = ["CONVERGED", "NEEDS_ROUND", "MEMBER_LOST", "TIMEOUT"];
32218
+ var RUN_PROFILES = ["review", "exam"];
32219
+ var RECORD_SCHEMA = 1;
32220
+ function asStringArray(v) {
32221
+ return Array.isArray(v) && v.every((x) => typeof x === "string") ? [...v] : null;
32222
+ }
32223
+ function isRosterRef(v) {
32224
+ if (typeof v !== "object" || v === null) return false;
32225
+ const o = v;
32226
+ const modelOk = o["policyCheck"] === "deny" ? typeof o["modelId"] === "string" : isNonEmptyString(o["modelId"]);
32227
+ return isNonEmptyString(o["role"]) && isNonEmptyString(o["slot"]) && modelOk && (o["policyCheck"] === "allow" || o["policyCheck"] === "deny") && (o["denyReason"] === void 0 || typeof o["denyReason"] === "string");
32228
+ }
32229
+ function isArtifactRef(v) {
32230
+ if (typeof v !== "object" || v === null) return false;
32231
+ const o = v;
32232
+ return isNonEmptyString(o["role"]) && typeof o["round"] === "number" && Number.isInteger(o["round"]) && o["round"] >= 0 && isNonEmptyString(o["phase"]) && isNonEmptyString(o["relPath"]) && typeof o["sha256"] === "string" && /^[0-9a-f]{64}$/.test(o["sha256"]) && typeof o["bytes"] === "number" && o["bytes"] >= 0 && isNonEmptyString(o["presentAt"]) && (o["rc"] === null || typeof o["rc"] === "number") && (o["signal"] === null || typeof o["signal"] === "string") && typeof o["timedOut"] === "boolean";
32233
+ }
32234
+ function validateChamberRecord(raw) {
32235
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return { ok: false, reason: "record is not an object" };
32236
+ const o = raw;
32237
+ if (o["schema"] !== RECORD_SCHEMA) return { ok: false, reason: `schema must be ${RECORD_SCHEMA}` };
32238
+ if (!isNonEmptyString(o["runId"])) return { ok: false, reason: "runId must be a non-empty string" };
32239
+ if (typeof o["serial"] !== "number" || !Number.isInteger(o["serial"]) || o["serial"] < 1) return { ok: false, reason: "serial must be a positive integer" };
32240
+ if (!RUN_PROFILES.includes(o["profile"])) return { ok: false, reason: `profile must be one of ${RUN_PROFILES.join("|")}` };
32241
+ if (!isNonEmptyString(o["goal"])) return { ok: false, reason: "goal must be a non-empty string" };
32242
+ if (typeof o["target"] !== "string") return { ok: false, reason: "target must be a string" };
32243
+ if (!isNonEmptyString(o["runDir"]) || !isAbsolute(o["runDir"])) return { ok: false, reason: "runDir must be an absolute path" };
32244
+ if (!isNonEmptyString(o["createdAt"]) || Number.isNaN(Date.parse(o["createdAt"]))) return { ok: false, reason: "createdAt must be ISO" };
32245
+ if (!isNonEmptyString(o["updatedAt"]) || Number.isNaN(Date.parse(o["updatedAt"]))) return { ok: false, reason: "updatedAt must be ISO" };
32246
+ if (o["terminal"] !== null && !TERMINAL_STATES.includes(o["terminal"])) return { ok: false, reason: "terminal must be null or a TerminalState" };
32247
+ if (!Array.isArray(o["roster"]) || !o["roster"].every(isRosterRef)) return { ok: false, reason: "roster must be an array of RosterRef" };
32248
+ const judgePool = asStringArray(o["judgePool"]);
32249
+ if (judgePool === null) return { ok: false, reason: "judgePool must be a string array" };
32250
+ if (!isNonEmptyString(o["seed"])) return { ok: false, reason: "seed must be a non-empty string" };
32251
+ if (typeof o["drawCommit"] !== "string" || !/^[0-9a-f]{64}$/.test(o["drawCommit"])) return { ok: false, reason: "drawCommit must be 64-hex" };
32252
+ if (typeof o["judgeModelId"] !== "string") return { ok: false, reason: "judgeModelId must be a string" };
32253
+ if (typeof o["rounds"] !== "number" || !Number.isInteger(o["rounds"]) || o["rounds"] < 0) return { ok: false, reason: "rounds must be a non-negative integer" };
32254
+ if (!Array.isArray(o["artifacts"]) || !o["artifacts"].every(isArtifactRef)) return { ok: false, reason: "artifacts must be an array of ArtifactRef" };
32255
+ if (typeof o["evidenceRows"] !== "number" || !Number.isInteger(o["evidenceRows"]) || o["evidenceRows"] < 0) return { ok: false, reason: "evidenceRows must be a non-negative integer" };
32256
+ if (o["notes"] !== void 0 && typeof o["notes"] !== "string") return { ok: false, reason: "notes must be a string when present" };
32257
+ if (o["instrument"] !== void 0 && !isInstrumentShape(o["instrument"])) {
32258
+ return { ok: false, reason: "instrument must be {rulesHash: 64-hex, components: name->64-hex} when present" };
32259
+ }
32260
+ if (o["independence"] !== void 0 && !isIndependenceShape(o["independence"])) {
32261
+ return { ok: false, reason: "independence must be {status: INDEPENDENT|NOT-INDEPENDENT|UNVERIFIABLE, convenerChain: string[], evidence: string} when present" };
32262
+ }
32263
+ const record3 = {
32264
+ schema: RECORD_SCHEMA,
32265
+ runId: o["runId"],
32266
+ serial: o["serial"],
32267
+ profile: o["profile"],
32268
+ goal: o["goal"],
32269
+ target: o["target"],
32270
+ runDir: o["runDir"],
32271
+ createdAt: o["createdAt"],
32272
+ updatedAt: o["updatedAt"],
32273
+ terminal: o["terminal"],
32274
+ roster: [...o["roster"]],
32275
+ judgePool,
32276
+ seed: o["seed"],
32277
+ drawCommit: o["drawCommit"],
32278
+ judgeModelId: o["judgeModelId"],
32279
+ rounds: o["rounds"],
32280
+ artifacts: [...o["artifacts"]],
32281
+ evidenceRows: o["evidenceRows"]
32282
+ };
32283
+ if (typeof o["notes"] === "string") record3.notes = o["notes"];
32284
+ if (o["instrument"] !== void 0) record3.instrument = rebuildInstrument(o["instrument"]);
32285
+ if (o["independence"] !== void 0) record3.independence = rebuildIndependence(o["independence"]);
32286
+ return { ok: true, record: record3 };
32287
+ }
32288
+ function parseLedgerLine(line) {
32289
+ const trimmed = line.trim();
32290
+ if (trimmed.length === 0) return { ok: false, reason: "blank line" };
32291
+ let parsed;
32292
+ try {
32293
+ parsed = JSON.parse(trimmed);
32294
+ } catch (err) {
32295
+ const msg = err instanceof Error ? err.message : String(err);
32296
+ return { ok: false, reason: `not a single JSON document (merged-row guard): ${msg}` };
32297
+ }
32298
+ const v = validateChamberRecord(parsed);
32299
+ if (!v.ok) return { ok: false, reason: v.reason };
32300
+ return { ok: true, record: v.record };
32301
+ }
32302
+ async function loadLedger(ledgerPath) {
32303
+ let text;
32304
+ try {
32305
+ text = await readFile2(ledgerPath, "utf8");
32306
+ } catch {
32307
+ return { rows: [], dropped: [] };
32308
+ }
32309
+ const rows = [];
32310
+ const dropped = [];
32311
+ const lines = text.split("\n");
32312
+ for (const [i, line] of lines.entries()) {
32313
+ if (line.trim().length === 0) continue;
32314
+ const v = parseLedgerLine(line);
32315
+ if (v.ok) rows.push(v.record);
32316
+ else dropped.push({ line: i + 1, reason: v.reason });
32317
+ }
32318
+ return { rows, dropped };
32319
+ }
32320
+
32321
+ // src/chamber/protocol.ts
32322
+ var CONTRACT = (writeTo, sections) => `OUTPUT CONTRACT: as you work, WRITE your result incrementally to ${writeTo} (create parent dirs as needed). Required structure: ${sections}. The file on disk is the only receipt that counts; keep the chat reply short. Write ONLY inside your run directory.`;
32323
+ function evidencePrompt(cfg, abs) {
32324
+ return `You are the EVIDENCE GATHERER of a Sibyl review chamber. Goal: "${cfg.goal}". Artifact under review: ${cfg.targetPath}. Enumerate evidence sources BROADLY first (list directories, then sample candidates: the target, adjacent docs/notes, logs, prior reviews). Breadth over depth; no deep analysis. Append one JSON object per line to ${abs}: {"probe":"<command or path read>","found":"<path>","class":"<doc|log|ledger|transcript|other>","note":"<one line>"}. ` + CONTRACT(abs, "one JSON object per line");
32325
+ }
32326
+ function proPrompt(cfg, round, abs, charges) {
32327
+ const chargeBlock = charges.length > 0 ? ` Prior open charges you must answer: ${JSON.stringify(charges)}.` : "";
32328
+ return `You are PRO (round ${String(round)}) in a Sibyl adversarial chamber. Goal: "${cfg.goal}". Read ${cfg.targetPath} and the evidence ledger ${join4(cfg.runDir, "evidence/evidence.jsonl")}, then argue for the strongest DEFENSIBLE version of the artifact: real merits, each claim citing ledger rows/paths, no spin.` + chargeBlock + " " + CONTRACT(abs, "## Case (numbered claims C1..Cn) and ## Evidence (claim -> cited paths)");
32329
+ }
32330
+ function conPrompt(cfg, round, abs, charges) {
32331
+ const chargeBlock = charges.length > 0 ? ` Previously contested \u2014 sharpen or drop: ${JSON.stringify(charges)}.` : "";
32332
+ return `You are CON (round ${String(round)}) in a Sibyl adversarial chamber. Goal: "${cfg.goal}". Read ${cfg.targetPath} and the evidence ledger ${join4(cfg.runDir, "evidence/evidence.jsonl")}, then attack the artifact: REAL defects only (correctness, missing evidence, unactionable claims), every charge citing a path or quote.` + chargeBlock + " " + CONTRACT(abs, "## Charges (numbered N1..Nn, each with cited evidence)");
32333
+ }
32334
+ function rebuttalPrompt(cfg, role, round, otherAbs, abs) {
32335
+ return `You are ${role.toUpperCase()} in cross-critique (round ${String(round)}). Read your opponent's artifact at ${otherAbs} plus the evidence ledger under ${join4(cfg.runDir, "evidence")} (you may re-probe primary sources). Respond point by point: CONCEDE what is right, REBUT what is wrong, replies keyed to the opponent's numbering. Never edit the opponent's file; write only your own new file. ` + CONTRACT(abs, "## Replies (K1..Kn keyed to opponent numbering) and ## Still Open");
32336
+ }
32337
+ function judgePrompt(cfg, round, paths, abs) {
32338
+ return `You are the INDEPENDENT JUDGE (round ${String(round)}) of a Sibyl chamber. You wrote nothing here. FIRST ACTION (no analysis before it): write a valid interim JSON to ${abs} \u2014 {"convergence":"NEEDS_ROUND","conclusion":"NEEDS_HUMAN","confidence":0,"reasons":["interim - judging"],"must_fix":[],"charges":[]} \u2014 then read the materials and OVERWRITE it with your final verdict as your last action. Read ALL of: ${paths.join(" ; ")} and the original target ${cfg.targetPath}. Re-probe AT MOST the three most load-bearing citations (open the cited file; a citation to a non-existent path is an automatic charge) \u2014 time-box yourself, breadth of verification below 3 is enough. Decide if the debate has CONVERGED (no material open charges) and the single review conclusion. The file must end holding EXACTLY ONE JSON object: {"convergence":"CONVERGED"|"NEEDS_ROUND","conclusion":"APPROVE"|"REJECT"|"NEEDS_HUMAN","confidence":<0..1>,"reasons":[...],"must_fix":[...],"charges":[...open charges; empty when CONVERGED]}. Also print the same JSON in your reply. Partial or non-JSON output = failure.`;
32339
+ }
32340
+
32049
32341
  // src/personas.ts
32050
32342
  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).';
32051
32343
  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;
@@ -32055,6 +32347,11 @@ var ARCHITECT_PERSONA = {
32055
32347
  system: ARCHITECT_SYSTEM,
32056
32348
  modelSlot: "architect"
32057
32349
  };
32350
+ function repairDemand(why) {
32351
+ 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>]}`;
32352
+ }
32353
+ var SWARM_JUDGE_SYSTEM = "You are the SIBYL swarm judge. You rule on pipeline reports with exactly one word: APPROVE, REJECT, or EXHAUSTED.";
32354
+ var SWARM_JUDGE_WORD_CONTRACT = "Respond with EXACTLY one word and nothing else: APPROVE, REJECT, or EXHAUSTED.";
32058
32355
  var PERSONAS = {
32059
32356
  MELCHIOR: {
32060
32357
  id: "MELCHIOR",
@@ -32081,9 +32378,108 @@ var BY_ID = new Map(
32081
32378
  PERSONA_IDS.map((id) => [id, PERSONAS[id]])
32082
32379
  );
32083
32380
 
32381
+ // src/instrument.ts
32382
+ function sha256Hex(text) {
32383
+ return createHash("sha256").update(text, "utf8").digest("hex");
32384
+ }
32385
+ var REPAIR_SENTINEL = "<parse-failure-why>";
32386
+ var SENTINEL_CFG = {
32387
+ runId: "sibyl-sentinel",
32388
+ runDir: "/sentinel/run",
32389
+ goal: "<goal>",
32390
+ targetPath: "/sentinel/target.md",
32391
+ ledgerPath: "/sentinel/ledger.jsonl",
32392
+ seed: "<seed>",
32393
+ maxRounds: 1,
32394
+ pool: { default: { providerID: "", modelID: "" } },
32395
+ policy: null,
32396
+ slots: { evidence: "default", pro: "default", con: "default", judge: "default" },
32397
+ judgePoolIds: [],
32398
+ lane: null
32399
+ };
32400
+ function chamberRoleTexts() {
32401
+ return {
32402
+ "chamber-evidence-prompt": evidencePrompt(SENTINEL_CFG, "/sentinel/evidence.jsonl"),
32403
+ "chamber-pro-prompt": proPrompt(SENTINEL_CFG, 1, "/sentinel/pro.md", []),
32404
+ "chamber-con-prompt": conPrompt(SENTINEL_CFG, 1, "/sentinel/con.md", []),
32405
+ "chamber-rebuttal-prompt": rebuttalPrompt(SENTINEL_CFG, "pro", 1, "/sentinel/con.md", "/sentinel/pro-rebuttal.md"),
32406
+ "chamber-judge-prompt": judgePrompt(SENTINEL_CFG, 1, ["/sentinel/pro.md", "/sentinel/con.md"], "/sentinel/judge.json")
32407
+ };
32408
+ }
32409
+ function componentTexts() {
32410
+ const texts = {
32411
+ "repair-grammar": repairDemand(REPAIR_SENTINEL),
32412
+ "swarm-judge-system": SWARM_JUDGE_SYSTEM,
32413
+ "swarm-judge-word-contract": SWARM_JUDGE_WORD_CONTRACT,
32414
+ ...chamberRoleTexts()
32415
+ };
32416
+ for (const id of COUNCILORS) {
32417
+ texts[`councilor-${id.toLowerCase()}`] = COUNCILOR_PERSONAS[id].system;
32418
+ }
32419
+ texts["architect-system"] = ARCHITECT_SYSTEM;
32420
+ return texts;
32421
+ }
32422
+ var cached3 = null;
32423
+ function instrumentFace() {
32424
+ if (cached3 !== null) return cached3;
32425
+ const components = {};
32426
+ for (const [name, text] of Object.entries(componentTexts())) {
32427
+ components[name] = sha256Hex(text);
32428
+ }
32429
+ const canonical = JSON.stringify(
32430
+ Object.keys(components).sort().map((name) => [name, components[name]])
32431
+ );
32432
+ cached3 = { rulesHash: sha256Hex(canonical), components };
32433
+ return cached3;
32434
+ }
32435
+ function rulesLabel(face) {
32436
+ return `rules=${face.rulesHash.slice(0, 12)}`;
32437
+ }
32438
+
32439
+ // src/terminal.ts
32440
+ var TERMINAL_STATUSES = ["ok", "error", "timeout"];
32441
+ var TERMINAL_DETAIL_MAX = 240;
32442
+ function sanitizeDetail(text) {
32443
+ const flat = text.replace(/\s+/g, " ").trim();
32444
+ return flat.length <= TERMINAL_DETAIL_MAX ? flat : `${flat.slice(0, TERMINAL_DETAIL_MAX)}\u2026`;
32445
+ }
32446
+ function classifyFailure(error105) {
32447
+ return /timeout|timed out|deadline/i.test(error105) ? "timeout" : "error";
32448
+ }
32449
+ function formatTerminalRow(at, row) {
32450
+ return `ts=${at.toISOString()} task=${row.task} worker=${row.worker} status=${row.status} session=${row.session.length > 0 ? row.session : "-"} detail=${sanitizeDetail(row.detail)}`;
32451
+ }
32452
+ function parseTerminalRows(text) {
32453
+ const byTask = /* @__PURE__ */ new Map();
32454
+ const lines = text.split("\n");
32455
+ for (const [i, line] of lines.entries()) {
32456
+ if (line.trim().length === 0) continue;
32457
+ const task = /(?:^| )task=(\S+)/.exec(line)?.[1];
32458
+ const statusRaw = /(?:^| )status=(\S+)/.exec(line)?.[1];
32459
+ if (task === void 0 || statusRaw === void 0) continue;
32460
+ const status = TERMINAL_STATUSES.includes(statusRaw) ? statusRaw : void 0;
32461
+ if (status === void 0) continue;
32462
+ byTask.set(task, {
32463
+ task,
32464
+ worker: /(?:^| )worker=(\S+)/.exec(line)?.[1] ?? "-",
32465
+ status,
32466
+ session: /(?:^| )session=(\S+)/.exec(line)?.[1] ?? "-",
32467
+ detail: /(?:^| )detail=([\s\S]*)$/.exec(line)?.[1] ?? "",
32468
+ line: i + 1
32469
+ });
32470
+ }
32471
+ return byTask;
32472
+ }
32473
+ function deadWithoutRecord(declaredTaskIds, rows) {
32474
+ return declaredTaskIds.filter((id) => !rows.has(id));
32475
+ }
32476
+ function deadFaceCount(dead) {
32477
+ return `dead-without-record=${String(dead.length)}`;
32478
+ }
32479
+
32084
32480
  // src/tools/shared.ts
32085
- import { readFile as readFile2, stat } from "node:fs/promises";
32086
- import { isAbsolute, resolve } from "node:path";
32481
+ import { readFile as readFile3, stat } from "node:fs/promises";
32482
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
32087
32483
  var ARTIFACT_MAX_BYTES = 262144;
32088
32484
  var DEFAULT_SLOT2 = "default";
32089
32485
  function slotForModel(pool, overrideSlot, personaSlot) {
@@ -32108,7 +32504,7 @@ async function readArtifact(raw, cwd) {
32108
32504
  text: trimmed
32109
32505
  };
32110
32506
  }
32111
- const path = isAbsolute(trimmed) ? trimmed : resolve(cwd, trimmed);
32507
+ const path = isAbsolute2(trimmed) ? trimmed : resolve2(cwd, trimmed);
32112
32508
  let info;
32113
32509
  try {
32114
32510
  info = await stat(path);
@@ -32126,7 +32522,7 @@ async function readArtifact(raw, cwd) {
32126
32522
  }
32127
32523
  let text;
32128
32524
  try {
32129
- text = await readFile2(path, "utf8");
32525
+ text = await readFile3(path, "utf8");
32130
32526
  } catch (err) {
32131
32527
  return { ok: false, error: `artifact "${path}": ${errMessage(err)}` };
32132
32528
  }
@@ -32196,7 +32592,8 @@ function formatRunLine(record3) {
32196
32592
  const verdict = record3.verdict === void 0 ? "-" : `${record3.verdict.verdict}(A${String(record3.verdict.approvals)}/R${String(record3.verdict.rejects)}/E${String(record3.verdict.errors)}/M${String(record3.verdict.missing)})`;
32197
32593
  const rounds = record3.rounds === void 0 ? "" : ` rounds=${String(record3.rounds)}`;
32198
32594
  const goal = record3.goal === void 0 ? "" : ` goal="${record3.goal}"`;
32199
- return `${record3.runId} ${record3.kind} ${record3.status} ${verdict}${rounds}${goal} created=${record3.createdAt} updated=${record3.updatedAt} space=${record3.spaceDir}`;
32595
+ const rules = record3.instrument === void 0 ? "" : ` rules=${record3.instrument.rulesHash.slice(0, 12)}`;
32596
+ return `${record3.runId} ${record3.kind} ${record3.status} ${verdict}${rounds}${goal}${rules} created=${record3.createdAt} updated=${record3.updatedAt} space=${record3.spaceDir}`;
32200
32597
  }
32201
32598
 
32202
32599
  // src/tools/consult.ts
@@ -32210,9 +32607,6 @@ ${goal}
32210
32607
  ARTIFACT:
32211
32608
  ${artifactText}`;
32212
32609
  }
32213
- function buildRepairDemand(why) {
32214
- 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>]}`;
32215
- }
32216
32610
  var REPLY_HEADER = "<!-- SIBYL consult voter reply (plan task 8). Full raw session text below; parse/repair metadata at the end. -->\n";
32217
32611
  function renderReply(voter, text, meta3) {
32218
32612
  return `${REPLY_HEADER}
@@ -32230,7 +32624,7 @@ async function castVote(job) {
32230
32624
  const { client, options } = deps;
32231
32625
  const persona = PERSONAS[id];
32232
32626
  const model = slotForModel(options.modelPool, options.voters[id], persona.modelSlot);
32233
- const replyPath = join3(spaceDir, `${id}.md`);
32627
+ const replyPath = join5(spaceDir, `${id}.md`);
32234
32628
  const result = await runPersona({
32235
32629
  client,
32236
32630
  directory,
@@ -32244,7 +32638,7 @@ async function castVote(job) {
32244
32638
  const error105 = `${result.stage ?? "engine"}: ${result.error ?? "unknown error"}`;
32245
32639
  const vote2 = { id, ok: false, error: error105 };
32246
32640
  const replyAt2 = await safeWrite(replyPath, renderReply(id, "(no reply \u2014 engine call failed)", `ballot: ERROR ${error105}`));
32247
- return { id, vote: vote2, replyPath: replyAt2, modelApplied: result.modelApplied, latencyMs: result.latencyMs };
32641
+ return { id, vote: vote2, replyPath: replyAt2, sessionId: result.sessionID, modelApplied: result.modelApplied, latencyMs: result.latencyMs };
32248
32642
  }
32249
32643
  const verdict = await parseVerdict(result.text, async (_badText, why) => {
32250
32644
  const followed = await runInSession(
@@ -32252,7 +32646,7 @@ async function castVote(job) {
32252
32646
  directory,
32253
32647
  result.sessionID,
32254
32648
  model,
32255
- buildRepairDemand(why),
32649
+ repairDemand(why),
32256
32650
  options.timeoutMs
32257
32651
  );
32258
32652
  return followed.ok ? followed.text : "";
@@ -32261,11 +32655,11 @@ async function castVote(job) {
32261
32655
  const vote = unparseable ? { id, ok: false, error: verdict.reasons[0] ?? "verdict-unparseable" } : { id, ok: true, verdict };
32262
32656
  const meta3 = unparseable ? `ballot: ERROR ${verdict.reasons[0] ?? "verdict-unparseable"} (reply did not parse)` : `ballot: ${verdict.verdict} (confidence ${String(verdict.confidence)}) \xB7 session ${result.sessionID} \xB7 model ${result.modelApplied ?? "host-picked"} \xB7 ${String(result.latencyMs)}ms`;
32263
32657
  const replyAt = await safeWrite(replyPath, renderReply(id, result.text, meta3));
32264
- return { id, vote, replyPath: replyAt, modelApplied: result.modelApplied, latencyMs: result.latencyMs };
32658
+ return { id, vote, replyPath: replyAt, sessionId: result.sessionID, modelApplied: result.modelApplied, latencyMs: result.latencyMs };
32265
32659
  }
32266
32660
  async function safeWrite(path, text) {
32267
32661
  try {
32268
- await writeFile2(path, text, "utf8");
32662
+ await writeFile3(path, text, "utf8");
32269
32663
  return path;
32270
32664
  } catch (err) {
32271
32665
  return `<${path} write failed: ${errMessage(err)}>`;
@@ -32292,6 +32686,7 @@ async function consultExecute(deps, args, context) {
32292
32686
  if (!read.ok) {
32293
32687
  return `SIBYL consult: ${read.error}`;
32294
32688
  }
32689
+ const independence = await assessIndependence(deps.client, context.directory, context.sessionID, read);
32295
32690
  const { record: record3 } = await deps.store.createRun({
32296
32691
  kind: "consult",
32297
32692
  artifact: read.source,
@@ -32303,25 +32698,51 @@ async function consultExecute(deps, args, context) {
32303
32698
  )
32304
32699
  );
32305
32700
  const tally = tallyVotes(outcomes.map((o) => o.vote));
32701
+ const terminalsPath = join5(record3.spaceDir, "TERMINALS.txt");
32702
+ await Promise.all(
32703
+ outcomes.map((o) => {
32704
+ const vote = o.vote;
32705
+ const status = "missing" in vote ? "error" : vote.ok ? "ok" : classifyFailure(vote.error);
32706
+ const detail = "missing" in vote || !vote.ok ? `ballot=${ballotTag(vote)}` : `ballot=${vote.verdict.verdict}`;
32707
+ const line = formatTerminalRow(/* @__PURE__ */ new Date(), {
32708
+ task: o.id,
32709
+ worker: o.id,
32710
+ status,
32711
+ session: o.sessionId,
32712
+ detail
32713
+ });
32714
+ return appendFile(terminalsPath, `${line}
32715
+ `, "utf8").catch((err) => {
32716
+ console.error(`[sibyl consult] terminal row for ${o.id} failed to write: ${errMessage(err)}`);
32717
+ });
32718
+ })
32719
+ );
32720
+ const rows = parseTerminalRows(await readFile4(terminalsPath, "utf8").catch(() => ""));
32721
+ const dead = deadWithoutRecord([...COUNCILORS], rows);
32722
+ const effectiveVerdict = dead.length > 0 ? "CANNOT_ANSWER" : tally.verdict;
32306
32723
  const finalRecord = {
32307
32724
  ...record3,
32308
32725
  status: "done",
32309
32726
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
32310
32727
  verdict: {
32311
- verdict: tally.verdict,
32728
+ verdict: effectiveVerdict,
32312
32729
  approvals: tally.approvals,
32313
32730
  rejects: tally.rejects,
32314
32731
  errors: tally.errors,
32315
32732
  missing: tally.missing
32316
32733
  },
32317
- notes: `policy=${tally.policy} ` + outcomes.map((o) => `${o.id}=${ballotTag(o.vote)} reply=${o.replyPath}`).join(" \xB7 ")
32734
+ instrument: instrumentFace(),
32735
+ independence,
32736
+ notes: `policy=${tally.policy} ${deadFaceCount(dead)}${dead.length > 0 ? ` (${dead.join(",")} left no terminal row: instrument wounded, not a verdict)` : ""} independence=${independence.status}${independence.status === "NOT-INDEPENDENT" ? " (self-convoked: ballot preserved, not in any effective path)" : ""} ` + outcomes.map((o) => `${o.id}=${ballotTag(o.vote)} reply=${o.replyPath}`).join(" \xB7 ")
32318
32737
  };
32319
32738
  await deps.store.appendOrUpdate(finalRecord);
32320
32739
  const lines = [
32321
- `SIBYL CONSULT: ${tally.verdict} (votes ${String(tally.approvals)}A/${String(tally.rejects)}R/${String(tally.errors)}E/${String(tally.missing)}M) run ${record3.runId}`,
32740
+ `SIBYL CONSULT: ${effectiveVerdict} (${deadFaceCount(dead)} votes ${String(tally.approvals)}A/${String(tally.rejects)}R/${String(tally.errors)}E/${String(tally.missing)}M) run ${record3.runId}`,
32741
+ ` ${independenceLabel(independence)}`,
32322
32742
  ...tally.reasons.map((r) => ` reason: ${r}`),
32323
32743
  ...tally.must_fix.map((m) => ` must_fix: ${m}`),
32324
32744
  ...outcomes.map((o) => ` reply: ${o.id} -> ${o.replyPath}`),
32745
+ ` ${rulesLabel(finalRecord.instrument ?? instrumentFace())} components=${String(Object.keys(instrumentFace().components).length)}`,
32325
32746
  ` store: ${deps.store.runsFile}`
32326
32747
  ];
32327
32748
  return lines.join("\n");
@@ -32335,20 +32756,20 @@ function ballotTag(vote) {
32335
32756
  import { spawn } from "node:child_process";
32336
32757
  import { randomBytes as randomBytes2 } from "node:crypto";
32337
32758
  import { existsSync as existsSync2 } from "node:fs";
32338
- import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
32339
- import { dirname as dirname2, join as join4 } from "node:path";
32759
+ import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
32760
+ import { dirname as dirname3, join as join6 } from "node:path";
32340
32761
  import { fileURLToPath } from "node:url";
32341
32762
  var REVIEW_TOOL_NAME = "sibyl_review";
32342
32763
  function nodeBinary() {
32343
32764
  return existsSync2("/usr/bin/node") ? "/usr/bin/node" : process.execPath;
32344
32765
  }
32345
32766
  function cliCommand(thisUrl, node2) {
32346
- const moduleDir = dirname2(fileURLToPath(thisUrl));
32347
- const repoRoot = dirname2(dirname2(moduleDir));
32348
- const distCli = join4(repoRoot, "dist", "cli.js");
32767
+ const moduleDir = dirname3(fileURLToPath(thisUrl));
32768
+ const repoRoot = dirname3(dirname3(moduleDir));
32769
+ const distCli = join6(repoRoot, "dist", "cli.js");
32349
32770
  if (existsSync2(distCli)) return { bin: node2, args: [distCli] };
32350
- const srcCli = join4(repoRoot, "src", "cli.ts");
32351
- const hooks = join4(repoRoot, "tools", "register-ts.mjs");
32771
+ const srcCli = join6(repoRoot, "src", "cli.ts");
32772
+ const hooks = join6(repoRoot, "tools", "register-ts.mjs");
32352
32773
  return { bin: node2, args: ["--import", hooks, srcCli] };
32353
32774
  }
32354
32775
  async function reviewExecute(deps, args, context) {
@@ -32357,29 +32778,38 @@ async function reviewExecute(deps, args, context) {
32357
32778
  const opts = deps.options;
32358
32779
  const now = /* @__PURE__ */ new Date();
32359
32780
  const runId = `sibyl-${compactIso(now)}-${randomBytes2(2).toString("hex")}`;
32360
- const runDir = join4(opts.lane.runRoot, `sibyl-run-${runId}`);
32361
- await mkdir2(runDir, { recursive: true });
32781
+ const runDir = join6(opts.lane.runRoot, `sibyl-run-${runId}`);
32782
+ await mkdir3(runDir, { recursive: true });
32362
32783
  const artifact = await readArtifact(args.target, context.directory);
32363
32784
  if (!artifact.ok) return `SIBYL review: ${artifact.error}`;
32364
32785
  let targetArg;
32365
32786
  if (artifact.kind === "inline") {
32366
- targetArg = join4(runDir, "target-inline.md");
32367
- await writeFile3(targetArg, artifact.text, "utf8");
32787
+ targetArg = join6(runDir, "target-inline.md");
32788
+ await writeFile4(targetArg, artifact.text, "utf8");
32368
32789
  } else {
32369
32790
  targetArg = artifact.source;
32370
32791
  }
32371
- await writeFile3(join4(runDir, "run-config.json"), `${JSON.stringify(opts, null, 2)}
32792
+ await writeFile4(join6(runDir, "run-config.json"), `${JSON.stringify(opts, null, 2)}
32372
32793
  `, "utf8");
32794
+ const independence = await assessIndependence(deps.client, context.directory, context.sessionID, artifact);
32795
+ await writeFile4(
32796
+ join6(runDir, "launch-context.json"),
32797
+ `${JSON.stringify({ convenerSessionID: context.sessionID, independence }, null, 2)}
32798
+ `,
32799
+ "utf8"
32800
+ );
32373
32801
  const cmd = cliCommand(import.meta.url, nodeBinary());
32374
32802
  const spawnArgs = [
32375
32803
  ...cmd.args,
32376
32804
  "run",
32805
+ "--run-id",
32806
+ runId,
32377
32807
  "--target",
32378
32808
  targetArg,
32379
32809
  "--goal",
32380
32810
  args.goal,
32381
32811
  "--config",
32382
- join4(runDir, "run-config.json"),
32812
+ join6(runDir, "run-config.json"),
32383
32813
  "--run-root",
32384
32814
  opts.lane.runRoot,
32385
32815
  "--seed",
@@ -32402,8 +32832,10 @@ async function reviewExecute(deps, args, context) {
32402
32832
  ` run: ${receipt.runId}`,
32403
32833
  ` dir: ${receipt.runDir}`,
32404
32834
  ` status: ${receipt.statusCmd}`,
32405
- ` voice at: ${join4(runDir, "run-record.json")} + chamber-ledger.jsonl (append-only)`,
32406
- ` verify: ${receipt.spotcheck}`
32835
+ ` voice at: ${join6(runDir, "run-record.json")} + chamber-ledger.jsonl (append-only)`,
32836
+ ` verify: ${receipt.spotcheck}`,
32837
+ ` ${rulesLabel(instrumentFace())} (the runner records its own full face into the run record)`,
32838
+ ` ${independenceLabel(independence)}`
32407
32839
  ].join("\n");
32408
32840
  } catch (err) {
32409
32841
  return internalError(REVIEW_TOOL_NAME, err);
@@ -32423,7 +32855,7 @@ function buildReviewTool(deps) {
32423
32855
  }
32424
32856
 
32425
32857
  // src/tools/anchor.ts
32426
- import { createHash } from "node:crypto";
32858
+ import { createHash as createHash2 } from "node:crypto";
32427
32859
  function canonicalView(rows) {
32428
32860
  return JSON.stringify(
32429
32861
  rows.map((r) => ({
@@ -32436,7 +32868,7 @@ function canonicalText(raw) {
32436
32868
  return raw.replace(/\s*<dcp-message-id[^>]*>[\s\S]*?<\/dcp-message-id>\s*$/g, "").trim();
32437
32869
  }
32438
32870
  function sha256(text) {
32439
- return createHash("sha256").update(text, "utf8").digest("hex");
32871
+ return createHash2("sha256").update(text, "utf8").digest("hex");
32440
32872
  }
32441
32873
  function anchorText(row) {
32442
32874
  return (row.parts ?? []).filter((p) => p.type === "text").map((p) => p.text ?? "").join("\n");
@@ -32633,110 +33065,9 @@ function buildAttributeTool(deps) {
32633
33065
  });
32634
33066
  }
32635
33067
 
32636
- // src/state/chamber.ts
32637
- import { mkdir as mkdir3, readFile as readFile3, readdir, rename as rename2, unlink as unlink2, writeFile as writeFile4 } from "node:fs/promises";
32638
- import { homedir as homedir3 } from "node:os";
32639
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "node:path";
32640
- var DEFAULT_CHAMBER_LEDGER = join5(homedir3(), ".sibyl", "chamber-ledger.jsonl");
32641
- var TERMINAL_STATES = ["CONVERGED", "NEEDS_ROUND", "MEMBER_LOST", "TIMEOUT"];
32642
- var RUN_PROFILES = ["review", "exam"];
32643
- var RECORD_SCHEMA = 1;
32644
- function asStringArray(v) {
32645
- return Array.isArray(v) && v.every((x) => typeof x === "string") ? [...v] : null;
32646
- }
32647
- function isRosterRef(v) {
32648
- if (typeof v !== "object" || v === null) return false;
32649
- const o = v;
32650
- const modelOk = o["policyCheck"] === "deny" ? typeof o["modelId"] === "string" : isNonEmptyString(o["modelId"]);
32651
- return isNonEmptyString(o["role"]) && isNonEmptyString(o["slot"]) && modelOk && (o["policyCheck"] === "allow" || o["policyCheck"] === "deny") && (o["denyReason"] === void 0 || typeof o["denyReason"] === "string");
32652
- }
32653
- function isArtifactRef(v) {
32654
- if (typeof v !== "object" || v === null) return false;
32655
- const o = v;
32656
- return isNonEmptyString(o["role"]) && typeof o["round"] === "number" && Number.isInteger(o["round"]) && o["round"] >= 0 && isNonEmptyString(o["phase"]) && isNonEmptyString(o["relPath"]) && typeof o["sha256"] === "string" && /^[0-9a-f]{64}$/.test(o["sha256"]) && typeof o["bytes"] === "number" && o["bytes"] >= 0 && isNonEmptyString(o["presentAt"]) && (o["rc"] === null || typeof o["rc"] === "number") && (o["signal"] === null || typeof o["signal"] === "string") && typeof o["timedOut"] === "boolean";
32657
- }
32658
- function validateChamberRecord(raw) {
32659
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return { ok: false, reason: "record is not an object" };
32660
- const o = raw;
32661
- if (o["schema"] !== RECORD_SCHEMA) return { ok: false, reason: `schema must be ${RECORD_SCHEMA}` };
32662
- if (!isNonEmptyString(o["runId"])) return { ok: false, reason: "runId must be a non-empty string" };
32663
- if (typeof o["serial"] !== "number" || !Number.isInteger(o["serial"]) || o["serial"] < 1) return { ok: false, reason: "serial must be a positive integer" };
32664
- if (!RUN_PROFILES.includes(o["profile"])) return { ok: false, reason: `profile must be one of ${RUN_PROFILES.join("|")}` };
32665
- if (!isNonEmptyString(o["goal"])) return { ok: false, reason: "goal must be a non-empty string" };
32666
- if (typeof o["target"] !== "string") return { ok: false, reason: "target must be a string" };
32667
- if (!isNonEmptyString(o["runDir"]) || !isAbsolute2(o["runDir"])) return { ok: false, reason: "runDir must be an absolute path" };
32668
- if (!isNonEmptyString(o["createdAt"]) || Number.isNaN(Date.parse(o["createdAt"]))) return { ok: false, reason: "createdAt must be ISO" };
32669
- if (!isNonEmptyString(o["updatedAt"]) || Number.isNaN(Date.parse(o["updatedAt"]))) return { ok: false, reason: "updatedAt must be ISO" };
32670
- if (o["terminal"] !== null && !TERMINAL_STATES.includes(o["terminal"])) return { ok: false, reason: "terminal must be null or a TerminalState" };
32671
- if (!Array.isArray(o["roster"]) || !o["roster"].every(isRosterRef)) return { ok: false, reason: "roster must be an array of RosterRef" };
32672
- const judgePool = asStringArray(o["judgePool"]);
32673
- if (judgePool === null) return { ok: false, reason: "judgePool must be a string array" };
32674
- if (!isNonEmptyString(o["seed"])) return { ok: false, reason: "seed must be a non-empty string" };
32675
- if (typeof o["drawCommit"] !== "string" || !/^[0-9a-f]{64}$/.test(o["drawCommit"])) return { ok: false, reason: "drawCommit must be 64-hex" };
32676
- if (typeof o["judgeModelId"] !== "string") return { ok: false, reason: "judgeModelId must be a string" };
32677
- if (typeof o["rounds"] !== "number" || !Number.isInteger(o["rounds"]) || o["rounds"] < 0) return { ok: false, reason: "rounds must be a non-negative integer" };
32678
- if (!Array.isArray(o["artifacts"]) || !o["artifacts"].every(isArtifactRef)) return { ok: false, reason: "artifacts must be an array of ArtifactRef" };
32679
- if (typeof o["evidenceRows"] !== "number" || !Number.isInteger(o["evidenceRows"]) || o["evidenceRows"] < 0) return { ok: false, reason: "evidenceRows must be a non-negative integer" };
32680
- if (o["notes"] !== void 0 && typeof o["notes"] !== "string") return { ok: false, reason: "notes must be a string when present" };
32681
- const record3 = {
32682
- schema: RECORD_SCHEMA,
32683
- runId: o["runId"],
32684
- serial: o["serial"],
32685
- profile: o["profile"],
32686
- goal: o["goal"],
32687
- target: o["target"],
32688
- runDir: o["runDir"],
32689
- createdAt: o["createdAt"],
32690
- updatedAt: o["updatedAt"],
32691
- terminal: o["terminal"],
32692
- roster: [...o["roster"]],
32693
- judgePool,
32694
- seed: o["seed"],
32695
- drawCommit: o["drawCommit"],
32696
- judgeModelId: o["judgeModelId"],
32697
- rounds: o["rounds"],
32698
- artifacts: [...o["artifacts"]],
32699
- evidenceRows: o["evidenceRows"]
32700
- };
32701
- if (typeof o["notes"] === "string") record3.notes = o["notes"];
32702
- return { ok: true, record: record3 };
32703
- }
32704
- function parseLedgerLine(line) {
32705
- const trimmed = line.trim();
32706
- if (trimmed.length === 0) return { ok: false, reason: "blank line" };
32707
- let parsed;
32708
- try {
32709
- parsed = JSON.parse(trimmed);
32710
- } catch (err) {
32711
- const msg = err instanceof Error ? err.message : String(err);
32712
- return { ok: false, reason: `not a single JSON document (merged-row guard): ${msg}` };
32713
- }
32714
- const v = validateChamberRecord(parsed);
32715
- if (!v.ok) return { ok: false, reason: v.reason };
32716
- return { ok: true, record: v.record };
32717
- }
32718
- async function loadLedger(ledgerPath) {
32719
- let text;
32720
- try {
32721
- text = await readFile3(ledgerPath, "utf8");
32722
- } catch {
32723
- return { rows: [], dropped: [] };
32724
- }
32725
- const rows = [];
32726
- const dropped = [];
32727
- const lines = text.split("\n");
32728
- for (const [i, line] of lines.entries()) {
32729
- if (line.trim().length === 0) continue;
32730
- const v = parseLedgerLine(line);
32731
- if (v.ok) rows.push(v.record);
32732
- else dropped.push({ line: i + 1, reason: v.reason });
32733
- }
32734
- return { rows, dropped };
32735
- }
32736
-
32737
33068
  // src/tools/status.ts
32738
33069
  import { dirname as dirname4 } from "node:path";
32739
- import { join as join6 } from "node:path";
33070
+ import { join as join7 } from "node:path";
32740
33071
  var STATUS_TOOL_NAME = "sibyl_status";
32741
33072
  function buildStatusTool(deps) {
32742
33073
  return tool({
@@ -32757,7 +33088,7 @@ async function statusExecute(deps, args, _context) {
32757
33088
  return [`SIBYL RUN ${run2.runId}`, formatRunLine(run2)].join("\n");
32758
33089
  }
32759
33090
  const runs = await deps.store.load();
32760
- const ledger = await loadLedger(join6(dirname4(deps.store.runsFile), "chamber-ledger.jsonl"));
33091
+ const ledger = await loadLedger(join7(dirname4(deps.store.runsFile), "chamber-ledger.jsonl"));
32761
33092
  const chamberLines = ledger.rows.slice(-5).map(
32762
33093
  (r) => `${r.runId} chamber/${r.profile} ${r.terminal ?? "running"} rounds=${String(r.rounds)} artifacts=${String(r.artifacts.length)} judge=${r.judgeModelId} dir=${r.runDir}`
32763
33094
  );
@@ -32773,8 +33104,8 @@ async function statusExecute(deps, args, _context) {
32773
33104
  }
32774
33105
 
32775
33106
  // src/tools/swarm.ts
32776
- import { join as join7 } from "node:path";
32777
- import { writeFile as writeFile5 } from "node:fs/promises";
33107
+ import { join as join8 } from "node:path";
33108
+ import { appendFile as appendFile2, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
32778
33109
 
32779
33110
  // src/swarm/types.ts
32780
33111
  var SWARM_VERDICTS = {
@@ -32911,11 +33242,24 @@ function composeTaskPrompt(ctx, taskId) {
32911
33242
  Upstream conclusions (from completed dependencies):
32912
33243
  ${lines.join("\n")}`;
32913
33244
  }
33245
+ async function recordTerminal(ctx, taskId, workerId, status, session, detail) {
33246
+ if (ctx.writeTerminal === void 0) return true;
33247
+ try {
33248
+ await ctx.writeTerminal(
33249
+ formatTerminalRow(/* @__PURE__ */ new Date(), { task: taskId, worker: workerId, status, session, detail })
33250
+ );
33251
+ return true;
33252
+ } catch (e) {
33253
+ console.error(`[sibyl dispatch] terminal line for ${taskId} failed to write: ${describeFailure(e)}`);
33254
+ return false;
33255
+ }
33256
+ }
32914
33257
  async function runTask(ctx, task) {
32915
33258
  const worker = ctx.workerByTask.get(task.taskId);
32916
33259
  if (worker === void 0) {
32917
33260
  task.status = "failed";
32918
33261
  task.error = `no worker spec for task "${task.taskId}"`;
33262
+ await recordTerminal(ctx, task.taskId, "-", "error", "-", task.error);
32919
33263
  return;
32920
33264
  }
32921
33265
  for (let attempt = 1; attempt <= ctx.maxAttempts; attempt++) {
@@ -32936,30 +33280,45 @@ async function runTask(ctx, task) {
32936
33280
  } catch (e) {
32937
33281
  task.status = "failed";
32938
33282
  task.error = `runPersona threw: ${describeFailure(e)}`;
33283
+ await recordTerminal(ctx, task.taskId, worker.workerId, classifyFailure(task.error), "-", task.error);
32939
33284
  return;
32940
33285
  }
32941
33286
  if (result.ok) {
32942
- let ref;
32943
- try {
32944
- ref = await ctx.writeDraft({ taskId: task.taskId, workerId: worker.workerId, text: result.text });
32945
- } catch (e) {
32946
- task.status = "failed";
32947
- task.error = `draft write failed: ${describeFailure(e)}`;
33287
+ if (result.text.trim().length === 0) {
33288
+ task.error = "empty-reply: worker session ended with no content (absence is not a ballot)";
33289
+ } else {
33290
+ let ref;
33291
+ try {
33292
+ ref = await ctx.writeDraft({ taskId: task.taskId, workerId: worker.workerId, text: result.text });
33293
+ } catch (e) {
33294
+ task.status = "failed";
33295
+ task.error = `draft write failed: ${describeFailure(e)}`;
33296
+ await recordTerminal(ctx, task.taskId, worker.workerId, "error", result.sessionID, task.error);
33297
+ return;
33298
+ }
33299
+ const recorded = await recordTerminal(ctx, task.taskId, worker.workerId, "ok", result.sessionID, `draft=${ref}`);
33300
+ if (!recorded) {
33301
+ task.status = "failed";
33302
+ task.error = "terminal record write failed (W2: done without its record never stands)";
33303
+ return;
33304
+ }
33305
+ task.refs.push(ref);
33306
+ task.summary = summarizeTaskOutput(result.text, ctx.summaryMaxChars);
33307
+ if (result.sessionID !== "") task.sessionId = result.sessionID;
33308
+ task.status = "done";
32948
33309
  return;
32949
33310
  }
32950
- task.refs.push(ref);
32951
- task.summary = summarizeTaskOutput(result.text, ctx.summaryMaxChars);
32952
- if (result.sessionID !== "") task.sessionId = result.sessionID;
32953
- task.status = "done";
32954
- return;
33311
+ } else {
33312
+ task.error = result.error ?? "unknown engine error";
32955
33313
  }
32956
- task.error = result.error ?? "unknown engine error";
32957
33314
  if (isRateLimitError(task.error)) {
32958
33315
  task.status = "suspended";
33316
+ await recordTerminal(ctx, task.taskId, worker.workerId, "error", "-", `suspended: ${task.error}`);
32959
33317
  return;
32960
33318
  }
32961
33319
  }
32962
33320
  task.status = "failed";
33321
+ await recordTerminal(ctx, task.taskId, worker.workerId, classifyFailure(task.error ?? ""), "-", task.error ?? "attempts exhausted");
32963
33322
  }
32964
33323
  async function runWaves(ctx, state) {
32965
33324
  for (; ; ) {
@@ -32967,7 +33326,12 @@ async function runWaves(ctx, state) {
32967
33326
  if (schedulable.length === 0) break;
32968
33327
  if (state.rounds >= ctx.maxRounds) {
32969
33328
  state.exhausted = true;
32970
- for (const t of state.tasks) if (t.status === "pending") t.status = "suspended";
33329
+ for (const t of state.tasks) {
33330
+ if (t.status === "pending") {
33331
+ t.status = "suspended";
33332
+ await recordTerminal(ctx, t.taskId, ctx.workerByTask.get(t.taskId)?.workerId ?? "-", "error", "-", "exhausted: round budget spent before launch");
33333
+ }
33334
+ }
32971
33335
  break;
32972
33336
  }
32973
33337
  state.rounds += 1;
@@ -32978,9 +33342,12 @@ async function runWaves(ctx, state) {
32978
33342
  })());
32979
33343
  await Promise.all(launches);
32980
33344
  }
32981
- for (const t of state.tasks) if (t.status === "pending") {
32982
- t.status = "failed";
32983
- t.error = "blocked by non-done dependency";
33345
+ for (const t of state.tasks) {
33346
+ if (t.status === "pending") {
33347
+ t.status = "failed";
33348
+ t.error = "blocked by non-done dependency";
33349
+ await recordTerminal(ctx, t.taskId, ctx.workerByTask.get(t.taskId)?.workerId ?? "-", "error", "-", t.error);
33350
+ }
32984
33351
  }
32985
33352
  }
32986
33353
  function materialize(state) {
@@ -33028,7 +33395,8 @@ async function run(prior, roster, opts) {
33028
33395
  summaryMaxChars,
33029
33396
  maxRounds,
33030
33397
  clock: opts.clock ?? realClock(),
33031
- writeDraft: opts.writeDraft
33398
+ writeDraft: opts.writeDraft,
33399
+ writeTerminal: opts.writeTerminal
33032
33400
  };
33033
33401
  let state = prior ?? makeState(roster);
33034
33402
  if (prior !== void 0) {
@@ -33252,7 +33620,7 @@ function buildJudgeInput(report) {
33252
33620
  ...report.tasks.map((t) => `TASK ${t.id}: ${t.status}${t.summary === void 0 ? "" : ` \u2014 ${t.summary}`}`),
33253
33621
  ...report.artifacts.map((a) => `ARTIFACT: ${a}`),
33254
33622
  "",
33255
- "Respond with EXACTLY one word and nothing else: APPROVE, REJECT, or EXHAUSTED."
33623
+ SWARM_JUDGE_WORD_CONTRACT
33256
33624
  ];
33257
33625
  return lines.join("\n");
33258
33626
  }
@@ -33294,6 +33662,7 @@ async function swarmExecute(deps, args, context) {
33294
33662
  if (!read.ok) {
33295
33663
  return `SIBYL swarm: ${read.error}`;
33296
33664
  }
33665
+ const independence = await assessIndependence(client, directory, context.sessionID, read);
33297
33666
  const { record: record3 } = await store.createRun({ kind: "swarm", artifact: read.source, goal: args.goal });
33298
33667
  const architectModel = slotForModel(options.modelPool, void 0, PERSONAS.ARCHITECT.modelSlot);
33299
33668
  const plan = await planWorkflow({
@@ -33311,7 +33680,7 @@ async function swarmExecute(deps, args, context) {
33311
33680
  directory,
33312
33681
  persona: { system: PERSONAS.ARCHITECT.system },
33313
33682
  model: architectModel,
33314
- inputText: `${buildRepairDemand(why)} matching the workflow-schema contract: {"tasks":[{"id","title","instructions","dependsOn"}],"concurrency":<int >= 1>,"notes":<string>}`,
33683
+ inputText: `${repairDemand(why)} matching the workflow-schema contract: {"tasks":[{"id","title","instructions","dependsOn"}],"concurrency":<int >= 1>,"notes":<string>}`,
33315
33684
  timeoutMs: options.timeoutMs,
33316
33685
  parentID: context.sessionID
33317
33686
  });
@@ -33334,7 +33703,7 @@ async function swarmExecute(deps, args, context) {
33334
33703
  staggerMs: options.staggerMs,
33335
33704
  maxRounds: options.maxRounds,
33336
33705
  writeDraft: async ({ taskId, workerId, text }) => {
33337
- const path = join7(record3.spaceDir, `${workerId}.draft.md`);
33706
+ const path = join8(record3.spaceDir, `${workerId}.draft.md`);
33338
33707
  try {
33339
33708
  await writeFile5(path, `<!-- SIBYL swarm draft task=${taskId} worker=${workerId} -->
33340
33709
  ${text}
@@ -33343,7 +33712,9 @@ ${text}
33343
33712
  } catch (err) {
33344
33713
  return `<${path} write failed: ${errMessage(err)}>`;
33345
33714
  }
33346
- }
33715
+ },
33716
+ writeTerminal: (line) => appendFile2(join8(record3.spaceDir, "TERMINALS.txt"), `${line}
33717
+ `, "utf8")
33347
33718
  });
33348
33719
  if (!dispatch.ok) return failRun(deps, record3, { stage: "dispatch", error: dispatch.error });
33349
33720
  let report = buildReport({
@@ -33357,7 +33728,7 @@ ${text}
33357
33728
  const judged = await runPersona({
33358
33729
  client,
33359
33730
  directory,
33360
- persona: { system: "You are the SIBYL swarm judge. You rule on pipeline reports with exactly one word: APPROVE, REJECT, or EXHAUSTED." },
33731
+ persona: { system: SWARM_JUDGE_SYSTEM },
33361
33732
  model: slotForModel(options.modelPool, options.swarm.judge, void 0),
33362
33733
  inputText: buildJudgeInput(report),
33363
33734
  timeoutMs: options.timeoutMs,
@@ -33369,23 +33740,32 @@ ${text}
33369
33740
  }
33370
33741
  const counts = countTasks(report.tasks);
33371
33742
  const noSignal = counts.done === 0;
33372
- const tag = noSignal ? "CANNOT_ANSWER" : report.verdict === "APPROVE" ? "APPROVE" : "REJECT";
33743
+ const terminalsText = await readFile5(join8(record3.spaceDir, "TERMINALS.txt"), "utf8").catch(() => "");
33744
+ const dead = deadWithoutRecord(
33745
+ plan.schema.tasks.map((t) => t.id),
33746
+ parseTerminalRows(terminalsText)
33747
+ );
33748
+ const tag = dead.length > 0 || noSignal ? "CANNOT_ANSWER" : report.verdict === "APPROVE" ? "APPROVE" : "REJECT";
33373
33749
  await finishRun(deps, record3, {
33374
33750
  status: "done",
33375
33751
  rounds: report.rounds,
33752
+ instrument: instrumentFace(),
33753
+ independence,
33376
33754
  verdict: {
33377
33755
  verdict: tag,
33378
33756
  approvals: counts.done,
33379
- rejects: noSignal ? 0 : tag === "REJECT" ? 1 : 0,
33757
+ rejects: noSignal || dead.length > 0 ? 0 : tag === "REJECT" ? 1 : 0,
33380
33758
  errors: counts.failed,
33381
33759
  missing: counts.suspended
33382
33760
  },
33383
- notes: `${judgeNote}${noSignal ? " \xB7 zero drafts produced: instrument failure, not a verdict on the artifact" : ""} \xB7 ${report.tasks.map((t) => `${t.id}=${t.status}`).join(",")}`
33761
+ notes: `${judgeNote} \xB7 independence=${independence.status}${independence.status === "NOT-INDEPENDENT" ? " (self-convoked: report preserved, not in any effective path)" : ""} \xB7 ${deadFaceCount(dead)}${dead.length > 0 ? ` (${dead.join(",")} left no terminal row: instrument wounded, not a verdict on the artifact)` : ""}${noSignal ? " \xB7 zero drafts produced: instrument failure, not a verdict on the artifact" : ""} \xB7 ${report.tasks.map((t) => `${t.id}=${t.status}`).join(",")}`
33384
33762
  });
33385
33763
  const lines = [
33386
- `SIBYL SWARM: ${tag} run ${record3.runId} (rounds=${String(report.rounds)} done=${String(counts.done)} failed=${String(counts.failed)} suspended=${String(counts.suspended)} ${judgeNote})`,
33764
+ `SIBYL SWARM: ${tag} run ${record3.runId} (${deadFaceCount(dead)} rounds=${String(report.rounds)} done=${String(counts.done)} failed=${String(counts.failed)} suspended=${String(counts.suspended)} ${judgeNote})`,
33765
+ ` ${independenceLabel(independence)}`,
33387
33766
  ...report.tasks.map((t) => ` task: ${t.id} ${t.status}${t.sessionId === void 0 ? "" : ` session=${t.sessionId}`}`),
33388
33767
  ...report.artifacts.map((a) => ` artifact: ${a}`),
33768
+ ` ${rulesLabel(instrumentFace())} components=${String(Object.keys(instrumentFace().components).length)}`,
33389
33769
  ` space: ${record3.spaceDir}`,
33390
33770
  ` store: ${store.runsFile}`
33391
33771
  ];
@@ -33485,12 +33865,29 @@ function toEngineClient(sdk) {
33485
33865
  },
33486
33866
  parts: m.parts.map((p) => ({
33487
33867
  type: p.type,
33488
- ..."text" in p && p.text !== void 0 && { text: p.text }
33868
+ ..."text" in p && p.text !== void 0 && { text: p.text },
33869
+ // W1 drafting-evidence read: tool name + input bag of tool parts
33870
+ ...p.type === "tool" && {
33871
+ tool: p.tool,
33872
+ ...p.state.input !== void 0 && { input: p.state.input }
33873
+ }
33489
33874
  }))
33490
33875
  }))
33491
33876
  },
33492
33877
  ...r.error !== void 0 && { error: r.error }
33493
33878
  };
33879
+ },
33880
+ async get(args) {
33881
+ const r = await sdk.session.get(args);
33882
+ return {
33883
+ ...r.data !== void 0 && {
33884
+ data: {
33885
+ ...r.data.id !== void 0 && { id: r.data.id },
33886
+ ...r.data.parentID !== void 0 && { parentID: r.data.parentID }
33887
+ }
33888
+ },
33889
+ ...r.error !== void 0 && { error: r.error }
33890
+ };
33494
33891
  }
33495
33892
  }
33496
33893
  };