tamperward 2.28.0 → 2.29.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 CHANGED
@@ -133,30 +133,7 @@ surfaces, see [the full architecture diagram](./docs/architecture.md).
133
133
 
134
134
  ### The local enforcement envelope
135
135
 
136
- ```mermaid
137
- graph TD
138
- entry["Trusted entry snapshot"]
139
- hooks["In-loop hooks and observer"]
140
- agent["Agent runtime"]
141
- tree["Candidate commit and worktree"]
142
- adjudicate["Post-exit adjudication"]
143
- checks["Diff and worktree checks"]
144
- verify["Visible and pristine verification"]
145
- integrity["Ancestry dependency and quiescence"]
146
- verdict["Final exit verdict"]
147
-
148
- entry --> agent
149
- hooks --> agent
150
- agent --> tree
151
- entry --> adjudicate
152
- tree --> adjudicate
153
- adjudicate --> checks
154
- adjudicate --> verify
155
- adjudicate --> integrity
156
- checks --> verdict
157
- verify --> verdict
158
- integrity --> verdict
159
- ```
136
+ ![TamperWard local enforcement envelope — trusted entry state and in-loop steering feed the agent lifecycle; post-exit adjudication fans out through change checks, visible/pristine verification, and ancestry/dependency/quiescence checks before one final verdict](./docs/local-enforcement-envelope.svg)
160
137
 
161
138
  Before starting the agent, the envelope
162
139
 
package/dist/cli/index.js CHANGED
@@ -17956,11 +17956,88 @@ var init_trace_verify = __esm({
17956
17956
  }
17957
17957
  });
17958
17958
 
17959
+ // src/runtimes.ts
17960
+ import { existsSync as existsSync17 } from "node:fs";
17961
+ import { join as join23 } from "node:path";
17962
+ function detectRuntimes(cwd) {
17963
+ const found = [];
17964
+ for (const rt of KNOWN_RUNTIMES) {
17965
+ const matchedMarker = rt.markers.find((m) => {
17966
+ try {
17967
+ return existsSync17(join23(cwd, m));
17968
+ } catch {
17969
+ return false;
17970
+ }
17971
+ });
17972
+ if (matchedMarker !== void 0) found.push({ ...rt, matchedMarker });
17973
+ }
17974
+ return found;
17975
+ }
17976
+ function hasInLoopRuntime(detected) {
17977
+ return detected.some((r) => r.steering === "in-loop");
17978
+ }
17979
+ function detectionHeadline(detected) {
17980
+ if (detected.length === 0) {
17981
+ return "No agent runtime detected. Wiring Claude Code in-loop steering plus the agent-neutral layers (pre-commit + CI) that protect any agent or human.";
17982
+ }
17983
+ if (detected.length === 1) {
17984
+ const r = detected[0];
17985
+ return `Detected ${r.label} \u2014 ${r.note}`;
17986
+ }
17987
+ const parts = detected.map((r) => `${r.label} (${r.steering === "in-loop" ? "in-loop" : "neutral layers"})`);
17988
+ return `Detected ${detected.length} agent runtimes: ${parts.join(", ")}.`;
17989
+ }
17990
+ function neutralOnlyCaveat(detected) {
17991
+ if (detected.length === 0 || hasInLoopRuntime(detected)) return null;
17992
+ const names = detected.map((r) => r.label).join(", ");
17993
+ const tracking = [...new Set(detected.map((r) => r.adapterTracking).filter(Boolean))].join(", ");
17994
+ return `In-loop steering (deny-before-execute) ships for Claude Code only today, so on ${names} your live protection is the agent-neutral layers: pre-commit and CI. The Claude hook wiring is still written and is inert unless Claude Code runs in this repository.${tracking ? ` A native in-loop adapter is tracked in ${tracking}.` : ""}`;
17995
+ }
17996
+ var KNOWN_RUNTIMES;
17997
+ var init_runtimes = __esm({
17998
+ "src/runtimes.ts"() {
17999
+ "use strict";
18000
+ KNOWN_RUNTIMES = [
18001
+ {
18002
+ id: "claude-code",
18003
+ label: "Claude Code",
18004
+ markers: [".claude/settings.json", ".claude"],
18005
+ steering: "in-loop",
18006
+ note: "native in-loop steering (PreToolUse deny + Stop sweep) plus the agent-neutral layers."
18007
+ },
18008
+ {
18009
+ id: "cursor",
18010
+ label: "Cursor",
18011
+ markers: [".cursor", ".cursorrules"],
18012
+ steering: "neutral",
18013
+ adapterTracking: "#482",
18014
+ note: "agent-neutral layers (pre-commit + CI) today; a native in-loop adapter is tracked in #482."
18015
+ },
18016
+ {
18017
+ id: "copilot",
18018
+ label: "GitHub Copilot",
18019
+ markers: [".github/copilot-instructions.md", ".github/copilot"],
18020
+ steering: "neutral",
18021
+ adapterTracking: "#482",
18022
+ note: "agent-neutral layers (pre-commit + CI) today; a native in-loop adapter is tracked in #482."
18023
+ },
18024
+ {
18025
+ id: "codex",
18026
+ label: "an AGENTS.md-aware agent (e.g. Codex)",
18027
+ markers: [".codex", "AGENTS.md"],
18028
+ steering: "neutral",
18029
+ adapterTracking: "#482",
18030
+ note: "agent-neutral layers (pre-commit + CI) today; a native in-loop adapter is tracked in #482."
18031
+ }
18032
+ ];
18033
+ }
18034
+ });
18035
+
17959
18036
  // src/cli/onboard.ts
17960
18037
  import { execFileSync as execFileSync12 } from "node:child_process";
17961
18038
  import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync21, realpathSync as realpathSync11, rmSync as rmSync10 } from "node:fs";
17962
18039
  import { tmpdir as tmpdir7 } from "node:os";
17963
- import { join as join23, relative as relative7, resolve as resolve12, sep as sep5 } from "node:path";
18040
+ import { join as join24, relative as relative7, resolve as resolve12, sep as sep5 } from "node:path";
17964
18041
  import { createInterface } from "node:readline";
17965
18042
  import { parseDocument } from "yaml";
17966
18043
  function readlineAsker(input, output) {
@@ -18028,7 +18105,7 @@ function explainVerdict(v, code2) {
18028
18105
  }
18029
18106
  }
18030
18107
  function writeVerifyCommand(cwd, command) {
18031
- const path = join23(cwd, POLICY_FILE);
18108
+ const path = join24(cwd, POLICY_FILE);
18032
18109
  const kind = writeTargetKind(path);
18033
18110
  if (kind === "symlink" || kind === "irregular") {
18034
18111
  return `${POLICY_FILE} is not a regular file; refusing to follow or replace a symlink or special file`;
@@ -18182,11 +18259,15 @@ async function runOnboard(opts, io = {}) {
18182
18259
  }
18183
18260
  }
18184
18261
  section(2);
18262
+ const detected = detectRuntimes(cwd);
18263
+ status("RUNTIME", detectionHeadline(detected), "info");
18264
+ const caveat = neutralOnlyCaveat(detected);
18265
+ if (caveat) status("NOTE", caveat, "warn");
18185
18266
  const plan = planInit(cwd);
18186
18267
  const names = {
18187
18268
  policy: "Policy",
18188
18269
  gitignore: "Git ignore",
18189
- agent: "Claude hooks",
18270
+ agent: "In-loop (Claude)",
18190
18271
  "pre-commit": "Pre-commit",
18191
18272
  ci: "CI workflow",
18192
18273
  codeowners: "CODEOWNERS"
@@ -18423,13 +18504,13 @@ function runDemo(cwd, head, runners, out3) {
18423
18504
  }
18424
18505
  const before = treeFingerprint(cwd);
18425
18506
  out3(`working tree fingerprint before: ${before.slice(0, 16)}`);
18426
- const wt = mkdtempSync6(join23(tmpdir7(), "tw-onboard-demo-"));
18507
+ const wt = mkdtempSync6(join24(tmpdir7(), "tw-onboard-demo-"));
18427
18508
  try {
18428
18509
  if (git5(cwd, ["worktree", "add", "--detach", wt, head]) === null) {
18429
18510
  out3("Demo skipped: could not create a temporary worktree (git worktree add failed).");
18430
18511
  return;
18431
18512
  }
18432
- const targetPath = join23(wt, target.path);
18513
+ const targetPath = join24(wt, target.path);
18433
18514
  const targetStat = lstatSync10(targetPath, { throwIfNoEntry: false });
18434
18515
  if (!targetStat || !targetStat.isFile() || targetStat.isSymbolicLink()) {
18435
18516
  out3("Demo skipped: the selected test is not a regular file in the disposable worktree.");
@@ -18484,6 +18565,7 @@ var init_onboard = __esm({
18484
18565
  init_policy();
18485
18566
  init_policy_load();
18486
18567
  init_narrow();
18568
+ init_runtimes();
18487
18569
  init_wiring();
18488
18570
  init_safe_write();
18489
18571
  Aborted = class extends Error {
@@ -18876,13 +18958,13 @@ var init_record = __esm({
18876
18958
 
18877
18959
  // src/research/run.ts
18878
18960
  import { execFileSync as execFileSync13 } from "node:child_process";
18879
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync23, renameSync as renameSync4, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
18880
- import { join as join24, resolve as resolve15 } from "node:path";
18961
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync23, renameSync as renameSync4, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
18962
+ import { join as join25, resolve as resolve15 } from "node:path";
18881
18963
  function git6(args, cwd) {
18882
18964
  return execFileSync13("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
18883
18965
  }
18884
18966
  function pairRecordPath(ledger, task, pair) {
18885
- return join24(ledger, "pairs", `${task}--${pair}.json`);
18967
+ return join25(ledger, "pairs", `${task}--${pair}.json`);
18886
18968
  }
18887
18969
  function resumableRecord(path, expected) {
18888
18970
  let raw;
@@ -18928,9 +19010,9 @@ function writeRecordAtomically(path, text) {
18928
19010
  renameSync4(tmp, path);
18929
19011
  }
18930
19012
  function freshWorkspace(ledger, task, pair, arm, sourceBase) {
18931
- const ws3 = join24(ledger, "workspaces", `${task.id}--${pair}--${arm}`);
19013
+ const ws3 = join25(ledger, "workspaces", `${task.id}--${pair}--${arm}`);
18932
19014
  rmSync11(ws3, { recursive: true, force: true });
18933
- mkdirSync9(join24(ledger, "workspaces"), { recursive: true });
19015
+ mkdirSync9(join25(ledger, "workspaces"), { recursive: true });
18934
19016
  try {
18935
19017
  execFileSync13("git", ["clone", "-q", "--no-hardlinks", task.repo, ws3], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
18936
19018
  } catch (e) {
@@ -19178,14 +19260,14 @@ function runResearch(opts) {
19178
19260
  }
19179
19261
  const ledger = resolve15(opts.out);
19180
19262
  const pairs = opts.pairs ?? 1;
19181
- mkdirSync9(join24(ledger, "pairs"), { recursive: true });
19263
+ mkdirSync9(join25(ledger, "pairs"), { recursive: true });
19182
19264
  for (const task of tasks) {
19183
19265
  const existingRecords = /* @__PURE__ */ new Map();
19184
19266
  let sourceBase = null;
19185
19267
  try {
19186
19268
  for (let pair = 1; pair <= pairs; pair++) {
19187
19269
  const path = pairRecordPath(ledger, task.id, pair);
19188
- if (!existsSync17(path)) continue;
19270
+ if (!existsSync18(path)) continue;
19189
19271
  const existing = resumableRecord(path, {
19190
19272
  task: task.id,
19191
19273
  pair,
@@ -19311,9 +19393,9 @@ var init_run2 = __esm({
19311
19393
 
19312
19394
  // src/research/summarize.ts
19313
19395
  import { readdirSync as readdirSync7, readFileSync as readFileSync24 } from "node:fs";
19314
- import { join as join25, resolve as resolve16 } from "node:path";
19396
+ import { join as join26, resolve as resolve16 } from "node:path";
19315
19397
  function readLedger2(dir) {
19316
- const pairsDir = join25(resolve16(dir), "pairs");
19398
+ const pairsDir = join26(resolve16(dir), "pairs");
19317
19399
  let names;
19318
19400
  try {
19319
19401
  names = readdirSync7(pairsDir).filter((n) => n.endsWith(".json")).sort();
@@ -19322,7 +19404,7 @@ function readLedger2(dir) {
19322
19404
  }
19323
19405
  if (names.length === 0) throw new ResearchError(`ledger ${pairsDir} holds no pair records`);
19324
19406
  return names.map((n) => {
19325
- const path = join25(pairsDir, n);
19407
+ const path = join26(pairsDir, n);
19326
19408
  let raw;
19327
19409
  try {
19328
19410
  raw = JSON.parse(readFileSync24(path, "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.28.0",
3
+ "version": "2.29.1",
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",