tamperward 2.31.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +214 -94
- package/package.json +4 -2
package/dist/cli/index.js
CHANGED
|
@@ -12476,7 +12476,7 @@ function verdict(blocks2, kind, audit) {
|
|
|
12476
12476
|
});
|
|
12477
12477
|
}
|
|
12478
12478
|
const reason = formatDenial(blocks2);
|
|
12479
|
-
return { exitCode: 0, stdout: denyWire(reason, kind) };
|
|
12479
|
+
return { exitCode: 0, stdout: denyWire(reason, kind), findings: blocks2 };
|
|
12480
12480
|
}
|
|
12481
12481
|
function fileChanges(changes) {
|
|
12482
12482
|
return changes.filter((c) => c.kind === "file");
|
|
@@ -19275,6 +19275,9 @@ function taskEnv(task) {
|
|
|
19275
19275
|
...task.model !== void 0 ? { TAMPERWARD_RESEARCH_MODEL: task.model } : {}
|
|
19276
19276
|
};
|
|
19277
19277
|
}
|
|
19278
|
+
function substitutePlaceholders(template, task) {
|
|
19279
|
+
return template.replace(PLACEHOLDER_PATTERN, (token) => PLACEHOLDERS[token](task));
|
|
19280
|
+
}
|
|
19278
19281
|
function normalizeCommandArgv(argv, cwd = process.cwd()) {
|
|
19279
19282
|
if (argv.length === 0) {
|
|
19280
19283
|
throw new ResearchError('the command adapter needs an agent command after "--"');
|
|
@@ -19288,9 +19291,7 @@ function commandAdapter(argv, cwd = process.cwd()) {
|
|
|
19288
19291
|
name: "command",
|
|
19289
19292
|
layers: ["envelope"],
|
|
19290
19293
|
launch(task) {
|
|
19291
|
-
const substituted = anchored.map(
|
|
19292
|
-
(arg) => Object.entries(PLACEHOLDERS).reduce((acc2, [token, value]) => acc2.split(token).join(value(task)), arg)
|
|
19293
|
-
);
|
|
19294
|
+
const substituted = anchored.map((arg) => substitutePlaceholders(arg, task));
|
|
19294
19295
|
return { argv: substituted, env: taskEnv(task) };
|
|
19295
19296
|
}
|
|
19296
19297
|
};
|
|
@@ -19319,7 +19320,7 @@ function resolveAdapter(name, argv, model) {
|
|
|
19319
19320
|
if (name === "claude-code") return claudeCodeAdapter(model);
|
|
19320
19321
|
throw new ResearchError(`unknown adapter "${name}" (built-in adapters: ${BUILTIN_ADAPTERS.join(", ")})`);
|
|
19321
19322
|
}
|
|
19322
|
-
var ADAPTER_LAYERS, RESEARCH_ARMS, BUILTIN_ADAPTERS, ResearchError, PLACEHOLDERS;
|
|
19323
|
+
var ADAPTER_LAYERS, RESEARCH_ARMS, BUILTIN_ADAPTERS, ResearchError, PLACEHOLDERS, PLACEHOLDER_PATTERN;
|
|
19323
19324
|
var init_adapter = __esm({
|
|
19324
19325
|
"src/research/adapter.ts"() {
|
|
19325
19326
|
"use strict";
|
|
@@ -19337,6 +19338,10 @@ var init_adapter = __esm({
|
|
|
19337
19338
|
"{arm}": (t) => t.arm,
|
|
19338
19339
|
"{model}": (t) => t.model ?? ""
|
|
19339
19340
|
};
|
|
19341
|
+
PLACEHOLDER_PATTERN = new RegExp(
|
|
19342
|
+
Object.keys(PLACEHOLDERS).map((token) => token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"),
|
|
19343
|
+
"g"
|
|
19344
|
+
);
|
|
19340
19345
|
}
|
|
19341
19346
|
});
|
|
19342
19347
|
|
|
@@ -19710,8 +19715,10 @@ var init_record = __esm({
|
|
|
19710
19715
|
});
|
|
19711
19716
|
|
|
19712
19717
|
// src/research/run.ts
|
|
19718
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
19713
19719
|
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
19714
|
-
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync23, renameSync as renameSync4, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
|
|
19720
|
+
import { closeSync as closeSync3, existsSync as existsSync18, linkSync, mkdirSync as mkdirSync9, openSync as openSync3, readFileSync as readFileSync23, renameSync as renameSync4, rmSync as rmSync11, unlinkSync, writeFileSync as writeFileSync11, writeSync } from "node:fs";
|
|
19721
|
+
import { hostname } from "node:os";
|
|
19715
19722
|
import { join as join25, resolve as resolve15 } from "node:path";
|
|
19716
19723
|
function git6(args, cwd) {
|
|
19717
19724
|
return execFileSync13("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
@@ -19719,6 +19726,102 @@ function git6(args, cwd) {
|
|
|
19719
19726
|
function pairRecordPath(ledger, task, pair) {
|
|
19720
19727
|
return join25(ledger, "pairs", `${task}--${pair}.json`);
|
|
19721
19728
|
}
|
|
19729
|
+
function researchLockPath(ledger) {
|
|
19730
|
+
return join25(ledger, "run.lock");
|
|
19731
|
+
}
|
|
19732
|
+
function readLockOwner(path) {
|
|
19733
|
+
try {
|
|
19734
|
+
const parsed = JSON.parse(readFileSync23(path, "utf8"));
|
|
19735
|
+
if (!isRecord(parsed) || typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
|
|
19736
|
+
if (typeof parsed.host !== "string" || typeof parsed.started_at !== "string" || typeof parsed.token !== "string") return null;
|
|
19737
|
+
return { pid: parsed.pid, host: parsed.host, started_at: parsed.started_at, token: parsed.token };
|
|
19738
|
+
} catch {
|
|
19739
|
+
return null;
|
|
19740
|
+
}
|
|
19741
|
+
}
|
|
19742
|
+
function processAlive(pid) {
|
|
19743
|
+
try {
|
|
19744
|
+
process.kill(pid, 0);
|
|
19745
|
+
return true;
|
|
19746
|
+
} catch (e) {
|
|
19747
|
+
return e.code !== "ESRCH";
|
|
19748
|
+
}
|
|
19749
|
+
}
|
|
19750
|
+
function lockHolder(path) {
|
|
19751
|
+
const owner = readLockOwner(path);
|
|
19752
|
+
return owner ? `pid ${owner.pid} on ${owner.host}, started ${owner.started_at}` : "an unreadable lock file";
|
|
19753
|
+
}
|
|
19754
|
+
function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
19755
|
+
mkdirSync9(ledger, { recursive: true });
|
|
19756
|
+
const path = researchLockPath(ledger);
|
|
19757
|
+
if (breakStaleLock && existsSync18(path)) {
|
|
19758
|
+
const owner2 = readLockOwner(path);
|
|
19759
|
+
if (!owner2) {
|
|
19760
|
+
throw new ResearchError(`research output ${ledger} has an unreadable lock at ${path}; verify no run is active, then remove it deliberately`);
|
|
19761
|
+
}
|
|
19762
|
+
if (owner2.host !== hostname() || processAlive(owner2.pid)) {
|
|
19763
|
+
throw new ResearchError(
|
|
19764
|
+
`research output ${ledger} is still locked by ${lockHolder(path)}; refusing to break an active lock (a reused pid looks alive: if that owner is certainly gone, remove ${path} deliberately)`
|
|
19765
|
+
);
|
|
19766
|
+
}
|
|
19767
|
+
const claimed = `${path}.stale-${process.pid}-${randomUUID2()}`;
|
|
19768
|
+
let moved = true;
|
|
19769
|
+
try {
|
|
19770
|
+
renameSync4(path, claimed);
|
|
19771
|
+
} catch (e) {
|
|
19772
|
+
if (e.code !== "ENOENT") {
|
|
19773
|
+
throw new ResearchError(`research output ${ledger} stale lock recovery failed for ${path}`);
|
|
19774
|
+
}
|
|
19775
|
+
moved = false;
|
|
19776
|
+
}
|
|
19777
|
+
if (moved) {
|
|
19778
|
+
const inspected = readLockOwner(claimed);
|
|
19779
|
+
if (inspected && inspected.token === owner2.token) {
|
|
19780
|
+
unlinkSync(claimed);
|
|
19781
|
+
} else {
|
|
19782
|
+
try {
|
|
19783
|
+
linkSync(claimed, path);
|
|
19784
|
+
unlinkSync(claimed);
|
|
19785
|
+
} catch {
|
|
19786
|
+
}
|
|
19787
|
+
throw new ResearchError(`research output ${ledger}: the lock at ${path} changed while it was being broken; refusing (rerun --break-lock only once no run is active)`);
|
|
19788
|
+
}
|
|
19789
|
+
}
|
|
19790
|
+
}
|
|
19791
|
+
const owner = {
|
|
19792
|
+
pid: process.pid,
|
|
19793
|
+
host: hostname(),
|
|
19794
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19795
|
+
token: randomUUID2()
|
|
19796
|
+
};
|
|
19797
|
+
const text = JSON.stringify(owner) + "\n";
|
|
19798
|
+
let fd = -1;
|
|
19799
|
+
try {
|
|
19800
|
+
fd = openSync3(path, "wx", 384);
|
|
19801
|
+
writeSync(fd, text);
|
|
19802
|
+
closeSync3(fd);
|
|
19803
|
+
fd = -1;
|
|
19804
|
+
} catch (e) {
|
|
19805
|
+
if (fd !== -1) closeSync3(fd);
|
|
19806
|
+
if (e.code === "EEXIST") {
|
|
19807
|
+
throw new ResearchError(
|
|
19808
|
+
`research output ${ledger} is already locked by ${lockHolder(path)}; refusing concurrent writers. Verify the owner is not running, then remove ${path} deliberately or rerun with --break-lock`
|
|
19809
|
+
);
|
|
19810
|
+
}
|
|
19811
|
+
throw new ResearchError(`research output ${ledger} cannot create lock ${path}`);
|
|
19812
|
+
}
|
|
19813
|
+
let released = false;
|
|
19814
|
+
return {
|
|
19815
|
+
release() {
|
|
19816
|
+
if (released) return;
|
|
19817
|
+
released = true;
|
|
19818
|
+
try {
|
|
19819
|
+
if (readFileSync23(path, "utf8") === text) unlinkSync(path);
|
|
19820
|
+
} catch {
|
|
19821
|
+
}
|
|
19822
|
+
}
|
|
19823
|
+
};
|
|
19824
|
+
}
|
|
19722
19825
|
function resumableRecord(path, expected) {
|
|
19723
19826
|
let raw;
|
|
19724
19827
|
try {
|
|
@@ -20007,84 +20110,46 @@ function runResearch(opts) {
|
|
|
20007
20110
|
return 2;
|
|
20008
20111
|
}
|
|
20009
20112
|
const ledger = resolve15(opts.out);
|
|
20010
|
-
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20017
|
-
const path = pairRecordPath(ledger, task.id, pair);
|
|
20018
|
-
if (!existsSync18(path)) continue;
|
|
20019
|
-
const existing = resumableRecord(path, {
|
|
20020
|
-
task: task.id,
|
|
20021
|
-
pair,
|
|
20022
|
-
manifest_sha256: manifestSha,
|
|
20023
|
-
adapter: { name: adapter.name, layers: adapter.layers },
|
|
20024
|
-
model: opts.model ?? null,
|
|
20025
|
-
tamperward_version: TW_VERSION,
|
|
20026
|
-
agent_argv: agentArgvIdentity,
|
|
20027
|
-
agent_budget: opts.agentBudget ?? null,
|
|
20028
|
-
verify_command: task.verify.command,
|
|
20029
|
-
source_base: sourceBase
|
|
20030
|
-
});
|
|
20031
|
-
sourceBase ??= existing.arms.ungated.base;
|
|
20032
|
-
if (existing.arms.ungated.base !== sourceBase) {
|
|
20033
|
-
throw new ResearchError(
|
|
20034
|
-
`ledger record ${path} belongs to a different source commit (${existing.arms.ungated.base.slice(0, 12)}\u2026 != ${sourceBase.slice(0, 12)}\u2026); use a new --out, or remove it deliberately`
|
|
20035
|
-
);
|
|
20036
|
-
}
|
|
20037
|
-
existingRecords.set(pair, existing);
|
|
20038
|
-
}
|
|
20039
|
-
} catch (e) {
|
|
20040
|
-
if (e instanceof ResearchError) {
|
|
20041
|
-
err2(`tamperward research: ${e.message}`);
|
|
20042
|
-
return 2;
|
|
20043
|
-
}
|
|
20044
|
-
throw e;
|
|
20113
|
+
let lock;
|
|
20114
|
+
try {
|
|
20115
|
+
lock = acquireResearchLock(ledger, opts.breakLock === true);
|
|
20116
|
+
} catch (e) {
|
|
20117
|
+
if (e instanceof ResearchError) {
|
|
20118
|
+
err2(`tamperward research: ${e.message}`);
|
|
20119
|
+
return 2;
|
|
20045
20120
|
}
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
let
|
|
20121
|
+
throw e;
|
|
20122
|
+
}
|
|
20123
|
+
try {
|
|
20124
|
+
const pairs = opts.pairs ?? 1;
|
|
20125
|
+
mkdirSync9(join25(ledger, "pairs"), { recursive: true });
|
|
20126
|
+
for (const task of tasks) {
|
|
20127
|
+
const existingRecords = /* @__PURE__ */ new Map();
|
|
20128
|
+
let sourceBase = null;
|
|
20054
20129
|
try {
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
if (!
|
|
20058
|
-
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20130
|
+
for (let pair = 1; pair <= pairs; pair++) {
|
|
20131
|
+
const path = pairRecordPath(ledger, task.id, pair);
|
|
20132
|
+
if (!existsSync18(path)) continue;
|
|
20133
|
+
const existing = resumableRecord(path, {
|
|
20134
|
+
task: task.id,
|
|
20135
|
+
pair,
|
|
20136
|
+
manifest_sha256: manifestSha,
|
|
20137
|
+
adapter: { name: adapter.name, layers: adapter.layers },
|
|
20138
|
+
model: opts.model ?? null,
|
|
20139
|
+
tamperward_version: TW_VERSION,
|
|
20140
|
+
agent_argv: agentArgvIdentity,
|
|
20141
|
+
agent_budget: opts.agentBudget ?? null,
|
|
20142
|
+
verify_command: task.verify.command,
|
|
20143
|
+
source_base: sourceBase
|
|
20144
|
+
});
|
|
20145
|
+
sourceBase ??= existing.arms.ungated.base;
|
|
20146
|
+
if (existing.arms.ungated.base !== sourceBase) {
|
|
20147
|
+
throw new ResearchError(
|
|
20148
|
+
`ledger record ${path} belongs to a different source commit (${existing.arms.ungated.base.slice(0, 12)}\u2026 != ${sourceBase.slice(0, 12)}\u2026); use a new --out, or remove it deliberately`
|
|
20149
|
+
);
|
|
20068
20150
|
}
|
|
20151
|
+
existingRecords.set(pair, existing);
|
|
20069
20152
|
}
|
|
20070
|
-
const ungated = arms.ungated;
|
|
20071
|
-
const gated = arms.gated;
|
|
20072
|
-
if (!ungated || !gated) throw new ResearchError(`task "${task.id}" pair ${pair}: an arm produced no record`);
|
|
20073
|
-
record = {
|
|
20074
|
-
schema_version: MACHINE_SCHEMA_VERSION,
|
|
20075
|
-
command: "research",
|
|
20076
|
-
document: "pair",
|
|
20077
|
-
task: task.id,
|
|
20078
|
-
pair,
|
|
20079
|
-
adapter: { name: adapter.name, layers: [...adapter.layers] },
|
|
20080
|
-
model: opts.model ?? null,
|
|
20081
|
-
tamperward_version: TW_VERSION,
|
|
20082
|
-
agent_argv: [...agentArgvIdentity],
|
|
20083
|
-
agent_budget: opts.agentBudget ?? null,
|
|
20084
|
-
manifest_sha256: manifestSha,
|
|
20085
|
-
verify_command: task.verify.command,
|
|
20086
|
-
arms: { ungated, gated }
|
|
20087
|
-
};
|
|
20088
20153
|
} catch (e) {
|
|
20089
20154
|
if (e instanceof ResearchError) {
|
|
20090
20155
|
err2(`tamperward research: ${e.message}`);
|
|
@@ -20092,20 +20157,72 @@ function runResearch(opts) {
|
|
|
20092
20157
|
}
|
|
20093
20158
|
throw e;
|
|
20094
20159
|
}
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20160
|
+
for (let pair = 1; pair <= pairs; pair++) {
|
|
20161
|
+
const path = pairRecordPath(ledger, task.id, pair);
|
|
20162
|
+
const existing = existingRecords.get(pair);
|
|
20163
|
+
if (existing) {
|
|
20164
|
+
if (!opts.json) out2(`tamperward research \u2014 task ${task.id} pair ${pair}: already recorded (${path}); skipping`);
|
|
20165
|
+
continue;
|
|
20166
|
+
}
|
|
20167
|
+
let record;
|
|
20168
|
+
try {
|
|
20169
|
+
const arms = {};
|
|
20170
|
+
for (const arm of RESEARCH_ARMS) {
|
|
20171
|
+
if (!opts.json) out2(`tamperward research \u2014 task ${task.id} pair ${pair}: ${arm} arm`);
|
|
20172
|
+
arms[arm] = runTrajectory(ledger, task, pair, arm, adapter, opts, sourceBase ?? void 0);
|
|
20173
|
+
if (arm === "ungated") {
|
|
20174
|
+
const resolvedSource = arms[arm]?.base;
|
|
20175
|
+
if (!resolvedSource) throw new ResearchError(`task "${task.id}" pair ${pair}: ungated arm produced no source base`);
|
|
20176
|
+
if (sourceBase !== null && resolvedSource !== sourceBase) {
|
|
20177
|
+
throw new ResearchError(
|
|
20178
|
+
`task "${task.id}" pair ${pair}: source base moved (${resolvedSource.slice(0, 12)}\u2026 != ${sourceBase.slice(0, 12)}\u2026)`
|
|
20179
|
+
);
|
|
20180
|
+
}
|
|
20181
|
+
sourceBase ??= resolvedSource;
|
|
20182
|
+
}
|
|
20183
|
+
}
|
|
20184
|
+
const ungated = arms.ungated;
|
|
20185
|
+
const gated = arms.gated;
|
|
20186
|
+
if (!ungated || !gated) throw new ResearchError(`task "${task.id}" pair ${pair}: an arm produced no record`);
|
|
20187
|
+
record = {
|
|
20188
|
+
schema_version: MACHINE_SCHEMA_VERSION,
|
|
20189
|
+
command: "research",
|
|
20190
|
+
document: "pair",
|
|
20191
|
+
task: task.id,
|
|
20192
|
+
pair,
|
|
20193
|
+
adapter: { name: adapter.name, layers: [...adapter.layers] },
|
|
20194
|
+
model: opts.model ?? null,
|
|
20195
|
+
tamperward_version: TW_VERSION,
|
|
20196
|
+
agent_argv: [...agentArgvIdentity],
|
|
20197
|
+
agent_budget: opts.agentBudget ?? null,
|
|
20198
|
+
manifest_sha256: manifestSha,
|
|
20199
|
+
verify_command: task.verify.command,
|
|
20200
|
+
arms: { ungated, gated }
|
|
20201
|
+
};
|
|
20202
|
+
} catch (e) {
|
|
20203
|
+
if (e instanceof ResearchError) {
|
|
20204
|
+
err2(`tamperward research: ${e.message}`);
|
|
20205
|
+
return 2;
|
|
20206
|
+
}
|
|
20207
|
+
throw e;
|
|
20208
|
+
}
|
|
20209
|
+
const text = JSON.stringify(record);
|
|
20210
|
+
writeRecordAtomically(path, text + "\n");
|
|
20211
|
+
if (opts.json) {
|
|
20212
|
+
out2(text);
|
|
20213
|
+
} else {
|
|
20214
|
+
const g = record.arms.gated;
|
|
20215
|
+
const u = record.arms.ungated;
|
|
20216
|
+
out2(
|
|
20217
|
+
`tamperward research \u2014 task ${task.id} pair ${pair}: ungated ${u.outcome.verify_verdict} (masked=${u.outcome.masked_failure}, surviving=${u.outcome.surviving_protected_mutations}); gated ${g.outcome.verify_verdict} (masked=${g.outcome.masked_failure}, surviving=${g.outcome.surviving_protected_mutations}), tamperward ${g.treatment?.verdict ?? "n/a"} \u2192 ${g.treatment?.disposition ?? "n/a"}` + (u.measured && g.measured ? "" : `; UNMEASURABLE (${[u.unmeasurable, g.unmeasurable].filter(Boolean).join(" / ")})`) + `; recorded ${path}`
|
|
20218
|
+
);
|
|
20219
|
+
}
|
|
20105
20220
|
}
|
|
20106
20221
|
}
|
|
20222
|
+
return 0;
|
|
20223
|
+
} finally {
|
|
20224
|
+
lock.release();
|
|
20107
20225
|
}
|
|
20108
|
-
return 0;
|
|
20109
20226
|
}
|
|
20110
20227
|
var err2, out2;
|
|
20111
20228
|
var init_run2 = __esm({
|
|
@@ -20291,6 +20408,7 @@ function parseResearchRun(args) {
|
|
|
20291
20408
|
else if (a === "--pairs") o.pairs = Number(args[++i]);
|
|
20292
20409
|
else if (a === "--model") o.model = args[++i];
|
|
20293
20410
|
else if (a === "--agent-budget") o.agentBudget = Number(args[++i]);
|
|
20411
|
+
else if (a === "--break-lock") o.breakLock = true;
|
|
20294
20412
|
else if (a === "--json") o.json = true;
|
|
20295
20413
|
}
|
|
20296
20414
|
return o;
|
|
@@ -20603,7 +20721,7 @@ function validateCliArgs(cmd, args) {
|
|
|
20603
20721
|
const delimiter3 = rest.indexOf("--");
|
|
20604
20722
|
const prefix = delimiter3 < 0 ? rest : rest.slice(0, delimiter3);
|
|
20605
20723
|
const parsed = validateFlatArgs(prefix, {
|
|
20606
|
-
flags: ["--json"],
|
|
20724
|
+
flags: ["--break-lock", "--json"],
|
|
20607
20725
|
values: {
|
|
20608
20726
|
"--manifest": "string",
|
|
20609
20727
|
"--out": "string",
|
|
@@ -20702,9 +20820,11 @@ Formats:
|
|
|
20702
20820
|
tamperward research run --manifest F bring-your-own-model evaluation: for every
|
|
20703
20821
|
--out D --adapter A [--pairs N] task in the manifest, pin one source commit,
|
|
20704
20822
|
[--model M] [--agent-budget S] clone fresh state per arm, run the agent
|
|
20705
|
-
[--
|
|
20706
|
-
|
|
20823
|
+
[--break-lock] [--json] ungated and under the run envelope, then
|
|
20824
|
+
[-- <agent cmd...>] observe both with verify + check. Records
|
|
20707
20825
|
are resumable by full experiment identity.
|
|
20826
|
+
--break-lock recovers a verified stale
|
|
20827
|
+
output lock (see the research guide).
|
|
20708
20828
|
tamperward research summarize --ledger D aggregate measured pairs into model behaviour,
|
|
20709
20829
|
independent outcome, TamperWard hits/misses
|
|
20710
20830
|
and paired counts \u2014 no composite score
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tamperward",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.32.0",
|
|
4
4
|
"description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "hexrift",
|
|
@@ -48,7 +48,9 @@
|
|
|
48
48
|
"test:watch": "vitest",
|
|
49
49
|
"test:perf-smoke": "vitest run --config vitest.perf-smoke.config.ts --no-file-parallelism",
|
|
50
50
|
"check": "node dist/cli/index.js check",
|
|
51
|
-
"probe:codex-runtime": "node harness/adapters/codex-probe.mjs"
|
|
51
|
+
"probe:codex-runtime": "node harness/adapters/codex-probe.mjs",
|
|
52
|
+
"probe:copilot-runtime": "node harness/adapters/copilot-probe.mjs",
|
|
53
|
+
"spike:copilot-sdk": "node harness/adapters/copilot-sdk-spike.mjs"
|
|
52
54
|
},
|
|
53
55
|
"dependencies": {
|
|
54
56
|
"picomatch": "^4.0.2",
|