sibyl-system 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -2
- package/dist/cli.js +356 -187
- package/dist/index.js +568 -174
- 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
|
|
4908
|
-
if (
|
|
4909
|
-
return
|
|
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
|
|
18666
|
-
if (
|
|
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 (
|
|
18673
|
-
return
|
|
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);
|
|
@@ -18986,7 +18986,36 @@ import { dirname, join as join2 } from "node:path";
|
|
|
18986
18986
|
var SIBYL_STATE_FILE_ENV = "SIBYL_STATE_FILE";
|
|
18987
18987
|
var RUN_KINDS = ["consult", "swarm"];
|
|
18988
18988
|
var RUN_STATUSES = ["running", "done", "failed", "suspended"];
|
|
18989
|
+
var INDEPENDENCE_STATUSES = ["INDEPENDENT", "NOT-INDEPENDENT", "UNVERIFIABLE"];
|
|
18990
|
+
function isIndependenceShape(v) {
|
|
18991
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
18992
|
+
const o = v;
|
|
18993
|
+
if (!INDEPENDENCE_STATUSES.includes(o["status"])) return false;
|
|
18994
|
+
if (!Array.isArray(o["convenerChain"]) || !o["convenerChain"].every((s) => typeof s === "string" && s.length > 0)) return false;
|
|
18995
|
+
return typeof o["evidence"] === "string";
|
|
18996
|
+
}
|
|
18997
|
+
function rebuildIndependence(v) {
|
|
18998
|
+
return { status: v.status, convenerChain: [...v.convenerChain], evidence: v.evidence };
|
|
18999
|
+
}
|
|
18989
19000
|
var VERDICT_TAGS = ["APPROVE", "REJECT", "CANNOT_ANSWER"];
|
|
19001
|
+
var INSTRUMENT_NAME = /^[a-z0-9-]{1,40}$/;
|
|
19002
|
+
var HEX64 = /^[0-9a-f]{64}$/;
|
|
19003
|
+
function isInstrumentShape(v) {
|
|
19004
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
19005
|
+
const o = v;
|
|
19006
|
+
if (typeof o["rulesHash"] !== "string" || !HEX64.test(o["rulesHash"])) return false;
|
|
19007
|
+
const comps = o["components"];
|
|
19008
|
+
if (typeof comps !== "object" || comps === null || Array.isArray(comps)) return false;
|
|
19009
|
+
return Object.entries(comps).every(
|
|
19010
|
+
([name, hash3]) => INSTRUMENT_NAME.test(name) && typeof hash3 === "string" && HEX64.test(hash3)
|
|
19011
|
+
);
|
|
19012
|
+
}
|
|
19013
|
+
function rebuildInstrument(v) {
|
|
19014
|
+
return {
|
|
19015
|
+
rulesHash: v.rulesHash,
|
|
19016
|
+
components: Object.fromEntries(Object.entries(v.components).map(([name, hash3]) => [name, hash3]))
|
|
19017
|
+
};
|
|
19018
|
+
}
|
|
18990
19019
|
function oneOf(v, opts) {
|
|
18991
19020
|
return opts.some((o) => o === v);
|
|
18992
19021
|
}
|
|
@@ -19062,6 +19091,20 @@ function validateEntry(raw) {
|
|
|
19062
19091
|
if (typeof notes !== "string") return { ok: false, reason: "notes must be a string when present" };
|
|
19063
19092
|
record3.notes = notes;
|
|
19064
19093
|
}
|
|
19094
|
+
const instrument = field(raw, "instrument");
|
|
19095
|
+
if (instrument !== void 0) {
|
|
19096
|
+
if (!isInstrumentShape(instrument)) {
|
|
19097
|
+
return { ok: false, reason: "instrument must be {rulesHash: 64-hex, components: name->64-hex} when present" };
|
|
19098
|
+
}
|
|
19099
|
+
record3.instrument = rebuildInstrument(instrument);
|
|
19100
|
+
}
|
|
19101
|
+
const independence = field(raw, "independence");
|
|
19102
|
+
if (independence !== void 0) {
|
|
19103
|
+
if (!isIndependenceShape(independence)) {
|
|
19104
|
+
return { ok: false, reason: "independence must be {status: INDEPENDENT|NOT-INDEPENDENT|UNVERIFIABLE, convenerChain: string[], evidence: string} when present" };
|
|
19105
|
+
}
|
|
19106
|
+
record3.independence = rebuildIndependence(independence);
|
|
19107
|
+
}
|
|
19065
19108
|
return { ok: true, record: record3 };
|
|
19066
19109
|
}
|
|
19067
19110
|
function byCreatedAt(a, b) {
|
|
@@ -19190,8 +19233,8 @@ var RunStore = class {
|
|
|
19190
19233
|
};
|
|
19191
19234
|
|
|
19192
19235
|
// src/tools/consult.ts
|
|
19193
|
-
import { join as
|
|
19194
|
-
import { writeFile as
|
|
19236
|
+
import { join as join5 } from "node:path";
|
|
19237
|
+
import { appendFile, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
19195
19238
|
|
|
19196
19239
|
// node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
|
|
19197
19240
|
var external_exports2 = {};
|
|
@@ -31895,6 +31938,123 @@ async function runPersona(opts) {
|
|
|
31895
31938
|
}
|
|
31896
31939
|
}
|
|
31897
31940
|
|
|
31941
|
+
// src/independence.ts
|
|
31942
|
+
var MAX_CHAIN_DEPTH = 16;
|
|
31943
|
+
var MIN_INLINE_MATCH = 64;
|
|
31944
|
+
var DRAFTING_TOOLS = ["write", "edit", "multiedit", "patch", "apply_patch"];
|
|
31945
|
+
var PATH_KEYS = ["filePath", "path"];
|
|
31946
|
+
var CONTENT_KEYS = ["content", "text", "oldString", "newString"];
|
|
31947
|
+
function isDraftingTool(name) {
|
|
31948
|
+
return name !== void 0 && DRAFTING_TOOLS.includes(name);
|
|
31949
|
+
}
|
|
31950
|
+
function pathTargets(input2, artifactPath) {
|
|
31951
|
+
if (input2 === void 0) return false;
|
|
31952
|
+
for (const key of PATH_KEYS) {
|
|
31953
|
+
const v = input2[key];
|
|
31954
|
+
if (typeof v === "string" && v.trim() === artifactPath) return true;
|
|
31955
|
+
}
|
|
31956
|
+
return false;
|
|
31957
|
+
}
|
|
31958
|
+
function contentMatches(input2, needle) {
|
|
31959
|
+
if (input2 === void 0) return false;
|
|
31960
|
+
for (const key of CONTENT_KEYS) {
|
|
31961
|
+
const v = input2[key];
|
|
31962
|
+
if (typeof v !== "string") continue;
|
|
31963
|
+
if (v.trim() === needle) return true;
|
|
31964
|
+
if (needle.length >= MIN_INLINE_MATCH && v.includes(needle)) return true;
|
|
31965
|
+
if (needle.length >= MIN_INLINE_MATCH && v.length >= MIN_INLINE_MATCH && needle.includes(v.trim()) && v.trim().length > 0) {
|
|
31966
|
+
return true;
|
|
31967
|
+
}
|
|
31968
|
+
}
|
|
31969
|
+
return false;
|
|
31970
|
+
}
|
|
31971
|
+
function scanMessages(rows, convenerIndex, sid, artifact) {
|
|
31972
|
+
for (const [mi, m] of rows.entries()) {
|
|
31973
|
+
for (const p of m.parts ?? []) {
|
|
31974
|
+
if (p.type === "tool" && isDraftingTool(p.tool)) {
|
|
31975
|
+
if (artifact.kind === "path" && pathTargets(p.input, artifact.source)) {
|
|
31976
|
+
return `drafting evidence: ${p.tool} on ${artifact.source} in convener-chain session ${sid} (chain position ${String(convenerIndex)}, message ${String(mi)})`;
|
|
31977
|
+
}
|
|
31978
|
+
if (artifact.kind === "inline" && contentMatches(p.input, artifact.text.trim())) {
|
|
31979
|
+
return `drafting evidence: ${p.tool} content matches the inline artifact in convener-chain session ${sid} (chain position ${String(convenerIndex)}, message ${String(mi)})`;
|
|
31980
|
+
}
|
|
31981
|
+
}
|
|
31982
|
+
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())) {
|
|
31983
|
+
return `drafting evidence: assistant text in convener-chain session ${sid} (chain position ${String(convenerIndex)}, message ${String(mi)}) contains the inline artifact`;
|
|
31984
|
+
}
|
|
31985
|
+
}
|
|
31986
|
+
}
|
|
31987
|
+
return null;
|
|
31988
|
+
}
|
|
31989
|
+
async function assessIndependence(client, directory, convenerSessionID, artifact) {
|
|
31990
|
+
const noChain = { status: "UNVERIFIABLE", convenerChain: [], evidence: "" };
|
|
31991
|
+
if (convenerSessionID.length === 0) {
|
|
31992
|
+
return { ...noChain, evidence: "headless invocation: no calling session to resolve the convener chain against" };
|
|
31993
|
+
}
|
|
31994
|
+
if (!artifact.ok) {
|
|
31995
|
+
return { ...noChain, evidence: "artifact unreadable at assessment time" };
|
|
31996
|
+
}
|
|
31997
|
+
if (client.session.get === void 0) {
|
|
31998
|
+
return { ...noChain, evidence: "engine client exposes no session.get seam (chain cannot be walked; not a verdict of independence)" };
|
|
31999
|
+
}
|
|
32000
|
+
if (client.session.messages === void 0) {
|
|
32001
|
+
return { ...noChain, convenerChain: [convenerSessionID], evidence: "engine client exposes no session.messages seam (drafting scan impossible)" };
|
|
32002
|
+
}
|
|
32003
|
+
const chain = [convenerSessionID];
|
|
32004
|
+
const seen = new Set(chain);
|
|
32005
|
+
let walkIncomplete = false;
|
|
32006
|
+
let walkReason = "";
|
|
32007
|
+
let current = convenerSessionID;
|
|
32008
|
+
for (let depth = 0; depth < MAX_CHAIN_DEPTH; depth += 1) {
|
|
32009
|
+
const res = await client.session.get({ path: { id: current } });
|
|
32010
|
+
if (res.error !== void 0 || res.data === void 0) {
|
|
32011
|
+
walkIncomplete = true;
|
|
32012
|
+
walkReason = `session.get(${current}) refused (${res.error === void 0 ? "no data" : JSON.stringify(res.error).slice(0, 120)})`;
|
|
32013
|
+
break;
|
|
32014
|
+
}
|
|
32015
|
+
const parent = res.data.parentID ?? "";
|
|
32016
|
+
if (parent.length === 0) break;
|
|
32017
|
+
if (seen.has(parent)) {
|
|
32018
|
+
walkReason = `cycle at ${parent} (store anomaly); walk stopped`;
|
|
32019
|
+
break;
|
|
32020
|
+
}
|
|
32021
|
+
chain.push(parent);
|
|
32022
|
+
seen.add(parent);
|
|
32023
|
+
current = parent;
|
|
32024
|
+
}
|
|
32025
|
+
let scanFailed = "";
|
|
32026
|
+
for (const [i, sid] of chain.entries()) {
|
|
32027
|
+
const res = await client.session.messages({ path: { id: sid }, query: { directory } });
|
|
32028
|
+
if (res.error !== void 0 || res.data === void 0) {
|
|
32029
|
+
scanFailed = scanFailed.length > 0 ? scanFailed : `messages(${sid}) refused`;
|
|
32030
|
+
continue;
|
|
32031
|
+
}
|
|
32032
|
+
const hit = scanMessages(res.data, i, sid, artifact);
|
|
32033
|
+
if (hit !== null) {
|
|
32034
|
+
return { status: "NOT-INDEPENDENT", convenerChain: chain, evidence: hit };
|
|
32035
|
+
}
|
|
32036
|
+
}
|
|
32037
|
+
if (scanFailed.length > 0 || walkIncomplete) {
|
|
32038
|
+
const why = [walkIncomplete ? walkReason : "", scanFailed].filter((s) => s.length > 0).join("; ");
|
|
32039
|
+
return { status: "UNVERIFIABLE", convenerChain: chain, evidence: `partial read: ${why} \u2014 independence was NOT established` };
|
|
32040
|
+
}
|
|
32041
|
+
const note = walkReason.length > 0 ? `; walk note: ${walkReason}` : "";
|
|
32042
|
+
return {
|
|
32043
|
+
status: "INDEPENDENT",
|
|
32044
|
+
convenerChain: chain,
|
|
32045
|
+
evidence: `scanned ${String(chain.length)} session(s) of the convener chain to root; no drafting evidence within read scope${note}`
|
|
32046
|
+
};
|
|
32047
|
+
}
|
|
32048
|
+
function independenceLabel(v) {
|
|
32049
|
+
return v.status === "INDEPENDENT" ? "independence=INDEPENDENT" : `independence=${v.status} \u2014 ${v.evidence}`;
|
|
32050
|
+
}
|
|
32051
|
+
|
|
32052
|
+
// src/instrument.ts
|
|
32053
|
+
import { createHash } from "node:crypto";
|
|
32054
|
+
|
|
32055
|
+
// src/chamber/protocol.ts
|
|
32056
|
+
import { join as join4 } from "node:path";
|
|
32057
|
+
|
|
31898
32058
|
// src/verdict/index.ts
|
|
31899
32059
|
var OPEN_BRACE = 123;
|
|
31900
32060
|
var CLOSE_BRACE = 125;
|
|
@@ -32046,6 +32206,135 @@ async function parseVerdict(text, repair) {
|
|
|
32046
32206
|
return rejectVerdict(`${first.why} + repair-invalid: ${second.why}`);
|
|
32047
32207
|
}
|
|
32048
32208
|
|
|
32209
|
+
// src/state/chamber.ts
|
|
32210
|
+
import { mkdir as mkdir2, readFile as readFile2, readdir, rename as rename2, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
|
|
32211
|
+
import { homedir as homedir3 } from "node:os";
|
|
32212
|
+
import { dirname as dirname2, isAbsolute, join as join3, resolve } from "node:path";
|
|
32213
|
+
var DEFAULT_CHAMBER_LEDGER = join3(homedir3(), ".sibyl", "chamber-ledger.jsonl");
|
|
32214
|
+
var TERMINAL_STATES = ["CONVERGED", "NEEDS_ROUND", "MEMBER_LOST", "TIMEOUT"];
|
|
32215
|
+
var RUN_PROFILES = ["review", "exam"];
|
|
32216
|
+
var RECORD_SCHEMA = 1;
|
|
32217
|
+
function asStringArray(v) {
|
|
32218
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string") ? [...v] : null;
|
|
32219
|
+
}
|
|
32220
|
+
function isRosterRef(v) {
|
|
32221
|
+
if (typeof v !== "object" || v === null) return false;
|
|
32222
|
+
const o = v;
|
|
32223
|
+
const modelOk = o["policyCheck"] === "deny" ? typeof o["modelId"] === "string" : isNonEmptyString(o["modelId"]);
|
|
32224
|
+
return isNonEmptyString(o["role"]) && isNonEmptyString(o["slot"]) && modelOk && (o["policyCheck"] === "allow" || o["policyCheck"] === "deny") && (o["denyReason"] === void 0 || typeof o["denyReason"] === "string");
|
|
32225
|
+
}
|
|
32226
|
+
function isArtifactRef(v) {
|
|
32227
|
+
if (typeof v !== "object" || v === null) return false;
|
|
32228
|
+
const o = v;
|
|
32229
|
+
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";
|
|
32230
|
+
}
|
|
32231
|
+
function validateChamberRecord(raw) {
|
|
32232
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return { ok: false, reason: "record is not an object" };
|
|
32233
|
+
const o = raw;
|
|
32234
|
+
if (o["schema"] !== RECORD_SCHEMA) return { ok: false, reason: `schema must be ${RECORD_SCHEMA}` };
|
|
32235
|
+
if (!isNonEmptyString(o["runId"])) return { ok: false, reason: "runId must be a non-empty string" };
|
|
32236
|
+
if (typeof o["serial"] !== "number" || !Number.isInteger(o["serial"]) || o["serial"] < 1) return { ok: false, reason: "serial must be a positive integer" };
|
|
32237
|
+
if (!RUN_PROFILES.includes(o["profile"])) return { ok: false, reason: `profile must be one of ${RUN_PROFILES.join("|")}` };
|
|
32238
|
+
if (!isNonEmptyString(o["goal"])) return { ok: false, reason: "goal must be a non-empty string" };
|
|
32239
|
+
if (typeof o["target"] !== "string") return { ok: false, reason: "target must be a string" };
|
|
32240
|
+
if (!isNonEmptyString(o["runDir"]) || !isAbsolute(o["runDir"])) return { ok: false, reason: "runDir must be an absolute path" };
|
|
32241
|
+
if (!isNonEmptyString(o["createdAt"]) || Number.isNaN(Date.parse(o["createdAt"]))) return { ok: false, reason: "createdAt must be ISO" };
|
|
32242
|
+
if (!isNonEmptyString(o["updatedAt"]) || Number.isNaN(Date.parse(o["updatedAt"]))) return { ok: false, reason: "updatedAt must be ISO" };
|
|
32243
|
+
if (o["terminal"] !== null && !TERMINAL_STATES.includes(o["terminal"])) return { ok: false, reason: "terminal must be null or a TerminalState" };
|
|
32244
|
+
if (!Array.isArray(o["roster"]) || !o["roster"].every(isRosterRef)) return { ok: false, reason: "roster must be an array of RosterRef" };
|
|
32245
|
+
const judgePool = asStringArray(o["judgePool"]);
|
|
32246
|
+
if (judgePool === null) return { ok: false, reason: "judgePool must be a string array" };
|
|
32247
|
+
if (!isNonEmptyString(o["seed"])) return { ok: false, reason: "seed must be a non-empty string" };
|
|
32248
|
+
if (typeof o["drawCommit"] !== "string" || !/^[0-9a-f]{64}$/.test(o["drawCommit"])) return { ok: false, reason: "drawCommit must be 64-hex" };
|
|
32249
|
+
if (typeof o["judgeModelId"] !== "string") return { ok: false, reason: "judgeModelId must be a string" };
|
|
32250
|
+
if (typeof o["rounds"] !== "number" || !Number.isInteger(o["rounds"]) || o["rounds"] < 0) return { ok: false, reason: "rounds must be a non-negative integer" };
|
|
32251
|
+
if (!Array.isArray(o["artifacts"]) || !o["artifacts"].every(isArtifactRef)) return { ok: false, reason: "artifacts must be an array of ArtifactRef" };
|
|
32252
|
+
if (typeof o["evidenceRows"] !== "number" || !Number.isInteger(o["evidenceRows"]) || o["evidenceRows"] < 0) return { ok: false, reason: "evidenceRows must be a non-negative integer" };
|
|
32253
|
+
if (o["notes"] !== void 0 && typeof o["notes"] !== "string") return { ok: false, reason: "notes must be a string when present" };
|
|
32254
|
+
if (o["instrument"] !== void 0 && !isInstrumentShape(o["instrument"])) {
|
|
32255
|
+
return { ok: false, reason: "instrument must be {rulesHash: 64-hex, components: name->64-hex} when present" };
|
|
32256
|
+
}
|
|
32257
|
+
if (o["independence"] !== void 0 && !isIndependenceShape(o["independence"])) {
|
|
32258
|
+
return { ok: false, reason: "independence must be {status: INDEPENDENT|NOT-INDEPENDENT|UNVERIFIABLE, convenerChain: string[], evidence: string} when present" };
|
|
32259
|
+
}
|
|
32260
|
+
const record3 = {
|
|
32261
|
+
schema: RECORD_SCHEMA,
|
|
32262
|
+
runId: o["runId"],
|
|
32263
|
+
serial: o["serial"],
|
|
32264
|
+
profile: o["profile"],
|
|
32265
|
+
goal: o["goal"],
|
|
32266
|
+
target: o["target"],
|
|
32267
|
+
runDir: o["runDir"],
|
|
32268
|
+
createdAt: o["createdAt"],
|
|
32269
|
+
updatedAt: o["updatedAt"],
|
|
32270
|
+
terminal: o["terminal"],
|
|
32271
|
+
roster: [...o["roster"]],
|
|
32272
|
+
judgePool,
|
|
32273
|
+
seed: o["seed"],
|
|
32274
|
+
drawCommit: o["drawCommit"],
|
|
32275
|
+
judgeModelId: o["judgeModelId"],
|
|
32276
|
+
rounds: o["rounds"],
|
|
32277
|
+
artifacts: [...o["artifacts"]],
|
|
32278
|
+
evidenceRows: o["evidenceRows"]
|
|
32279
|
+
};
|
|
32280
|
+
if (typeof o["notes"] === "string") record3.notes = o["notes"];
|
|
32281
|
+
if (o["instrument"] !== void 0) record3.instrument = rebuildInstrument(o["instrument"]);
|
|
32282
|
+
if (o["independence"] !== void 0) record3.independence = rebuildIndependence(o["independence"]);
|
|
32283
|
+
return { ok: true, record: record3 };
|
|
32284
|
+
}
|
|
32285
|
+
function parseLedgerLine(line) {
|
|
32286
|
+
const trimmed = line.trim();
|
|
32287
|
+
if (trimmed.length === 0) return { ok: false, reason: "blank line" };
|
|
32288
|
+
let parsed;
|
|
32289
|
+
try {
|
|
32290
|
+
parsed = JSON.parse(trimmed);
|
|
32291
|
+
} catch (err) {
|
|
32292
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
32293
|
+
return { ok: false, reason: `not a single JSON document (merged-row guard): ${msg}` };
|
|
32294
|
+
}
|
|
32295
|
+
const v = validateChamberRecord(parsed);
|
|
32296
|
+
if (!v.ok) return { ok: false, reason: v.reason };
|
|
32297
|
+
return { ok: true, record: v.record };
|
|
32298
|
+
}
|
|
32299
|
+
async function loadLedger(ledgerPath) {
|
|
32300
|
+
let text;
|
|
32301
|
+
try {
|
|
32302
|
+
text = await readFile2(ledgerPath, "utf8");
|
|
32303
|
+
} catch {
|
|
32304
|
+
return { rows: [], dropped: [] };
|
|
32305
|
+
}
|
|
32306
|
+
const rows = [];
|
|
32307
|
+
const dropped = [];
|
|
32308
|
+
const lines = text.split("\n");
|
|
32309
|
+
for (const [i, line] of lines.entries()) {
|
|
32310
|
+
if (line.trim().length === 0) continue;
|
|
32311
|
+
const v = parseLedgerLine(line);
|
|
32312
|
+
if (v.ok) rows.push(v.record);
|
|
32313
|
+
else dropped.push({ line: i + 1, reason: v.reason });
|
|
32314
|
+
}
|
|
32315
|
+
return { rows, dropped };
|
|
32316
|
+
}
|
|
32317
|
+
|
|
32318
|
+
// src/chamber/protocol.ts
|
|
32319
|
+
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.`;
|
|
32320
|
+
function evidencePrompt(cfg, abs) {
|
|
32321
|
+
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");
|
|
32322
|
+
}
|
|
32323
|
+
function proPrompt(cfg, round, abs, charges) {
|
|
32324
|
+
const chargeBlock = charges.length > 0 ? ` Prior open charges you must answer: ${JSON.stringify(charges)}.` : "";
|
|
32325
|
+
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)");
|
|
32326
|
+
}
|
|
32327
|
+
function conPrompt(cfg, round, abs, charges) {
|
|
32328
|
+
const chargeBlock = charges.length > 0 ? ` Previously contested \u2014 sharpen or drop: ${JSON.stringify(charges)}.` : "";
|
|
32329
|
+
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)");
|
|
32330
|
+
}
|
|
32331
|
+
function rebuttalPrompt(cfg, role, round, otherAbs, abs) {
|
|
32332
|
+
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");
|
|
32333
|
+
}
|
|
32334
|
+
function judgePrompt(cfg, round, paths, abs) {
|
|
32335
|
+
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.`;
|
|
32336
|
+
}
|
|
32337
|
+
|
|
32049
32338
|
// src/personas.ts
|
|
32050
32339
|
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
32340
|
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 +32344,11 @@ var ARCHITECT_PERSONA = {
|
|
|
32055
32344
|
system: ARCHITECT_SYSTEM,
|
|
32056
32345
|
modelSlot: "architect"
|
|
32057
32346
|
};
|
|
32347
|
+
function repairDemand(why) {
|
|
32348
|
+
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>]}`;
|
|
32349
|
+
}
|
|
32350
|
+
var SWARM_JUDGE_SYSTEM = "You are the SIBYL swarm judge. You rule on pipeline reports with exactly one word: APPROVE, REJECT, or EXHAUSTED.";
|
|
32351
|
+
var SWARM_JUDGE_WORD_CONTRACT = "Respond with EXACTLY one word and nothing else: APPROVE, REJECT, or EXHAUSTED.";
|
|
32058
32352
|
var PERSONAS = {
|
|
32059
32353
|
MELCHIOR: {
|
|
32060
32354
|
id: "MELCHIOR",
|
|
@@ -32081,9 +32375,108 @@ var BY_ID = new Map(
|
|
|
32081
32375
|
PERSONA_IDS.map((id) => [id, PERSONAS[id]])
|
|
32082
32376
|
);
|
|
32083
32377
|
|
|
32378
|
+
// src/instrument.ts
|
|
32379
|
+
function sha256Hex(text) {
|
|
32380
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
32381
|
+
}
|
|
32382
|
+
var REPAIR_SENTINEL = "<parse-failure-why>";
|
|
32383
|
+
var SENTINEL_CFG = {
|
|
32384
|
+
runId: "sibyl-sentinel",
|
|
32385
|
+
runDir: "/sentinel/run",
|
|
32386
|
+
goal: "<goal>",
|
|
32387
|
+
targetPath: "/sentinel/target.md",
|
|
32388
|
+
ledgerPath: "/sentinel/ledger.jsonl",
|
|
32389
|
+
seed: "<seed>",
|
|
32390
|
+
maxRounds: 1,
|
|
32391
|
+
pool: { default: { providerID: "", modelID: "" } },
|
|
32392
|
+
policy: null,
|
|
32393
|
+
slots: { evidence: "default", pro: "default", con: "default", judge: "default" },
|
|
32394
|
+
judgePoolIds: [],
|
|
32395
|
+
lane: null
|
|
32396
|
+
};
|
|
32397
|
+
function chamberRoleTexts() {
|
|
32398
|
+
return {
|
|
32399
|
+
"chamber-evidence-prompt": evidencePrompt(SENTINEL_CFG, "/sentinel/evidence.jsonl"),
|
|
32400
|
+
"chamber-pro-prompt": proPrompt(SENTINEL_CFG, 1, "/sentinel/pro.md", []),
|
|
32401
|
+
"chamber-con-prompt": conPrompt(SENTINEL_CFG, 1, "/sentinel/con.md", []),
|
|
32402
|
+
"chamber-rebuttal-prompt": rebuttalPrompt(SENTINEL_CFG, "pro", 1, "/sentinel/con.md", "/sentinel/pro-rebuttal.md"),
|
|
32403
|
+
"chamber-judge-prompt": judgePrompt(SENTINEL_CFG, 1, ["/sentinel/pro.md", "/sentinel/con.md"], "/sentinel/judge.json")
|
|
32404
|
+
};
|
|
32405
|
+
}
|
|
32406
|
+
function componentTexts() {
|
|
32407
|
+
const texts = {
|
|
32408
|
+
"repair-grammar": repairDemand(REPAIR_SENTINEL),
|
|
32409
|
+
"swarm-judge-system": SWARM_JUDGE_SYSTEM,
|
|
32410
|
+
"swarm-judge-word-contract": SWARM_JUDGE_WORD_CONTRACT,
|
|
32411
|
+
...chamberRoleTexts()
|
|
32412
|
+
};
|
|
32413
|
+
for (const id of COUNCILORS) {
|
|
32414
|
+
texts[`councilor-${id.toLowerCase()}`] = COUNCILOR_PERSONAS[id].system;
|
|
32415
|
+
}
|
|
32416
|
+
texts["architect-system"] = ARCHITECT_SYSTEM;
|
|
32417
|
+
return texts;
|
|
32418
|
+
}
|
|
32419
|
+
var cached3 = null;
|
|
32420
|
+
function instrumentFace() {
|
|
32421
|
+
if (cached3 !== null) return cached3;
|
|
32422
|
+
const components = {};
|
|
32423
|
+
for (const [name, text] of Object.entries(componentTexts())) {
|
|
32424
|
+
components[name] = sha256Hex(text);
|
|
32425
|
+
}
|
|
32426
|
+
const canonical = JSON.stringify(
|
|
32427
|
+
Object.keys(components).sort().map((name) => [name, components[name]])
|
|
32428
|
+
);
|
|
32429
|
+
cached3 = { rulesHash: sha256Hex(canonical), components };
|
|
32430
|
+
return cached3;
|
|
32431
|
+
}
|
|
32432
|
+
function rulesLabel(face) {
|
|
32433
|
+
return `rules=${face.rulesHash.slice(0, 12)}`;
|
|
32434
|
+
}
|
|
32435
|
+
|
|
32436
|
+
// src/terminal.ts
|
|
32437
|
+
var TERMINAL_STATUSES = ["ok", "error", "timeout"];
|
|
32438
|
+
var TERMINAL_DETAIL_MAX = 240;
|
|
32439
|
+
function sanitizeDetail(text) {
|
|
32440
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
32441
|
+
return flat.length <= TERMINAL_DETAIL_MAX ? flat : `${flat.slice(0, TERMINAL_DETAIL_MAX)}\u2026`;
|
|
32442
|
+
}
|
|
32443
|
+
function classifyFailure(error105) {
|
|
32444
|
+
return /timeout|timed out|deadline/i.test(error105) ? "timeout" : "error";
|
|
32445
|
+
}
|
|
32446
|
+
function formatTerminalRow(at, row) {
|
|
32447
|
+
return `ts=${at.toISOString()} task=${row.task} worker=${row.worker} status=${row.status} session=${row.session.length > 0 ? row.session : "-"} detail=${sanitizeDetail(row.detail)}`;
|
|
32448
|
+
}
|
|
32449
|
+
function parseTerminalRows(text) {
|
|
32450
|
+
const byTask = /* @__PURE__ */ new Map();
|
|
32451
|
+
const lines = text.split("\n");
|
|
32452
|
+
for (const [i, line] of lines.entries()) {
|
|
32453
|
+
if (line.trim().length === 0) continue;
|
|
32454
|
+
const task = /(?:^| )task=(\S+)/.exec(line)?.[1];
|
|
32455
|
+
const statusRaw = /(?:^| )status=(\S+)/.exec(line)?.[1];
|
|
32456
|
+
if (task === void 0 || statusRaw === void 0) continue;
|
|
32457
|
+
const status = TERMINAL_STATUSES.includes(statusRaw) ? statusRaw : void 0;
|
|
32458
|
+
if (status === void 0) continue;
|
|
32459
|
+
byTask.set(task, {
|
|
32460
|
+
task,
|
|
32461
|
+
worker: /(?:^| )worker=(\S+)/.exec(line)?.[1] ?? "-",
|
|
32462
|
+
status,
|
|
32463
|
+
session: /(?:^| )session=(\S+)/.exec(line)?.[1] ?? "-",
|
|
32464
|
+
detail: /(?:^| )detail=([\s\S]*)$/.exec(line)?.[1] ?? "",
|
|
32465
|
+
line: i + 1
|
|
32466
|
+
});
|
|
32467
|
+
}
|
|
32468
|
+
return byTask;
|
|
32469
|
+
}
|
|
32470
|
+
function deadWithoutRecord(declaredTaskIds, rows) {
|
|
32471
|
+
return declaredTaskIds.filter((id) => !rows.has(id));
|
|
32472
|
+
}
|
|
32473
|
+
function deadFaceCount(dead) {
|
|
32474
|
+
return `dead-without-record=${String(dead.length)}`;
|
|
32475
|
+
}
|
|
32476
|
+
|
|
32084
32477
|
// src/tools/shared.ts
|
|
32085
|
-
import { readFile as
|
|
32086
|
-
import { isAbsolute, resolve } from "node:path";
|
|
32478
|
+
import { readFile as readFile3, stat } from "node:fs/promises";
|
|
32479
|
+
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
32087
32480
|
var ARTIFACT_MAX_BYTES = 262144;
|
|
32088
32481
|
var DEFAULT_SLOT2 = "default";
|
|
32089
32482
|
function slotForModel(pool, overrideSlot, personaSlot) {
|
|
@@ -32108,7 +32501,7 @@ async function readArtifact(raw, cwd) {
|
|
|
32108
32501
|
text: trimmed
|
|
32109
32502
|
};
|
|
32110
32503
|
}
|
|
32111
|
-
const path =
|
|
32504
|
+
const path = isAbsolute2(trimmed) ? trimmed : resolve2(cwd, trimmed);
|
|
32112
32505
|
let info;
|
|
32113
32506
|
try {
|
|
32114
32507
|
info = await stat(path);
|
|
@@ -32126,7 +32519,7 @@ async function readArtifact(raw, cwd) {
|
|
|
32126
32519
|
}
|
|
32127
32520
|
let text;
|
|
32128
32521
|
try {
|
|
32129
|
-
text = await
|
|
32522
|
+
text = await readFile3(path, "utf8");
|
|
32130
32523
|
} catch (err) {
|
|
32131
32524
|
return { ok: false, error: `artifact "${path}": ${errMessage(err)}` };
|
|
32132
32525
|
}
|
|
@@ -32196,7 +32589,8 @@ function formatRunLine(record3) {
|
|
|
32196
32589
|
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
32590
|
const rounds = record3.rounds === void 0 ? "" : ` rounds=${String(record3.rounds)}`;
|
|
32198
32591
|
const goal = record3.goal === void 0 ? "" : ` goal="${record3.goal}"`;
|
|
32199
|
-
|
|
32592
|
+
const rules = record3.instrument === void 0 ? "" : ` rules=${record3.instrument.rulesHash.slice(0, 12)}`;
|
|
32593
|
+
return `${record3.runId} ${record3.kind} ${record3.status} ${verdict}${rounds}${goal}${rules} created=${record3.createdAt} updated=${record3.updatedAt} space=${record3.spaceDir}`;
|
|
32200
32594
|
}
|
|
32201
32595
|
|
|
32202
32596
|
// src/tools/consult.ts
|
|
@@ -32210,9 +32604,6 @@ ${goal}
|
|
|
32210
32604
|
ARTIFACT:
|
|
32211
32605
|
${artifactText}`;
|
|
32212
32606
|
}
|
|
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
32607
|
var REPLY_HEADER = "<!-- SIBYL consult voter reply (plan task 8). Full raw session text below; parse/repair metadata at the end. -->\n";
|
|
32217
32608
|
function renderReply(voter, text, meta3) {
|
|
32218
32609
|
return `${REPLY_HEADER}
|
|
@@ -32230,7 +32621,7 @@ async function castVote(job) {
|
|
|
32230
32621
|
const { client, options } = deps;
|
|
32231
32622
|
const persona = PERSONAS[id];
|
|
32232
32623
|
const model = slotForModel(options.modelPool, options.voters[id], persona.modelSlot);
|
|
32233
|
-
const replyPath =
|
|
32624
|
+
const replyPath = join5(spaceDir, `${id}.md`);
|
|
32234
32625
|
const result = await runPersona({
|
|
32235
32626
|
client,
|
|
32236
32627
|
directory,
|
|
@@ -32244,7 +32635,7 @@ async function castVote(job) {
|
|
|
32244
32635
|
const error105 = `${result.stage ?? "engine"}: ${result.error ?? "unknown error"}`;
|
|
32245
32636
|
const vote2 = { id, ok: false, error: error105 };
|
|
32246
32637
|
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 };
|
|
32638
|
+
return { id, vote: vote2, replyPath: replyAt2, sessionId: result.sessionID, modelApplied: result.modelApplied, latencyMs: result.latencyMs };
|
|
32248
32639
|
}
|
|
32249
32640
|
const verdict = await parseVerdict(result.text, async (_badText, why) => {
|
|
32250
32641
|
const followed = await runInSession(
|
|
@@ -32252,7 +32643,7 @@ async function castVote(job) {
|
|
|
32252
32643
|
directory,
|
|
32253
32644
|
result.sessionID,
|
|
32254
32645
|
model,
|
|
32255
|
-
|
|
32646
|
+
repairDemand(why),
|
|
32256
32647
|
options.timeoutMs
|
|
32257
32648
|
);
|
|
32258
32649
|
return followed.ok ? followed.text : "";
|
|
@@ -32261,11 +32652,11 @@ async function castVote(job) {
|
|
|
32261
32652
|
const vote = unparseable ? { id, ok: false, error: verdict.reasons[0] ?? "verdict-unparseable" } : { id, ok: true, verdict };
|
|
32262
32653
|
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
32654
|
const replyAt = await safeWrite(replyPath, renderReply(id, result.text, meta3));
|
|
32264
|
-
return { id, vote, replyPath: replyAt, modelApplied: result.modelApplied, latencyMs: result.latencyMs };
|
|
32655
|
+
return { id, vote, replyPath: replyAt, sessionId: result.sessionID, modelApplied: result.modelApplied, latencyMs: result.latencyMs };
|
|
32265
32656
|
}
|
|
32266
32657
|
async function safeWrite(path, text) {
|
|
32267
32658
|
try {
|
|
32268
|
-
await
|
|
32659
|
+
await writeFile3(path, text, "utf8");
|
|
32269
32660
|
return path;
|
|
32270
32661
|
} catch (err) {
|
|
32271
32662
|
return `<${path} write failed: ${errMessage(err)}>`;
|
|
@@ -32292,6 +32683,7 @@ async function consultExecute(deps, args, context) {
|
|
|
32292
32683
|
if (!read.ok) {
|
|
32293
32684
|
return `SIBYL consult: ${read.error}`;
|
|
32294
32685
|
}
|
|
32686
|
+
const independence = await assessIndependence(deps.client, context.directory, context.sessionID, read);
|
|
32295
32687
|
const { record: record3 } = await deps.store.createRun({
|
|
32296
32688
|
kind: "consult",
|
|
32297
32689
|
artifact: read.source,
|
|
@@ -32303,25 +32695,51 @@ async function consultExecute(deps, args, context) {
|
|
|
32303
32695
|
)
|
|
32304
32696
|
);
|
|
32305
32697
|
const tally = tallyVotes(outcomes.map((o) => o.vote));
|
|
32698
|
+
const terminalsPath = join5(record3.spaceDir, "TERMINALS.txt");
|
|
32699
|
+
await Promise.all(
|
|
32700
|
+
outcomes.map((o) => {
|
|
32701
|
+
const vote = o.vote;
|
|
32702
|
+
const status = "missing" in vote ? "error" : vote.ok ? "ok" : classifyFailure(vote.error);
|
|
32703
|
+
const detail = "missing" in vote || !vote.ok ? `ballot=${ballotTag(vote)}` : `ballot=${vote.verdict.verdict}`;
|
|
32704
|
+
const line = formatTerminalRow(/* @__PURE__ */ new Date(), {
|
|
32705
|
+
task: o.id,
|
|
32706
|
+
worker: o.id,
|
|
32707
|
+
status,
|
|
32708
|
+
session: o.sessionId,
|
|
32709
|
+
detail
|
|
32710
|
+
});
|
|
32711
|
+
return appendFile(terminalsPath, `${line}
|
|
32712
|
+
`, "utf8").catch((err) => {
|
|
32713
|
+
console.error(`[sibyl consult] terminal row for ${o.id} failed to write: ${errMessage(err)}`);
|
|
32714
|
+
});
|
|
32715
|
+
})
|
|
32716
|
+
);
|
|
32717
|
+
const rows = parseTerminalRows(await readFile4(terminalsPath, "utf8").catch(() => ""));
|
|
32718
|
+
const dead = deadWithoutRecord([...COUNCILORS], rows);
|
|
32719
|
+
const effectiveVerdict = dead.length > 0 ? "CANNOT_ANSWER" : tally.verdict;
|
|
32306
32720
|
const finalRecord = {
|
|
32307
32721
|
...record3,
|
|
32308
32722
|
status: "done",
|
|
32309
32723
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32310
32724
|
verdict: {
|
|
32311
|
-
verdict:
|
|
32725
|
+
verdict: effectiveVerdict,
|
|
32312
32726
|
approvals: tally.approvals,
|
|
32313
32727
|
rejects: tally.rejects,
|
|
32314
32728
|
errors: tally.errors,
|
|
32315
32729
|
missing: tally.missing
|
|
32316
32730
|
},
|
|
32317
|
-
|
|
32731
|
+
instrument: instrumentFace(),
|
|
32732
|
+
independence,
|
|
32733
|
+
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
32734
|
};
|
|
32319
32735
|
await deps.store.appendOrUpdate(finalRecord);
|
|
32320
32736
|
const lines = [
|
|
32321
|
-
`SIBYL CONSULT: ${
|
|
32737
|
+
`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}`,
|
|
32738
|
+
` ${independenceLabel(independence)}`,
|
|
32322
32739
|
...tally.reasons.map((r) => ` reason: ${r}`),
|
|
32323
32740
|
...tally.must_fix.map((m) => ` must_fix: ${m}`),
|
|
32324
32741
|
...outcomes.map((o) => ` reply: ${o.id} -> ${o.replyPath}`),
|
|
32742
|
+
` ${rulesLabel(finalRecord.instrument ?? instrumentFace())} components=${String(Object.keys(instrumentFace().components).length)}`,
|
|
32325
32743
|
` store: ${deps.store.runsFile}`
|
|
32326
32744
|
];
|
|
32327
32745
|
return lines.join("\n");
|
|
@@ -32335,20 +32753,20 @@ function ballotTag(vote) {
|
|
|
32335
32753
|
import { spawn } from "node:child_process";
|
|
32336
32754
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
32337
32755
|
import { existsSync as existsSync2 } from "node:fs";
|
|
32338
|
-
import { mkdir as
|
|
32339
|
-
import { dirname as
|
|
32756
|
+
import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
|
|
32757
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
32340
32758
|
import { fileURLToPath } from "node:url";
|
|
32341
32759
|
var REVIEW_TOOL_NAME = "sibyl_review";
|
|
32342
32760
|
function nodeBinary() {
|
|
32343
32761
|
return existsSync2("/usr/bin/node") ? "/usr/bin/node" : process.execPath;
|
|
32344
32762
|
}
|
|
32345
32763
|
function cliCommand(thisUrl, node2) {
|
|
32346
|
-
const moduleDir =
|
|
32347
|
-
const repoRoot =
|
|
32348
|
-
const distCli =
|
|
32764
|
+
const moduleDir = dirname3(fileURLToPath(thisUrl));
|
|
32765
|
+
const repoRoot = dirname3(dirname3(moduleDir));
|
|
32766
|
+
const distCli = join6(repoRoot, "dist", "cli.js");
|
|
32349
32767
|
if (existsSync2(distCli)) return { bin: node2, args: [distCli] };
|
|
32350
|
-
const srcCli =
|
|
32351
|
-
const hooks =
|
|
32768
|
+
const srcCli = join6(repoRoot, "src", "cli.ts");
|
|
32769
|
+
const hooks = join6(repoRoot, "tools", "register-ts.mjs");
|
|
32352
32770
|
return { bin: node2, args: ["--import", hooks, srcCli] };
|
|
32353
32771
|
}
|
|
32354
32772
|
async function reviewExecute(deps, args, context) {
|
|
@@ -32357,29 +32775,38 @@ async function reviewExecute(deps, args, context) {
|
|
|
32357
32775
|
const opts = deps.options;
|
|
32358
32776
|
const now = /* @__PURE__ */ new Date();
|
|
32359
32777
|
const runId = `sibyl-${compactIso(now)}-${randomBytes2(2).toString("hex")}`;
|
|
32360
|
-
const runDir =
|
|
32361
|
-
await
|
|
32778
|
+
const runDir = join6(opts.lane.runRoot, `sibyl-run-${runId}`);
|
|
32779
|
+
await mkdir3(runDir, { recursive: true });
|
|
32362
32780
|
const artifact = await readArtifact(args.target, context.directory);
|
|
32363
32781
|
if (!artifact.ok) return `SIBYL review: ${artifact.error}`;
|
|
32364
32782
|
let targetArg;
|
|
32365
32783
|
if (artifact.kind === "inline") {
|
|
32366
|
-
targetArg =
|
|
32367
|
-
await
|
|
32784
|
+
targetArg = join6(runDir, "target-inline.md");
|
|
32785
|
+
await writeFile4(targetArg, artifact.text, "utf8");
|
|
32368
32786
|
} else {
|
|
32369
32787
|
targetArg = artifact.source;
|
|
32370
32788
|
}
|
|
32371
|
-
await
|
|
32789
|
+
await writeFile4(join6(runDir, "run-config.json"), `${JSON.stringify(opts, null, 2)}
|
|
32372
32790
|
`, "utf8");
|
|
32791
|
+
const independence = await assessIndependence(deps.client, context.directory, context.sessionID, artifact);
|
|
32792
|
+
await writeFile4(
|
|
32793
|
+
join6(runDir, "launch-context.json"),
|
|
32794
|
+
`${JSON.stringify({ convenerSessionID: context.sessionID, independence }, null, 2)}
|
|
32795
|
+
`,
|
|
32796
|
+
"utf8"
|
|
32797
|
+
);
|
|
32373
32798
|
const cmd = cliCommand(import.meta.url, nodeBinary());
|
|
32374
32799
|
const spawnArgs = [
|
|
32375
32800
|
...cmd.args,
|
|
32376
32801
|
"run",
|
|
32802
|
+
"--run-id",
|
|
32803
|
+
runId,
|
|
32377
32804
|
"--target",
|
|
32378
32805
|
targetArg,
|
|
32379
32806
|
"--goal",
|
|
32380
32807
|
args.goal,
|
|
32381
32808
|
"--config",
|
|
32382
|
-
|
|
32809
|
+
join6(runDir, "run-config.json"),
|
|
32383
32810
|
"--run-root",
|
|
32384
32811
|
opts.lane.runRoot,
|
|
32385
32812
|
"--seed",
|
|
@@ -32402,8 +32829,10 @@ async function reviewExecute(deps, args, context) {
|
|
|
32402
32829
|
` run: ${receipt.runId}`,
|
|
32403
32830
|
` dir: ${receipt.runDir}`,
|
|
32404
32831
|
` status: ${receipt.statusCmd}`,
|
|
32405
|
-
` voice at: ${
|
|
32406
|
-
` verify: ${receipt.spotcheck}
|
|
32832
|
+
` voice at: ${join6(runDir, "run-record.json")} + chamber-ledger.jsonl (append-only)`,
|
|
32833
|
+
` verify: ${receipt.spotcheck}`,
|
|
32834
|
+
` ${rulesLabel(instrumentFace())} (the runner records its own full face into the run record)`,
|
|
32835
|
+
` ${independenceLabel(independence)}`
|
|
32407
32836
|
].join("\n");
|
|
32408
32837
|
} catch (err) {
|
|
32409
32838
|
return internalError(REVIEW_TOOL_NAME, err);
|
|
@@ -32423,7 +32852,7 @@ function buildReviewTool(deps) {
|
|
|
32423
32852
|
}
|
|
32424
32853
|
|
|
32425
32854
|
// src/tools/anchor.ts
|
|
32426
|
-
import { createHash } from "node:crypto";
|
|
32855
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
32427
32856
|
function canonicalView(rows) {
|
|
32428
32857
|
return JSON.stringify(
|
|
32429
32858
|
rows.map((r) => ({
|
|
@@ -32436,7 +32865,7 @@ function canonicalText(raw) {
|
|
|
32436
32865
|
return raw.replace(/\s*<dcp-message-id[^>]*>[\s\S]*?<\/dcp-message-id>\s*$/g, "").trim();
|
|
32437
32866
|
}
|
|
32438
32867
|
function sha256(text) {
|
|
32439
|
-
return
|
|
32868
|
+
return createHash2("sha256").update(text, "utf8").digest("hex");
|
|
32440
32869
|
}
|
|
32441
32870
|
function anchorText(row) {
|
|
32442
32871
|
return (row.parts ?? []).filter((p) => p.type === "text").map((p) => p.text ?? "").join("\n");
|
|
@@ -32633,110 +33062,9 @@ function buildAttributeTool(deps) {
|
|
|
32633
33062
|
});
|
|
32634
33063
|
}
|
|
32635
33064
|
|
|
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
33065
|
// src/tools/status.ts
|
|
32738
33066
|
import { dirname as dirname4 } from "node:path";
|
|
32739
|
-
import { join as
|
|
33067
|
+
import { join as join7 } from "node:path";
|
|
32740
33068
|
var STATUS_TOOL_NAME = "sibyl_status";
|
|
32741
33069
|
function buildStatusTool(deps) {
|
|
32742
33070
|
return tool({
|
|
@@ -32757,7 +33085,7 @@ async function statusExecute(deps, args, _context) {
|
|
|
32757
33085
|
return [`SIBYL RUN ${run2.runId}`, formatRunLine(run2)].join("\n");
|
|
32758
33086
|
}
|
|
32759
33087
|
const runs = await deps.store.load();
|
|
32760
|
-
const ledger = await loadLedger(
|
|
33088
|
+
const ledger = await loadLedger(join7(dirname4(deps.store.runsFile), "chamber-ledger.jsonl"));
|
|
32761
33089
|
const chamberLines = ledger.rows.slice(-5).map(
|
|
32762
33090
|
(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
33091
|
);
|
|
@@ -32773,8 +33101,8 @@ async function statusExecute(deps, args, _context) {
|
|
|
32773
33101
|
}
|
|
32774
33102
|
|
|
32775
33103
|
// src/tools/swarm.ts
|
|
32776
|
-
import { join as
|
|
32777
|
-
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
33104
|
+
import { join as join8 } from "node:path";
|
|
33105
|
+
import { appendFile as appendFile2, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
32778
33106
|
|
|
32779
33107
|
// src/swarm/types.ts
|
|
32780
33108
|
var SWARM_VERDICTS = {
|
|
@@ -32911,11 +33239,24 @@ function composeTaskPrompt(ctx, taskId) {
|
|
|
32911
33239
|
Upstream conclusions (from completed dependencies):
|
|
32912
33240
|
${lines.join("\n")}`;
|
|
32913
33241
|
}
|
|
33242
|
+
async function recordTerminal(ctx, taskId, workerId, status, session, detail) {
|
|
33243
|
+
if (ctx.writeTerminal === void 0) return true;
|
|
33244
|
+
try {
|
|
33245
|
+
await ctx.writeTerminal(
|
|
33246
|
+
formatTerminalRow(/* @__PURE__ */ new Date(), { task: taskId, worker: workerId, status, session, detail })
|
|
33247
|
+
);
|
|
33248
|
+
return true;
|
|
33249
|
+
} catch (e) {
|
|
33250
|
+
console.error(`[sibyl dispatch] terminal line for ${taskId} failed to write: ${describeFailure(e)}`);
|
|
33251
|
+
return false;
|
|
33252
|
+
}
|
|
33253
|
+
}
|
|
32914
33254
|
async function runTask(ctx, task) {
|
|
32915
33255
|
const worker = ctx.workerByTask.get(task.taskId);
|
|
32916
33256
|
if (worker === void 0) {
|
|
32917
33257
|
task.status = "failed";
|
|
32918
33258
|
task.error = `no worker spec for task "${task.taskId}"`;
|
|
33259
|
+
await recordTerminal(ctx, task.taskId, "-", "error", "-", task.error);
|
|
32919
33260
|
return;
|
|
32920
33261
|
}
|
|
32921
33262
|
for (let attempt = 1; attempt <= ctx.maxAttempts; attempt++) {
|
|
@@ -32936,30 +33277,45 @@ async function runTask(ctx, task) {
|
|
|
32936
33277
|
} catch (e) {
|
|
32937
33278
|
task.status = "failed";
|
|
32938
33279
|
task.error = `runPersona threw: ${describeFailure(e)}`;
|
|
33280
|
+
await recordTerminal(ctx, task.taskId, worker.workerId, classifyFailure(task.error), "-", task.error);
|
|
32939
33281
|
return;
|
|
32940
33282
|
}
|
|
32941
33283
|
if (result.ok) {
|
|
32942
|
-
|
|
32943
|
-
|
|
32944
|
-
|
|
32945
|
-
|
|
32946
|
-
|
|
32947
|
-
|
|
33284
|
+
if (result.text.trim().length === 0) {
|
|
33285
|
+
task.error = "empty-reply: worker session ended with no content (absence is not a ballot)";
|
|
33286
|
+
} else {
|
|
33287
|
+
let ref;
|
|
33288
|
+
try {
|
|
33289
|
+
ref = await ctx.writeDraft({ taskId: task.taskId, workerId: worker.workerId, text: result.text });
|
|
33290
|
+
} catch (e) {
|
|
33291
|
+
task.status = "failed";
|
|
33292
|
+
task.error = `draft write failed: ${describeFailure(e)}`;
|
|
33293
|
+
await recordTerminal(ctx, task.taskId, worker.workerId, "error", result.sessionID, task.error);
|
|
33294
|
+
return;
|
|
33295
|
+
}
|
|
33296
|
+
const recorded = await recordTerminal(ctx, task.taskId, worker.workerId, "ok", result.sessionID, `draft=${ref}`);
|
|
33297
|
+
if (!recorded) {
|
|
33298
|
+
task.status = "failed";
|
|
33299
|
+
task.error = "terminal record write failed (W2: done without its record never stands)";
|
|
33300
|
+
return;
|
|
33301
|
+
}
|
|
33302
|
+
task.refs.push(ref);
|
|
33303
|
+
task.summary = summarizeTaskOutput(result.text, ctx.summaryMaxChars);
|
|
33304
|
+
if (result.sessionID !== "") task.sessionId = result.sessionID;
|
|
33305
|
+
task.status = "done";
|
|
32948
33306
|
return;
|
|
32949
33307
|
}
|
|
32950
|
-
|
|
32951
|
-
task.
|
|
32952
|
-
if (result.sessionID !== "") task.sessionId = result.sessionID;
|
|
32953
|
-
task.status = "done";
|
|
32954
|
-
return;
|
|
33308
|
+
} else {
|
|
33309
|
+
task.error = result.error ?? "unknown engine error";
|
|
32955
33310
|
}
|
|
32956
|
-
task.error = result.error ?? "unknown engine error";
|
|
32957
33311
|
if (isRateLimitError(task.error)) {
|
|
32958
33312
|
task.status = "suspended";
|
|
33313
|
+
await recordTerminal(ctx, task.taskId, worker.workerId, "error", "-", `suspended: ${task.error}`);
|
|
32959
33314
|
return;
|
|
32960
33315
|
}
|
|
32961
33316
|
}
|
|
32962
33317
|
task.status = "failed";
|
|
33318
|
+
await recordTerminal(ctx, task.taskId, worker.workerId, classifyFailure(task.error ?? ""), "-", task.error ?? "attempts exhausted");
|
|
32963
33319
|
}
|
|
32964
33320
|
async function runWaves(ctx, state) {
|
|
32965
33321
|
for (; ; ) {
|
|
@@ -32967,7 +33323,12 @@ async function runWaves(ctx, state) {
|
|
|
32967
33323
|
if (schedulable.length === 0) break;
|
|
32968
33324
|
if (state.rounds >= ctx.maxRounds) {
|
|
32969
33325
|
state.exhausted = true;
|
|
32970
|
-
for (const t of state.tasks)
|
|
33326
|
+
for (const t of state.tasks) {
|
|
33327
|
+
if (t.status === "pending") {
|
|
33328
|
+
t.status = "suspended";
|
|
33329
|
+
await recordTerminal(ctx, t.taskId, ctx.workerByTask.get(t.taskId)?.workerId ?? "-", "error", "-", "exhausted: round budget spent before launch");
|
|
33330
|
+
}
|
|
33331
|
+
}
|
|
32971
33332
|
break;
|
|
32972
33333
|
}
|
|
32973
33334
|
state.rounds += 1;
|
|
@@ -32978,9 +33339,12 @@ async function runWaves(ctx, state) {
|
|
|
32978
33339
|
})());
|
|
32979
33340
|
await Promise.all(launches);
|
|
32980
33341
|
}
|
|
32981
|
-
for (const t of state.tasks)
|
|
32982
|
-
t.status
|
|
32983
|
-
|
|
33342
|
+
for (const t of state.tasks) {
|
|
33343
|
+
if (t.status === "pending") {
|
|
33344
|
+
t.status = "failed";
|
|
33345
|
+
t.error = "blocked by non-done dependency";
|
|
33346
|
+
await recordTerminal(ctx, t.taskId, ctx.workerByTask.get(t.taskId)?.workerId ?? "-", "error", "-", t.error);
|
|
33347
|
+
}
|
|
32984
33348
|
}
|
|
32985
33349
|
}
|
|
32986
33350
|
function materialize(state) {
|
|
@@ -33028,7 +33392,8 @@ async function run(prior, roster, opts) {
|
|
|
33028
33392
|
summaryMaxChars,
|
|
33029
33393
|
maxRounds,
|
|
33030
33394
|
clock: opts.clock ?? realClock(),
|
|
33031
|
-
writeDraft: opts.writeDraft
|
|
33395
|
+
writeDraft: opts.writeDraft,
|
|
33396
|
+
writeTerminal: opts.writeTerminal
|
|
33032
33397
|
};
|
|
33033
33398
|
let state = prior ?? makeState(roster);
|
|
33034
33399
|
if (prior !== void 0) {
|
|
@@ -33252,7 +33617,7 @@ function buildJudgeInput(report) {
|
|
|
33252
33617
|
...report.tasks.map((t) => `TASK ${t.id}: ${t.status}${t.summary === void 0 ? "" : ` \u2014 ${t.summary}`}`),
|
|
33253
33618
|
...report.artifacts.map((a) => `ARTIFACT: ${a}`),
|
|
33254
33619
|
"",
|
|
33255
|
-
|
|
33620
|
+
SWARM_JUDGE_WORD_CONTRACT
|
|
33256
33621
|
];
|
|
33257
33622
|
return lines.join("\n");
|
|
33258
33623
|
}
|
|
@@ -33294,6 +33659,7 @@ async function swarmExecute(deps, args, context) {
|
|
|
33294
33659
|
if (!read.ok) {
|
|
33295
33660
|
return `SIBYL swarm: ${read.error}`;
|
|
33296
33661
|
}
|
|
33662
|
+
const independence = await assessIndependence(client, directory, context.sessionID, read);
|
|
33297
33663
|
const { record: record3 } = await store.createRun({ kind: "swarm", artifact: read.source, goal: args.goal });
|
|
33298
33664
|
const architectModel = slotForModel(options.modelPool, void 0, PERSONAS.ARCHITECT.modelSlot);
|
|
33299
33665
|
const plan = await planWorkflow({
|
|
@@ -33311,7 +33677,7 @@ async function swarmExecute(deps, args, context) {
|
|
|
33311
33677
|
directory,
|
|
33312
33678
|
persona: { system: PERSONAS.ARCHITECT.system },
|
|
33313
33679
|
model: architectModel,
|
|
33314
|
-
inputText: `${
|
|
33680
|
+
inputText: `${repairDemand(why)} matching the workflow-schema contract: {"tasks":[{"id","title","instructions","dependsOn"}],"concurrency":<int >= 1>,"notes":<string>}`,
|
|
33315
33681
|
timeoutMs: options.timeoutMs,
|
|
33316
33682
|
parentID: context.sessionID
|
|
33317
33683
|
});
|
|
@@ -33334,7 +33700,7 @@ async function swarmExecute(deps, args, context) {
|
|
|
33334
33700
|
staggerMs: options.staggerMs,
|
|
33335
33701
|
maxRounds: options.maxRounds,
|
|
33336
33702
|
writeDraft: async ({ taskId, workerId, text }) => {
|
|
33337
|
-
const path =
|
|
33703
|
+
const path = join8(record3.spaceDir, `${workerId}.draft.md`);
|
|
33338
33704
|
try {
|
|
33339
33705
|
await writeFile5(path, `<!-- SIBYL swarm draft task=${taskId} worker=${workerId} -->
|
|
33340
33706
|
${text}
|
|
@@ -33343,7 +33709,9 @@ ${text}
|
|
|
33343
33709
|
} catch (err) {
|
|
33344
33710
|
return `<${path} write failed: ${errMessage(err)}>`;
|
|
33345
33711
|
}
|
|
33346
|
-
}
|
|
33712
|
+
},
|
|
33713
|
+
writeTerminal: (line) => appendFile2(join8(record3.spaceDir, "TERMINALS.txt"), `${line}
|
|
33714
|
+
`, "utf8")
|
|
33347
33715
|
});
|
|
33348
33716
|
if (!dispatch.ok) return failRun(deps, record3, { stage: "dispatch", error: dispatch.error });
|
|
33349
33717
|
let report = buildReport({
|
|
@@ -33357,7 +33725,7 @@ ${text}
|
|
|
33357
33725
|
const judged = await runPersona({
|
|
33358
33726
|
client,
|
|
33359
33727
|
directory,
|
|
33360
|
-
persona: { system:
|
|
33728
|
+
persona: { system: SWARM_JUDGE_SYSTEM },
|
|
33361
33729
|
model: slotForModel(options.modelPool, options.swarm.judge, void 0),
|
|
33362
33730
|
inputText: buildJudgeInput(report),
|
|
33363
33731
|
timeoutMs: options.timeoutMs,
|
|
@@ -33369,23 +33737,32 @@ ${text}
|
|
|
33369
33737
|
}
|
|
33370
33738
|
const counts = countTasks(report.tasks);
|
|
33371
33739
|
const noSignal = counts.done === 0;
|
|
33372
|
-
const
|
|
33740
|
+
const terminalsText = await readFile5(join8(record3.spaceDir, "TERMINALS.txt"), "utf8").catch(() => "");
|
|
33741
|
+
const dead = deadWithoutRecord(
|
|
33742
|
+
plan.schema.tasks.map((t) => t.id),
|
|
33743
|
+
parseTerminalRows(terminalsText)
|
|
33744
|
+
);
|
|
33745
|
+
const tag = dead.length > 0 || noSignal ? "CANNOT_ANSWER" : report.verdict === "APPROVE" ? "APPROVE" : "REJECT";
|
|
33373
33746
|
await finishRun(deps, record3, {
|
|
33374
33747
|
status: "done",
|
|
33375
33748
|
rounds: report.rounds,
|
|
33749
|
+
instrument: instrumentFace(),
|
|
33750
|
+
independence,
|
|
33376
33751
|
verdict: {
|
|
33377
33752
|
verdict: tag,
|
|
33378
33753
|
approvals: counts.done,
|
|
33379
|
-
rejects: noSignal ? 0 : tag === "REJECT" ? 1 : 0,
|
|
33754
|
+
rejects: noSignal || dead.length > 0 ? 0 : tag === "REJECT" ? 1 : 0,
|
|
33380
33755
|
errors: counts.failed,
|
|
33381
33756
|
missing: counts.suspended
|
|
33382
33757
|
},
|
|
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(",")}`
|
|
33758
|
+
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
33759
|
});
|
|
33385
33760
|
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})`,
|
|
33761
|
+
`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})`,
|
|
33762
|
+
` ${independenceLabel(independence)}`,
|
|
33387
33763
|
...report.tasks.map((t) => ` task: ${t.id} ${t.status}${t.sessionId === void 0 ? "" : ` session=${t.sessionId}`}`),
|
|
33388
33764
|
...report.artifacts.map((a) => ` artifact: ${a}`),
|
|
33765
|
+
` ${rulesLabel(instrumentFace())} components=${String(Object.keys(instrumentFace().components).length)}`,
|
|
33389
33766
|
` space: ${record3.spaceDir}`,
|
|
33390
33767
|
` store: ${store.runsFile}`
|
|
33391
33768
|
];
|
|
@@ -33485,12 +33862,29 @@ function toEngineClient(sdk) {
|
|
|
33485
33862
|
},
|
|
33486
33863
|
parts: m.parts.map((p) => ({
|
|
33487
33864
|
type: p.type,
|
|
33488
|
-
..."text" in p && p.text !== void 0 && { text: p.text }
|
|
33865
|
+
..."text" in p && p.text !== void 0 && { text: p.text },
|
|
33866
|
+
// W1 drafting-evidence read: tool name + input bag of tool parts
|
|
33867
|
+
...p.type === "tool" && {
|
|
33868
|
+
tool: p.tool,
|
|
33869
|
+
...p.state.input !== void 0 && { input: p.state.input }
|
|
33870
|
+
}
|
|
33489
33871
|
}))
|
|
33490
33872
|
}))
|
|
33491
33873
|
},
|
|
33492
33874
|
...r.error !== void 0 && { error: r.error }
|
|
33493
33875
|
};
|
|
33876
|
+
},
|
|
33877
|
+
async get(args) {
|
|
33878
|
+
const r = await sdk.session.get(args);
|
|
33879
|
+
return {
|
|
33880
|
+
...r.data !== void 0 && {
|
|
33881
|
+
data: {
|
|
33882
|
+
...r.data.id !== void 0 && { id: r.data.id },
|
|
33883
|
+
...r.data.parentID !== void 0 && { parentID: r.data.parentID }
|
|
33884
|
+
}
|
|
33885
|
+
},
|
|
33886
|
+
...r.error !== void 0 && { error: r.error }
|
|
33887
|
+
};
|
|
33494
33888
|
}
|
|
33495
33889
|
}
|
|
33496
33890
|
};
|