@brainervirus/workit-codex 8.13.0 → 8.13.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/.codex-plugin/plugin.json +1 -1
- package/agents/workit-reviewer.toml +2 -2
- package/agents/workit-verifier.toml +2 -2
- package/dist/launch-mcp.js +641 -401
- package/dist/{workit-hook-sc45d7h1.js → workit-hook-j1zd509p.js} +224 -33
- package/dist/workit-hook.js +1 -1
- package/package.json +4 -4
- package/skills/workit-fanout/references/orchestration.md +4 -3
- package/skills/workit-review/SKILL.md +1 -1
|
@@ -9789,7 +9789,7 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9789
9789
|
}
|
|
9790
9790
|
}
|
|
9791
9791
|
// packages/workit-core/src/host-session.ts
|
|
9792
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
9792
|
+
import { createHash as createHash4, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
9793
9793
|
import fs7 from "node:fs";
|
|
9794
9794
|
import path7 from "node:path";
|
|
9795
9795
|
|
|
@@ -9881,9 +9881,10 @@ function hostSessionFromEnv(env) {
|
|
|
9881
9881
|
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9882
9882
|
}
|
|
9883
9883
|
var ISSUED_PER_LEAD = 64;
|
|
9884
|
-
var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/
|
|
9884
|
+
var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3";
|
|
9885
9885
|
var hash = (value) => createHash4("sha256").update(value).digest("hex").slice(0, 32);
|
|
9886
9886
|
var userIssuedRoot = () => path7.join(resolveStateDir(), "issued");
|
|
9887
|
+
var userTokenRoot = () => path7.join(resolveStateDir(), "issued-tokens");
|
|
9887
9888
|
function migrateRepoIssued(cwd) {
|
|
9888
9889
|
if (cwd === undefined)
|
|
9889
9890
|
return;
|
|
@@ -9906,7 +9907,12 @@ function migrateRepoIssued(cwd) {
|
|
|
9906
9907
|
const to = path7.join(userIssuedRoot(), lead);
|
|
9907
9908
|
fs7.mkdirSync(to, { recursive: true, mode: 448 });
|
|
9908
9909
|
for (const id of fs7.readdirSync(from)) {
|
|
9909
|
-
|
|
9910
|
+
try {
|
|
9911
|
+
fs7.copyFileSync(path7.join(from, id), path7.join(to, id), fs7.constants.COPYFILE_EXCL);
|
|
9912
|
+
} catch (error) {
|
|
9913
|
+
if (error.code !== "EEXIST")
|
|
9914
|
+
throw error;
|
|
9915
|
+
}
|
|
9910
9916
|
fs7.rmSync(path7.join(from, id), { force: true });
|
|
9911
9917
|
}
|
|
9912
9918
|
removeIfEmpty(from);
|
|
@@ -9926,21 +9932,71 @@ var removeIfEmpty = (dir) => {
|
|
|
9926
9932
|
fs7.rmdirSync(dir);
|
|
9927
9933
|
} catch {}
|
|
9928
9934
|
};
|
|
9935
|
+
var tokenHash = (token) => createHash4("sha256").update(token).digest("hex");
|
|
9929
9936
|
function recordIssuedSession(cwd, lead, issued) {
|
|
9937
|
+
const token = randomBytes2(18).toString("base64url");
|
|
9930
9938
|
try {
|
|
9931
9939
|
migrateRepoIssued(cwd);
|
|
9940
|
+
const own = hash(issued);
|
|
9941
|
+
const tokens = path7.join(userTokenRoot(), hash(lead));
|
|
9942
|
+
fs7.mkdirSync(tokens, { recursive: true, mode: 448 });
|
|
9943
|
+
fs7.writeFileSync(path7.join(tokens, own), `${tokenHash(token)}
|
|
9944
|
+
`, { mode: 384, flag: "wx" });
|
|
9932
9945
|
const dir = path7.join(userIssuedRoot(), hash(lead));
|
|
9933
9946
|
fs7.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
9934
|
-
|
|
9935
|
-
|
|
9947
|
+
fs7.writeFileSync(path7.join(dir, own), `${ISSUED_SESSIONS_MARKER}
|
|
9948
|
+
`, { mode: 384 });
|
|
9949
|
+
pruneIssued(dir, own, tokens);
|
|
9950
|
+
return token;
|
|
9951
|
+
} catch {
|
|
9952
|
+
return null;
|
|
9953
|
+
}
|
|
9954
|
+
}
|
|
9955
|
+
function pruneIssued(dir, own, tokens) {
|
|
9956
|
+
try {
|
|
9936
9957
|
const entries = fs7.readdirSync(dir);
|
|
9937
9958
|
if (entries.length <= ISSUED_PER_LEAD)
|
|
9938
9959
|
return;
|
|
9939
9960
|
const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs7.statSync(path7.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
|
|
9940
|
-
for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
|
|
9961
|
+
for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD)) {
|
|
9941
9962
|
fs7.rmSync(path7.join(dir, name), { force: true });
|
|
9963
|
+
fs7.rmSync(path7.join(tokens, name), { force: true });
|
|
9964
|
+
}
|
|
9942
9965
|
} catch {}
|
|
9943
9966
|
}
|
|
9967
|
+
var issuedRecord = (cwd, claimed) => {
|
|
9968
|
+
const cut = claimed.lastIndexOf(":");
|
|
9969
|
+
if (cut <= 0)
|
|
9970
|
+
return null;
|
|
9971
|
+
migrateRepoIssued(cwd);
|
|
9972
|
+
try {
|
|
9973
|
+
return fs7.readFileSync(path7.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
|
|
9974
|
+
} catch {
|
|
9975
|
+
return null;
|
|
9976
|
+
}
|
|
9977
|
+
};
|
|
9978
|
+
var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
|
|
9979
|
+
var issuedToken = (claimed) => {
|
|
9980
|
+
const cut = claimed.lastIndexOf(":");
|
|
9981
|
+
if (cut <= 0)
|
|
9982
|
+
return { stored: null, leadBound: false };
|
|
9983
|
+
const dir = path7.join(userTokenRoot(), hash(claimed.slice(0, cut)));
|
|
9984
|
+
try {
|
|
9985
|
+
const text = fs7.readFileSync(path7.join(dir, hash(claimed)), "utf8");
|
|
9986
|
+
return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
|
|
9987
|
+
} catch {
|
|
9988
|
+
return { stored: null, leadBound: safeList(dir).length > 0 };
|
|
9989
|
+
}
|
|
9990
|
+
};
|
|
9991
|
+
function issuedBinding(cwd, claimed) {
|
|
9992
|
+
const record = issuedRecord(cwd, claimed);
|
|
9993
|
+
const { stored, leadBound } = issuedToken(claimed);
|
|
9994
|
+
if (stored)
|
|
9995
|
+
return "token";
|
|
9996
|
+
if (record === null)
|
|
9997
|
+
return null;
|
|
9998
|
+
return leadBound ? "token" : "legacy";
|
|
9999
|
+
}
|
|
9944
10000
|
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
9945
10001
|
|
|
9946
10002
|
// packages/workit-core/src/core/package-root.ts
|
|
@@ -9971,8 +10027,10 @@ var metadataLockSchema = object({
|
|
|
9971
10027
|
var FOREIGN_LOCK_TTL_MS = 10 * 60000;
|
|
9972
10028
|
var UNREADABLE_LOCK_TTL_MS = 30000;
|
|
9973
10029
|
var RECLAIM_GUARD_TTL_MS = 30000;
|
|
9974
|
-
var
|
|
10030
|
+
var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
|
|
10031
|
+
var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
9975
10032
|
var defaultLockTimeout = () => defaultLockTimeoutMs;
|
|
10033
|
+
var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
9976
10034
|
var parseMetadataLock = (raw) => {
|
|
9977
10035
|
let value;
|
|
9978
10036
|
try {
|
|
@@ -12245,7 +12303,7 @@ import path15 from "node:path";
|
|
|
12245
12303
|
|
|
12246
12304
|
// packages/workit-core/src/ledger.ts
|
|
12247
12305
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12248
|
-
import { createHash as createHash9, randomBytes as
|
|
12306
|
+
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
12249
12307
|
import fs12 from "node:fs";
|
|
12250
12308
|
import path14 from "node:path";
|
|
12251
12309
|
|
|
@@ -13636,7 +13694,7 @@ function ledgerPath(cwd) {
|
|
|
13636
13694
|
const root = storeRoot(cwd);
|
|
13637
13695
|
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
13638
13696
|
}
|
|
13639
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
13697
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
13640
13698
|
var NETWORK_FS_TYPES = new Set([
|
|
13641
13699
|
26985,
|
|
13642
13700
|
20859,
|
|
@@ -13686,14 +13744,14 @@ var ledgerLock = {
|
|
|
13686
13744
|
return null;
|
|
13687
13745
|
throw error;
|
|
13688
13746
|
}
|
|
13689
|
-
const token = `${process.pid}-${
|
|
13747
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
13690
13748
|
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
13691
13749
|
return token;
|
|
13692
13750
|
},
|
|
13693
13751
|
release(lock, token) {
|
|
13694
13752
|
if (lockToken(lock) !== token)
|
|
13695
13753
|
return false;
|
|
13696
|
-
const tomb = `${lock}.release-${
|
|
13754
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
13697
13755
|
try {
|
|
13698
13756
|
fs12.renameSync(lock, tomb);
|
|
13699
13757
|
} catch {
|
|
@@ -13710,7 +13768,7 @@ var ledgerLock = {
|
|
|
13710
13768
|
},
|
|
13711
13769
|
token: lockToken,
|
|
13712
13770
|
reap(lock, observed) {
|
|
13713
|
-
const tomb = `${lock}.reap-${
|
|
13771
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
13714
13772
|
try {
|
|
13715
13773
|
fs12.renameSync(lock, tomb);
|
|
13716
13774
|
} catch {
|
|
@@ -13728,7 +13786,7 @@ var ledgerLock = {
|
|
|
13728
13786
|
return false;
|
|
13729
13787
|
}
|
|
13730
13788
|
};
|
|
13731
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
13789
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
13732
13790
|
const lock = `${file}.lock`;
|
|
13733
13791
|
const deadline = Date.now() + waitMs;
|
|
13734
13792
|
let token = null;
|
|
@@ -13786,10 +13844,18 @@ var lastLine = (fd, size) => {
|
|
|
13786
13844
|
}
|
|
13787
13845
|
return null;
|
|
13788
13846
|
};
|
|
13847
|
+
var tighten = (fd) => {
|
|
13848
|
+
try {
|
|
13849
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
13850
|
+
fs12.fchmodSync(fd, 384);
|
|
13851
|
+
} catch {}
|
|
13852
|
+
};
|
|
13789
13853
|
function appendLine(file, build, options) {
|
|
13790
13854
|
const write = () => {
|
|
13791
|
-
const fd = fs12.openSync(file, "a+");
|
|
13855
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
13792
13856
|
try {
|
|
13857
|
+
if (process.platform !== "win32")
|
|
13858
|
+
tighten(fd);
|
|
13793
13859
|
const size = fs12.fstatSync(fd).size;
|
|
13794
13860
|
let fence = "";
|
|
13795
13861
|
if (size > 0) {
|
|
@@ -13884,7 +13950,9 @@ function readLedger(cwd, options = {}) {
|
|
|
13884
13950
|
if (!file.ok)
|
|
13885
13951
|
return file;
|
|
13886
13952
|
let text = "";
|
|
13953
|
+
let stat = null;
|
|
13887
13954
|
try {
|
|
13955
|
+
stat = fs12.statSync(file.value);
|
|
13888
13956
|
text = fs12.readFileSync(file.value, "utf8");
|
|
13889
13957
|
} catch (error) {
|
|
13890
13958
|
if (error.code === "ENOENT")
|
|
@@ -13904,6 +13972,7 @@ function readLedger(cwd, options = {}) {
|
|
|
13904
13972
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
13905
13973
|
const earlier = new Set([null]);
|
|
13906
13974
|
let chainStarted = false;
|
|
13975
|
+
const ids = new Set;
|
|
13907
13976
|
for (const raw of text.split(`
|
|
13908
13977
|
`)) {
|
|
13909
13978
|
const trimmed = raw.trim();
|
|
@@ -13937,6 +14006,10 @@ function readLedger(cwd, options = {}) {
|
|
|
13937
14006
|
reason = "unsigned";
|
|
13938
14007
|
else
|
|
13939
14008
|
rowIntegrity = "legacy";
|
|
14009
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
14010
|
+
reason = "duplicate_id";
|
|
14011
|
+
if (options.integrity)
|
|
14012
|
+
ids.add(parsed.id);
|
|
13940
14013
|
if (reason)
|
|
13941
14014
|
rowIntegrity = "unverified";
|
|
13942
14015
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -13960,7 +14033,8 @@ function readLedger(cwd, options = {}) {
|
|
|
13960
14033
|
seq: rows.length + 1,
|
|
13961
14034
|
superseded: false,
|
|
13962
14035
|
supersedeIgnored: false,
|
|
13963
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
14036
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
14037
|
+
...reason ? { integrityReason: reason } : {}
|
|
13964
14038
|
};
|
|
13965
14039
|
rows.push(row);
|
|
13966
14040
|
if (!options.integrity)
|
|
@@ -13988,18 +14062,48 @@ function readLedger(cwd, options = {}) {
|
|
|
13988
14062
|
const byId = new Map;
|
|
13989
14063
|
for (const row of rows) {
|
|
13990
14064
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
13991
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
14065
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
13992
14066
|
target.superseded = true;
|
|
13993
14067
|
else if (row.supersedes)
|
|
13994
14068
|
row.supersedeIgnored = true;
|
|
13995
14069
|
if (!byId.has(row.id))
|
|
13996
14070
|
byId.set(row.id, row);
|
|
13997
14071
|
}
|
|
14072
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
14073
|
+
tamperCache.set(file.value, {
|
|
14074
|
+
size: stat.size,
|
|
14075
|
+
stamp: fileStamp(stat),
|
|
14076
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
14077
|
+
});
|
|
13998
14078
|
return {
|
|
13999
14079
|
ok: true,
|
|
14000
14080
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
14001
14081
|
};
|
|
14002
14082
|
}
|
|
14083
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
14084
|
+
var tamperCache = new Map;
|
|
14085
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
14086
|
+
function ledgerTampering(cwd) {
|
|
14087
|
+
const file = ledgerPath(cwd);
|
|
14088
|
+
if (!file.ok)
|
|
14089
|
+
return [];
|
|
14090
|
+
try {
|
|
14091
|
+
const stat = fs12.statSync(file.value);
|
|
14092
|
+
const cached = tamperCache.get(file.value);
|
|
14093
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
14094
|
+
return cached.breaks;
|
|
14095
|
+
} catch {
|
|
14096
|
+
return [];
|
|
14097
|
+
}
|
|
14098
|
+
const read = readLedger(cwd, { integrity: true });
|
|
14099
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
14100
|
+
}
|
|
14101
|
+
function tamperedUnblock(cwd) {
|
|
14102
|
+
const file = ledgerPath(cwd);
|
|
14103
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
14104
|
+
return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
|
|
14105
|
+
}
|
|
14106
|
+
var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
|
|
14003
14107
|
function verdictBasis(verdict, current) {
|
|
14004
14108
|
if (verdict.head && verdict.head === current.head)
|
|
14005
14109
|
return "fresh";
|
|
@@ -14007,7 +14111,7 @@ function verdictBasis(verdict, current) {
|
|
|
14007
14111
|
return "carried";
|
|
14008
14112
|
return "stale";
|
|
14009
14113
|
}
|
|
14010
|
-
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
14114
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
14011
14115
|
function trailerSessions(cwd, base, head) {
|
|
14012
14116
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
14013
14117
|
return [];
|
|
@@ -14088,6 +14192,56 @@ var authoredBy = (authors, session) => {
|
|
|
14088
14192
|
return true;
|
|
14089
14193
|
return false;
|
|
14090
14194
|
};
|
|
14195
|
+
var TRUST_REASONS = new Set([
|
|
14196
|
+
"unsigned",
|
|
14197
|
+
"hash_mismatch",
|
|
14198
|
+
"broken_link",
|
|
14199
|
+
"duplicate_id",
|
|
14200
|
+
"unissued_session",
|
|
14201
|
+
"unbound_session"
|
|
14202
|
+
]);
|
|
14203
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
14204
|
+
"cli",
|
|
14205
|
+
"opencode",
|
|
14206
|
+
"pi",
|
|
14207
|
+
"codex_cli",
|
|
14208
|
+
"codex_desktop"
|
|
14209
|
+
]);
|
|
14210
|
+
function sessionTrust(cwd, row) {
|
|
14211
|
+
const session = row.actor.session;
|
|
14212
|
+
if (!session || row.self === true)
|
|
14213
|
+
return null;
|
|
14214
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
14215
|
+
const host = row.actor.host;
|
|
14216
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
14217
|
+
if (!issuedOnly)
|
|
14218
|
+
return null;
|
|
14219
|
+
const binding = issuedBinding(cwd, session);
|
|
14220
|
+
if (binding === null)
|
|
14221
|
+
return "unissued_session";
|
|
14222
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
14223
|
+
}
|
|
14224
|
+
function trustReasons(cwd, row) {
|
|
14225
|
+
if (row.integrity === "unverified")
|
|
14226
|
+
return [row.integrityReason ?? "unsigned"];
|
|
14227
|
+
if (row.integrity !== "verified")
|
|
14228
|
+
return ["unsigned"];
|
|
14229
|
+
const session = sessionTrust(cwd, row);
|
|
14230
|
+
return session ? [session] : [];
|
|
14231
|
+
}
|
|
14232
|
+
function withIntegrity(cwd, rows) {
|
|
14233
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
14234
|
+
return rows;
|
|
14235
|
+
const read = readLedger(cwd, { integrity: true });
|
|
14236
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
14237
|
+
return rows.map((row) => {
|
|
14238
|
+
if (row.integrity !== undefined)
|
|
14239
|
+
return row;
|
|
14240
|
+
const fresh = bySeq.get(row.seq);
|
|
14241
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
14242
|
+
});
|
|
14243
|
+
}
|
|
14244
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
14091
14245
|
var independenceReasons = (row, authors) => {
|
|
14092
14246
|
const reasons = [];
|
|
14093
14247
|
if (!row.actor.session)
|
|
@@ -14098,15 +14252,17 @@ var independenceReasons = (row, authors) => {
|
|
|
14098
14252
|
reasons.push("self");
|
|
14099
14253
|
return reasons;
|
|
14100
14254
|
};
|
|
14101
|
-
function effectiveOf(rows,
|
|
14255
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
14102
14256
|
let effective = null;
|
|
14103
14257
|
for (const row of rows) {
|
|
14104
14258
|
if (!effective) {
|
|
14105
14259
|
effective = row;
|
|
14106
14260
|
continue;
|
|
14107
14261
|
}
|
|
14108
|
-
|
|
14109
|
-
|
|
14262
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
14263
|
+
continue;
|
|
14264
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
14265
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
14110
14266
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
14111
14267
|
continue;
|
|
14112
14268
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -14120,7 +14276,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
14120
14276
|
return false;
|
|
14121
14277
|
const edges = new Map;
|
|
14122
14278
|
for (const row of rows)
|
|
14123
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
14279
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
14124
14280
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
14125
14281
|
const seen = new Set([from]);
|
|
14126
14282
|
const queue = [from];
|
|
@@ -14136,7 +14292,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
14136
14292
|
}
|
|
14137
14293
|
return false;
|
|
14138
14294
|
}
|
|
14139
|
-
function checkVerdicts(cwd, branch,
|
|
14295
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
14296
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
14140
14297
|
const head = branchHead(cwd, branch);
|
|
14141
14298
|
const fallbackBase = defaultBase(cwd);
|
|
14142
14299
|
const scope = branchScope(cwd, branch);
|
|
@@ -14166,21 +14323,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
14166
14323
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
14167
14324
|
};
|
|
14168
14325
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
14326
|
+
const failureAuthorCache = new Map;
|
|
14327
|
+
const failureAuthors = (row) => {
|
|
14328
|
+
let set = failureAuthorCache.get(row.seq);
|
|
14329
|
+
if (!set) {
|
|
14330
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
14331
|
+
failureAuthorCache.set(row.seq, set);
|
|
14332
|
+
}
|
|
14333
|
+
return set;
|
|
14334
|
+
};
|
|
14335
|
+
const reasonCache = new Map;
|
|
14336
|
+
const reasonsOf = (row) => {
|
|
14337
|
+
let reasons = reasonCache.get(row);
|
|
14338
|
+
if (!reasons) {
|
|
14339
|
+
const failing = FAILING.has(String(row.result));
|
|
14340
|
+
reasons = [
|
|
14341
|
+
...trustReasons(cwd, row),
|
|
14342
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
14343
|
+
];
|
|
14344
|
+
reasonCache.set(row, reasons);
|
|
14345
|
+
}
|
|
14346
|
+
return reasons;
|
|
14347
|
+
};
|
|
14348
|
+
const retired = new Set;
|
|
14349
|
+
for (const row of rows)
|
|
14350
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
14351
|
+
retired.add(row.supersedes);
|
|
14169
14352
|
const byKind = new Map;
|
|
14170
14353
|
for (const row of rows)
|
|
14171
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
14354
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
14172
14355
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
14173
14356
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
14174
14357
|
}
|
|
14175
14358
|
const verdicts = [];
|
|
14176
14359
|
const warnings = [];
|
|
14177
14360
|
for (const [kind, list] of byKind) {
|
|
14178
|
-
const row = effectiveOf(list,
|
|
14361
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
14179
14362
|
if (!row)
|
|
14180
14363
|
continue;
|
|
14181
14364
|
const basis = basisOf(row);
|
|
14182
14365
|
const current = basis !== "stale";
|
|
14183
|
-
const independence =
|
|
14366
|
+
const independence = reasonsOf(row);
|
|
14184
14367
|
const reasons = [
|
|
14185
14368
|
...current ? [] : ["stale"],
|
|
14186
14369
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -14197,23 +14380,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
14197
14380
|
});
|
|
14198
14381
|
if (reasons.length === 0) {
|
|
14199
14382
|
for (const later of list)
|
|
14200
|
-
if (later.seq > row.seq &&
|
|
14383
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
14201
14384
|
warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
|
|
14202
14385
|
}
|
|
14203
14386
|
}
|
|
14204
14387
|
for (const row of rows)
|
|
14205
14388
|
if (row.integrity === "unverified" && scope(row))
|
|
14206
14389
|
warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
|
|
14390
|
+
const tampered = ledgerTampering(cwd);
|
|
14391
|
+
if (tampered.length) {
|
|
14392
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
14393
|
+
for (const entry of verdicts) {
|
|
14394
|
+
entry.accepted = false;
|
|
14395
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
14396
|
+
}
|
|
14397
|
+
}
|
|
14207
14398
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
14208
14399
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
14209
14400
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
14210
14401
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
14211
|
-
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
14402
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
14212
14403
|
accepted: false,
|
|
14213
14404
|
verdict: failing?.verdict ?? null,
|
|
14214
14405
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
14215
14406
|
};
|
|
14216
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
14407
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
14217
14408
|
return {
|
|
14218
14409
|
branch,
|
|
14219
14410
|
head,
|
|
@@ -20313,10 +20504,10 @@ function stopObligation(input, descriptor) {
|
|
|
20313
20504
|
const branch = currentBranch(input.cwd);
|
|
20314
20505
|
if (!branch || guardedBranch(input.cwd, branch))
|
|
20315
20506
|
return null;
|
|
20316
|
-
const ledger = readLedger(input.cwd);
|
|
20507
|
+
const ledger = readLedger(input.cwd, { integrity: true });
|
|
20317
20508
|
if (!ledger.ok)
|
|
20318
20509
|
return null;
|
|
20319
|
-
const all = ledger.value.rows;
|
|
20510
|
+
const all = ledger.value.rows.filter((row) => row.integrity !== "unverified");
|
|
20320
20511
|
const rows = all.filter((row) => row.branch === branch);
|
|
20321
20512
|
const mine = rows.filter((row) => sessionOf(row) === session);
|
|
20322
20513
|
const commits = new Set(mine.filter((row) => row.type === "commit.recorded").map((row) => typeof row.sha === "string" ? row.sha : row.head).filter((sha) => typeof sha === "string"));
|
|
@@ -20692,9 +20883,9 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
|
20692
20883
|
if (role !== "judge" || !event.agentId)
|
|
20693
20884
|
return readOnly;
|
|
20694
20885
|
const own = subagentSession(session, event.agentId);
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result>
|
|
20886
|
+
const token = session ? recordIssuedSession(input.cwd || process.cwd(), session, own) : null;
|
|
20887
|
+
const flags = token ? `--session ${own} --token ${token}` : `--session ${own}`;
|
|
20888
|
+
return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result> ${flags} ...\` so the ledger tells it apart from the author.${token ? " The token is this subagent's alone: never write it into a report, a file or a commit. --token needs a workit CLI at least as new as this plugin; if the CLI answers that --token is an unknown option, update the workit CLI (workit upgrade --cli) and record again." : ""}`;
|
|
20698
20889
|
};
|
|
20699
20890
|
function handleHook(input, deps) {
|
|
20700
20891
|
const { descriptor } = deps;
|
package/dist/workit-hook.js
CHANGED
|
@@ -37,6 +37,6 @@ if (quiet)
|
|
|
37
37
|
process.stdout.write(`${JSON.stringify({ hookSpecificOutput: { hookEventName: quiet } })}
|
|
38
38
|
`);
|
|
39
39
|
else {
|
|
40
|
-
const { runCodexHook } = await import("./workit-hook-
|
|
40
|
+
const { runCodexHook } = await import("./workit-hook-j1zd509p.js");
|
|
41
41
|
await runCodexHook([payload]);
|
|
42
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-codex",
|
|
3
|
-
"version": "8.13.
|
|
3
|
+
"version": "8.13.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Codex CLI/desktop plugin — shared MCP transport, hooks, method skills, and verifier/reviewer/implementer agents",
|
|
6
6
|
"keywords": [
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "bun scripts/build.ts"
|
|
40
40
|
},
|
|
41
|
-
"
|
|
42
|
-
"@brainervirus/workit-core": "^8.13.
|
|
43
|
-
"@brainervirus/workit-mcp": "^8.13.
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@brainervirus/workit-core": "^8.13.1",
|
|
43
|
+
"@brainervirus/workit-mcp": "^8.13.1"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=24"
|
|
@@ -72,11 +72,12 @@ Which session id a verdict carries, per host:
|
|
|
72
72
|
|
|
73
73
|
- Claude Code: subagents inherit your `WORKIT_SESSION_ID`, so the
|
|
74
74
|
SubagentStart hook names each `verifier` and `reviewer` its own session
|
|
75
|
-
(`<lead>:<agent id>`)
|
|
76
|
-
|
|
75
|
+
(`<lead>:<agent id>`) and a token only that subagent sees, passed exactly as
|
|
76
|
+
`--session` and `--token`: the hook records the ids it issues, and only
|
|
77
|
+
those, with their token, are accepted. An `implementer` is issued
|
|
77
78
|
none: it keeps your id in its env for its workit commands and commits.
|
|
78
79
|
- Codex: the `workit-verifier` and `workit-reviewer` agents get the same
|
|
79
|
-
issued `--session` from SubagentStart; any other verifier passes
|
|
80
|
+
issued `--session` and `--token` from SubagentStart; any other verifier passes
|
|
80
81
|
`--as verifier`.
|
|
81
82
|
- OpenCode: a child session's shell already has
|
|
82
83
|
`WORKIT_SESSION_ID=<lead>:<child>`; Pi: a supervised worker's shell has
|
|
@@ -8,7 +8,7 @@ description: Independent review of a diff, branch or PR - intent fidelity and st
|
|
|
8
8
|
Review the candidate, never the author's summary of it. A session that wrote a
|
|
9
9
|
commit on the branch cannot record a passing verdict: if that is you, hand the
|
|
10
10
|
review to a fresh agent with its own host-derived session: Claude Code and
|
|
11
|
-
Codex, the `reviewer` or `verifier` agent (`--session` from SubagentStart);
|
|
11
|
+
Codex, the `reviewer` or `verifier` agent (`--session` and `--token` from SubagentStart);
|
|
12
12
|
OpenCode, a child session; Pi, a supervised worker. Never hand-mint one such
|
|
13
13
|
as `<lead>-v1`: it is refused as `forged_session`.
|
|
14
14
|
An author's `--self` verdict reads self-reviewed and never counts as
|