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.
- package/README.md +57 -14
- package/dist/cli.js +393 -199
- package/dist/index.js +572 -175
- 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);
|
|
@@ -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
|
|
19194
|
-
import { writeFile as
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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: ${
|
|
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
|
|
32339
|
-
import { dirname as
|
|
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 =
|
|
32347
|
-
const repoRoot =
|
|
32348
|
-
const distCli =
|
|
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 =
|
|
32351
|
-
const hooks =
|
|
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 =
|
|
32361
|
-
await
|
|
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 =
|
|
32367
|
-
await
|
|
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
|
|
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
|
-
|
|
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: ${
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
32943
|
-
|
|
32944
|
-
|
|
32945
|
-
|
|
32946
|
-
|
|
32947
|
-
|
|
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
|
-
|
|
32951
|
-
task.
|
|
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)
|
|
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)
|
|
32982
|
-
t.status
|
|
32983
|
-
|
|
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
|
-
|
|
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: `${
|
|
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 =
|
|
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:
|
|
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
|
|
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
|
};
|