@lannguyensi/harness 0.44.0 → 0.46.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/CHANGELOG.md +1245 -0
- package/dist/cli/adopt/derive.d.ts +16 -7
- package/dist/cli/adopt/derive.js +19 -2
- package/dist/cli/adopt/derive.js.map +1 -1
- package/dist/cli/adopt/index.js +18 -3
- package/dist/cli/adopt/index.js.map +1 -1
- package/dist/cli/apply/apply.d.ts +1 -0
- package/dist/cli/apply/apply.js +62 -6
- package/dist/cli/apply/apply.js.map +1 -1
- package/dist/cli/apply/generate-codex-config.js +5 -6
- package/dist/cli/apply/generate-codex-config.js.map +1 -1
- package/dist/cli/apply/generate-opencode-config.d.ts +58 -0
- package/dist/cli/apply/generate-opencode-config.js +210 -0
- package/dist/cli/apply/generate-opencode-config.js.map +1 -0
- package/dist/cli/apply/generate-settings.d.ts +90 -32
- package/dist/cli/apply/generate-settings.js +133 -10
- package/dist/cli/apply/generate-settings.js.map +1 -1
- package/dist/cli/apply/index.d.ts +2 -1
- package/dist/cli/apply/index.js +2 -1
- package/dist/cli/apply/index.js.map +1 -1
- package/dist/cli/apply/next-steps.d.ts +20 -0
- package/dist/cli/apply/next-steps.js +60 -0
- package/dist/cli/apply/next-steps.js.map +1 -1
- package/dist/cli/approve/stdin-report.d.ts +15 -0
- package/dist/cli/approve/stdin-report.js +1 -1
- package/dist/cli/approve/stdin-report.js.map +1 -1
- package/dist/cli/approve/understanding.d.ts +75 -0
- package/dist/cli/approve/understanding.js +150 -2
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/audit.d.ts +1 -1
- package/dist/cli/audit.js +3 -2
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/doctor/claude-mcp.d.ts +19 -1
- package/dist/cli/doctor/claude-mcp.js +36 -10
- package/dist/cli/doctor/claude-mcp.js.map +1 -1
- package/dist/cli/doctor/codex.d.ts +3 -1
- package/dist/cli/doctor/codex.js +6 -9
- package/dist/cli/doctor/codex.js.map +1 -1
- package/dist/cli/doctor/format.js +94 -8
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +23 -0
- package/dist/cli/doctor/index.js +106 -2
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/opencode.d.ts +24 -0
- package/dist/cli/doctor/opencode.js +107 -0
- package/dist/cli/doctor/opencode.js.map +1 -0
- package/dist/cli/doctor/target-checks.d.ts +11 -0
- package/dist/cli/doctor/target-checks.js +25 -0
- package/dist/cli/doctor/target-checks.js.map +1 -0
- package/dist/cli/doctor/toolchain-parity.d.ts +63 -0
- package/dist/cli/doctor/toolchain-parity.js +154 -0
- package/dist/cli/doctor/toolchain-parity.js.map +1 -0
- package/dist/cli/doctor/types.d.ts +85 -1
- package/dist/cli/doctor/types.js +1 -1
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/doctor/understanding-mode-env.d.ts +39 -0
- package/dist/cli/doctor/understanding-mode-env.js +83 -0
- package/dist/cli/doctor/understanding-mode-env.js.map +1 -0
- package/dist/cli/dry-run.js +32 -17
- package/dist/cli/dry-run.js.map +1 -1
- package/dist/cli/explain.d.ts +1 -1
- package/dist/cli/explain.js +1 -1
- package/dist/cli/explain.js.map +1 -1
- package/dist/cli/index.js +64 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +36 -13
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/interactive.d.ts +7 -4
- package/dist/cli/init/interactive.js +79 -27
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +32 -1
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +2 -1
- package/dist/cli/init/templates.js +130 -20
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/approve-escape.js +69 -8
- package/dist/cli/pack/approve-escape.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.js +1 -1
- package/dist/cli/pack/hook-branch-protection.js.map +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +27 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-post-merge-gate.js +1 -1
- package/dist/cli/pack/hook-post-merge-gate.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +91 -4
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-solution-acceptance.js +19 -2
- package/dist/cli/pack/hook-solution-acceptance.js.map +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.d.ts +13 -0
- package/dist/cli/pack/understanding-report-schema-hint.js +52 -3
- package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
- package/dist/cli/pause/index.js +1 -1
- package/dist/cli/pause/index.js.map +1 -1
- package/dist/cli/policy/intercept.d.ts +148 -0
- package/dist/cli/policy/intercept.js +379 -48
- package/dist/cli/policy/intercept.js.map +1 -1
- package/dist/cli/session-start/index.d.ts +12 -3
- package/dist/cli/session-start/index.js +67 -11
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/cli/session-start/stale-base-check.d.ts +71 -0
- package/dist/cli/session-start/stale-base-check.js +379 -0
- package/dist/cli/session-start/stale-base-check.js.map +1 -0
- package/dist/cli/session-start/toolchain-parity.d.ts +142 -8
- package/dist/cli/session-start/toolchain-parity.js +244 -36
- package/dist/cli/session-start/toolchain-parity.js.map +1 -1
- package/dist/cli/smoke/assertions.d.ts +9 -2
- package/dist/cli/smoke/assertions.js +9 -2
- package/dist/cli/smoke/assertions.js.map +1 -1
- package/dist/cli/uninstall/index.js +18 -2
- package/dist/cli/uninstall/index.js.map +1 -1
- package/dist/cli/validate/checks.d.ts +3 -1
- package/dist/cli/validate/checks.js +298 -3
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/io/claude-mcp.d.ts +122 -1
- package/dist/io/claude-mcp.js +214 -12
- package/dist/io/claude-mcp.js.map +1 -1
- package/dist/io/duration.js.map +1 -0
- package/dist/io/expand-home.js.map +1 -0
- package/dist/io/extract.js.map +1 -0
- package/dist/io/harness-lock.js +1 -1
- package/dist/io/harness-lock.js.map +1 -1
- package/dist/{runtime → io}/ledger-record.d.ts +1 -1
- package/dist/io/ledger-record.js.map +1 -0
- package/dist/policies/index.d.ts +2 -2
- package/dist/policies/index.js +2 -2
- package/dist/policies/index.js.map +1 -1
- package/dist/policies/ledger-client.js +10 -6
- package/dist/policies/ledger-client.js.map +1 -1
- package/dist/policies/producers.js +1 -1
- package/dist/policies/producers.js.map +1 -1
- package/dist/policies/requires.js +2 -2
- package/dist/policies/requires.js.map +1 -1
- package/dist/policy-packs/builtin/branch-protection.js +28 -3
- package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
- package/dist/policy-packs/builtin/post-merge-gate.js +45 -9
- package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +139 -8
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +246 -12
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance.js +17 -3
- package/dist/policy-packs/builtin/solution-acceptance.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.d.ts +25 -0
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js +89 -0
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +13 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +39 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.d.ts +25 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.js +56 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.d.ts +20 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +79 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.d.ts +103 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js +203 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +177 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +309 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.d.ts +106 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +209 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.d.ts +78 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js +185 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +1 -536
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +9 -1095
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +109 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js +183 -15
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/policy-packs/registry.d.ts +11 -0
- package/dist/policy-packs/registry.js +1 -1
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/policy-packs/runtime.d.ts +1 -1
- package/dist/policy-packs/runtime.js +10 -1
- package/dist/policy-packs/runtime.js.map +1 -1
- package/dist/runtime/agent-facing.js +1 -1
- package/dist/runtime/agent-facing.js.map +1 -1
- package/dist/runtime/bash-match-facts.d.ts +9 -7
- package/dist/runtime/bash-match-facts.js +9 -7
- package/dist/runtime/bash-match-facts.js.map +1 -1
- package/dist/runtime/bash-match-registry.d.ts +75 -38
- package/dist/runtime/bash-match-registry.js +279 -232
- package/dist/runtime/bash-match-registry.js.map +1 -1
- package/dist/runtime/bash-prefix-parse.d.ts +7 -0
- package/dist/runtime/bash-prefix-parse.js +205 -17
- package/dist/runtime/bash-prefix-parse.js.map +1 -1
- package/dist/runtime/command-normalize.d.ts +112 -0
- package/dist/runtime/command-normalize.js +411 -38
- package/dist/runtime/command-normalize.js.map +1 -1
- package/dist/runtime/hermetic-spawn-guard.js +6 -0
- package/dist/runtime/hermetic-spawn-guard.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/intercept.d.ts +78 -3
- package/dist/runtime/intercept.js +151 -39
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/ledger-add.js +1 -1
- package/dist/runtime/ledger-add.js.map +1 -1
- package/dist/runtime/read-only-bash.js +303 -24
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/schema/doctor.d.ts +25 -0
- package/dist/schema/doctor.js +23 -0
- package/dist/schema/doctor.js.map +1 -0
- package/dist/schema/extract.js +1 -1
- package/dist/schema/extract.js.map +1 -1
- package/dist/schema/index.d.ts +76 -0
- package/dist/schema/index.js +9 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/requires.js +2 -2
- package/dist/schema/requires.js.map +1 -1
- package/dist/schema/risk.d.ts +7 -0
- package/dist/schema/risk.js +23 -0
- package/dist/schema/risk.js.map +1 -1
- package/dist/schema/stale-base-check.d.ts +18 -0
- package/dist/schema/stale-base-check.js +37 -0
- package/dist/schema/stale-base-check.js.map +1 -0
- package/dist/schema/toolchain-parity.d.ts +3 -0
- package/dist/schema/toolchain-parity.js +13 -0
- package/dist/schema/toolchain-parity.js.map +1 -1
- package/package.json +3 -2
- package/dist/policies/duration.js.map +0 -1
- package/dist/policies/extract.js.map +0 -1
- package/dist/runtime/expand-home.js.map +0 -1
- package/dist/runtime/ledger-record.js.map +0 -1
- /package/dist/{policies → io}/duration.d.ts +0 -0
- /package/dist/{policies → io}/duration.js +0 -0
- /package/dist/{runtime → io}/expand-home.d.ts +0 -0
- /package/dist/{runtime → io}/expand-home.js +0 -0
- /package/dist/{policies → io}/extract.d.ts +0 -0
- /package/dist/{policies → io}/extract.js +0 -0
- /package/dist/{runtime → io}/ledger-record.js +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Active-claim tracking (harness/494fd1e5), split out of the former
|
|
2
|
+
// monolithic understanding-before-execution-runtime.ts (structural
|
|
3
|
+
// concentration slice 2, agent-tasks 348a4d42). Pure move: see
|
|
4
|
+
// src/policy-packs/builtin/understanding-before-execution/index.ts for
|
|
5
|
+
// the re-exported public surface.
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { atomicWriteFile } from "../../../io/atomic-write.js";
|
|
9
|
+
// Active-claim tracking (harness/494fd1e5). When the agent calls
|
|
10
|
+
// `mcp__agent-tasks__task_start`, a PostToolUse hook writes the claimed
|
|
11
|
+
// task id to a stable file. `harness approve understanding` reads it
|
|
12
|
+
// when --task is absent and auto-supplies it as the task-scoped marker
|
|
13
|
+
// target. This closes the v1 ergonomics gap from PR #184 where the
|
|
14
|
+
// operator had to type the taskId by hand.
|
|
15
|
+
//
|
|
16
|
+
// File contract: a single line containing just the taskId. No JSON,
|
|
17
|
+
// no metadata. Operators can `cat` it to debug. The post-tool-use
|
|
18
|
+
// hook on task_finish / task_abandon removes the file.
|
|
19
|
+
export const ACTIVE_CLAIM_FILENAME = "active-claim";
|
|
20
|
+
export function activeClaimPathFor(generatedDir) {
|
|
21
|
+
return path.join(generatedDir, ACTIVE_CLAIM_FILENAME);
|
|
22
|
+
}
|
|
23
|
+
function rejectMalformedClaimId(taskId) {
|
|
24
|
+
if (taskId.length === 0) {
|
|
25
|
+
throw new Error("taskId is empty");
|
|
26
|
+
}
|
|
27
|
+
if (taskId.includes("\n") ||
|
|
28
|
+
taskId.includes("\r") ||
|
|
29
|
+
taskId.includes("/") ||
|
|
30
|
+
taskId.includes("\\") ||
|
|
31
|
+
taskId.includes("..")) {
|
|
32
|
+
throw new Error(`taskId contains forbidden characters (newline / path-separator / traversal): ${JSON.stringify(taskId)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Hook-side: write the active claim file. Atomic. Called from the
|
|
37
|
+
* track-active-claim PostToolUse hook on `task_start`. The body is
|
|
38
|
+
* just the taskId (no JSON) so an operator running
|
|
39
|
+
* `cat ~/.claude/harness.generated/active-claim` sees the id directly.
|
|
40
|
+
*/
|
|
41
|
+
export function writeActiveClaim(generatedDir, taskId) {
|
|
42
|
+
rejectMalformedClaimId(taskId);
|
|
43
|
+
const filePath = activeClaimPathFor(generatedDir);
|
|
44
|
+
atomicWriteFile(filePath, `${taskId}\n`);
|
|
45
|
+
return filePath;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Operator-side: read the active claim file. Returns the taskId or
|
|
49
|
+
* null when the file is absent / unreadable / empty. `harness approve
|
|
50
|
+
* understanding` calls this when --task is absent, then passes the
|
|
51
|
+
* resolved id to writeTaskApprovalMarker.
|
|
52
|
+
*
|
|
53
|
+
* Defense-in-depth: if the on-disk content fails the same
|
|
54
|
+
* path-traversal / newline check that gates writes, the read returns
|
|
55
|
+
* null instead of surfacing a poisoned id. The write side guards
|
|
56
|
+
* against a malformed taskId reaching the file in the first place,
|
|
57
|
+
* but a stale file authored before this guard (or hand-edited)
|
|
58
|
+
* shouldn't escalate into a forged task-marker write downstream.
|
|
59
|
+
*/
|
|
60
|
+
export function readActiveClaim(generatedDir) {
|
|
61
|
+
const filePath = activeClaimPathFor(generatedDir);
|
|
62
|
+
let raw;
|
|
63
|
+
try {
|
|
64
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const trimmed = raw.trim();
|
|
70
|
+
if (trimmed.length === 0)
|
|
71
|
+
return null;
|
|
72
|
+
try {
|
|
73
|
+
rejectMalformedClaimId(trimmed);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return trimmed;
|
|
79
|
+
}
|
|
80
|
+
/** Hook-side: remove the active claim file. Idempotent. */
|
|
81
|
+
export function clearActiveClaim(generatedDir) {
|
|
82
|
+
try {
|
|
83
|
+
fs.rmSync(activeClaimPathFor(generatedDir));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
/* already gone */
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=active-claim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-claim.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/active-claim.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,mEAAmE;AACnE,+DAA+D;AAC/D,uEAAuE;AACvE,kCAAkC;AAElC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,iEAAiE;AACjE,wEAAwE;AACxE,qEAAqE;AACrE,uEAAuE;AACvE,mEAAmE;AACnE,2CAA2C;AAC3C,EAAE;AACF,oEAAoE;AACpE,kEAAkE;AAClE,uDAAuD;AAEvD,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CAAC;AAEpD,MAAM,UAAU,kBAAkB,CAAC,YAAoB;IACrD,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAc;IAC5C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,IACE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QACrB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QACrB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QACpB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QACrB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EACrB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,gFAAgF,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CACzG,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,YAAoB,EAAE,MAAc;IACnE,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAClD,eAAe,CAAC,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC;IACzC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAClD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,CAAC;QACH,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAAC,YAAoB;IACnD,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ApprovalSource = "marker" | "ledger" | "persisted-report" | "none" | "recovery-commit";
|
|
2
|
+
export interface ApprovalCheckResult {
|
|
3
|
+
approved: boolean;
|
|
4
|
+
source: ApprovalSource;
|
|
5
|
+
detail: string;
|
|
6
|
+
}
|
|
7
|
+
export { APPROVED_LEDGER_TAG_PREFIX, approvedLedgerTagFor, isPolicyDecisionRow, matchLedgerEntries, } from "./ledger.js";
|
|
8
|
+
export { APPROVAL_MARKER_DIRNAME, approvalMarkerPathFor, type ApprovalMarker, writeApprovalMarker, type MarkerCheck, type CheckApprovalMarkerOptions, checkApprovalMarker, clearApprovalMarker, } from "./markers.js";
|
|
9
|
+
export { type PersistedReport, REPORTS_DIR_ENV, defaultReportsDir, reportsDirForManifest, listPersistedReports, TOLERANT_FALLBACK_MAX_AGE_MS, TOLERANT_FALLBACK_FUTURE_SKEW_MS, type FindReportOptions, type FindReportSelection, selectReportForSession, findLatestReportForSession, type PersistedReportApprovalCheck, expirePersistedReport, checkPersistedReport, } from "./persisted-reports.js";
|
|
10
|
+
export { type ApprovalLifecycle, parseApprovalLifecycle } from "./lifecycle.js";
|
|
11
|
+
export { APPROVAL_MARKER_TASK_PREFIX, taskApprovalMarkerPathFor, writeTaskApprovalMarker, checkActiveClaimApprovalMarker, type OperatorMarkerApproval, checkOperatorApprovalMarkers, clearTaskApprovalMarker, } from "./task-markers.js";
|
|
12
|
+
export { DEFAULT_BASH_TOOL_NAMES, toolNameMatchesAny, bashCommandMatchesAny, extractBashCommandFromToolInput, extractTaskIdFromToolInput, extractTasksTransitionStatusFromToolInput, type PostToolUseBoundaryMatch, matchPostToolUseBoundary, type ApplyPostToolUseExpiryResult, applyPostToolUseExpiry, describePostToolUseExpiry, } from "./post-tool-use-boundary.js";
|
|
13
|
+
export { ACTIVE_CLAIM_FILENAME, activeClaimPathFor, writeActiveClaim, readActiveClaim, clearActiveClaim, } from "./active-claim.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Phase 6 #4 — runtime helpers for the understanding-before-execution pack.
|
|
2
|
+
//
|
|
3
|
+
// Two-source approval check that the harness-side PreToolUse blocker
|
|
4
|
+
// consults:
|
|
5
|
+
//
|
|
6
|
+
// 1. Filesystem marker `<generatedDir>/.approvals/<sessionId>` written
|
|
7
|
+
// by `harness approve understanding` from the operator's shell.
|
|
8
|
+
// Canonical for harnessed sessions. Replaces the ledger-substring
|
|
9
|
+
// check that shipped through v0.13.0 (agent-tasks/88ca4bb3): the
|
|
10
|
+
// agent has direct MCP access to the same ledger that gate path
|
|
11
|
+
// consulted, so any agent could write `understanding-approved:<sid>`
|
|
12
|
+
// itself and self-approve. Edit / Write / Bash are all gated by
|
|
13
|
+
// this same PreToolUse hook, and the configured MCP servers do not
|
|
14
|
+
// expose filesystem writes, so the marker file is reachable only
|
|
15
|
+
// from a process the operator launched (their `!`-shell or any
|
|
16
|
+
// other un-hooked terminal). Operator-side: writeApprovalMarker
|
|
17
|
+
// below. Forensics: the ledger row is still written by
|
|
18
|
+
// `harness approve understanding`, for audit only.
|
|
19
|
+
// 2. Persisted JSON report under `.understanding-gate/reports/`
|
|
20
|
+
// (canonical for solo `@lannguyensi/understanding-gate` users).
|
|
21
|
+
// The package writes one file per session; the latest with
|
|
22
|
+
// `approvalStatus: "approved"` matching the session_id wins. The
|
|
23
|
+
// report is flipped to "approved" by `harness approve`; the
|
|
24
|
+
// agent's Stop hook only writes `pending` reports and cannot flip
|
|
25
|
+
// them (Edit/Write/Bash gated), so this source is also operator-
|
|
26
|
+
// authored.
|
|
27
|
+
//
|
|
28
|
+
// Either source approves. The persisted-report fallback is what makes a
|
|
29
|
+
// solo user without grounding-mcp wired still able to approve via the
|
|
30
|
+
// package's CLI; the marker path is what makes a harnessed session see
|
|
31
|
+
// the approval immediately on the next tool call.
|
|
32
|
+
export { APPROVED_LEDGER_TAG_PREFIX, approvedLedgerTagFor, isPolicyDecisionRow, matchLedgerEntries, } from "./ledger.js";
|
|
33
|
+
export { APPROVAL_MARKER_DIRNAME, approvalMarkerPathFor, writeApprovalMarker, checkApprovalMarker, clearApprovalMarker, } from "./markers.js";
|
|
34
|
+
export { REPORTS_DIR_ENV, defaultReportsDir, reportsDirForManifest, listPersistedReports, TOLERANT_FALLBACK_MAX_AGE_MS, TOLERANT_FALLBACK_FUTURE_SKEW_MS, selectReportForSession, findLatestReportForSession, expirePersistedReport, checkPersistedReport, } from "./persisted-reports.js";
|
|
35
|
+
export { parseApprovalLifecycle } from "./lifecycle.js";
|
|
36
|
+
export { APPROVAL_MARKER_TASK_PREFIX, taskApprovalMarkerPathFor, writeTaskApprovalMarker, checkActiveClaimApprovalMarker, checkOperatorApprovalMarkers, clearTaskApprovalMarker, } from "./task-markers.js";
|
|
37
|
+
export { DEFAULT_BASH_TOOL_NAMES, toolNameMatchesAny, bashCommandMatchesAny, extractBashCommandFromToolInput, extractTaskIdFromToolInput, extractTasksTransitionStatusFromToolInput, matchPostToolUseBoundary, applyPostToolUseExpiry, describePostToolUseExpiry, } from "./post-tool-use-boundary.js";
|
|
38
|
+
export { ACTIVE_CLAIM_FILENAME, activeClaimPathFor, writeActiveClaim, readActiveClaim, clearActiveClaim, } from "./active-claim.js";
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,qEAAqE;AACrE,YAAY;AACZ,EAAE;AACF,yEAAyE;AACzE,qEAAqE;AACrE,uEAAuE;AACvE,sEAAsE;AACtE,qEAAqE;AACrE,0EAA0E;AAC1E,qEAAqE;AACrE,wEAAwE;AACxE,sEAAsE;AACtE,oEAAoE;AACpE,qEAAqE;AACrE,4DAA4D;AAC5D,wDAAwD;AACxD,kEAAkE;AAClE,qEAAqE;AACrE,gEAAgE;AAChE,sEAAsE;AACtE,iEAAiE;AACjE,uEAAuE;AACvE,sEAAsE;AACtE,iBAAiB;AACjB,EAAE;AACF,wEAAwE;AACxE,sEAAsE;AACtE,uEAAuE;AACvE,kDAAkD;AAelD,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EAErB,mBAAmB,EAGnB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAEL,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,4BAA4B,EAC5B,gCAAgC,EAGhC,sBAAsB,EACtB,0BAA0B,EAE1B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAA0B,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,8BAA8B,EAE9B,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,+BAA+B,EAC/B,0BAA0B,EAC1B,yCAAyC,EAEzC,wBAAwB,EAExB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type LedgerEntry } from "../../../policies/index.js";
|
|
2
|
+
export declare const APPROVED_LEDGER_TAG_PREFIX = "understanding-approved:";
|
|
3
|
+
/** Build the per-session ledger tag the pack searches for. */
|
|
4
|
+
export declare function approvedLedgerTagFor(sessionId: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Phase 6 #6 — substring-pollution defence shared by every PreToolUse
|
|
7
|
+
* blocker (Claude Code + Codex). Drops policy_decision rows so a
|
|
8
|
+
* `understanding-approved:<sess>` substring inside an audit row's
|
|
9
|
+
* JSON-encoded `reason` cannot accidentally satisfy the gate.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isPolicyDecisionRow(e: LedgerEntry): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Match a ledger fetch against the per-session approval tag. Kept for
|
|
14
|
+
* the audit / forensics path only: a ledger entry tagged
|
|
15
|
+
* `understanding-approved:<sid>` is no longer a sufficient signal to
|
|
16
|
+
* unblock the gate (agent-tasks/88ca4bb3: the agent has the same MCP
|
|
17
|
+
* surface and could self-write the row). Use `checkApprovalMarker`
|
|
18
|
+
* for the gate decision; this helper now serves `harness audit` /
|
|
19
|
+
* `harness explain --trace` style read paths that surface the
|
|
20
|
+
* historic ledger trail without granting approval power.
|
|
21
|
+
*/
|
|
22
|
+
export declare function matchLedgerEntries(entries: LedgerEntry[], sessionId: string): {
|
|
23
|
+
matched: boolean;
|
|
24
|
+
detail: string;
|
|
25
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Ledger audit/forensics helpers, split out of the former monolithic
|
|
2
|
+
// understanding-before-execution-runtime.ts (structural concentration
|
|
3
|
+
// slice 2, agent-tasks 348a4d42). Pure move: see
|
|
4
|
+
// src/policy-packs/builtin/understanding-before-execution/index.ts for
|
|
5
|
+
// the re-exported public surface.
|
|
6
|
+
import { POLICY_DECISION_TYPE } from "../../../io/ledger-record.js";
|
|
7
|
+
import { APPROVAL_MARKER_DIRNAME } from "./markers.js";
|
|
8
|
+
export const APPROVED_LEDGER_TAG_PREFIX = "understanding-approved:";
|
|
9
|
+
/** Build the per-session ledger tag the pack searches for. */
|
|
10
|
+
export function approvedLedgerTagFor(sessionId) {
|
|
11
|
+
return `${APPROVED_LEDGER_TAG_PREFIX}${sessionId}`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Phase 6 #6 — substring-pollution defence shared by every PreToolUse
|
|
15
|
+
* blocker (Claude Code + Codex). Drops policy_decision rows so a
|
|
16
|
+
* `understanding-approved:<sess>` substring inside an audit row's
|
|
17
|
+
* JSON-encoded `reason` cannot accidentally satisfy the gate.
|
|
18
|
+
*/
|
|
19
|
+
export function isPolicyDecisionRow(e) {
|
|
20
|
+
if (e.type === POLICY_DECISION_TYPE)
|
|
21
|
+
return true;
|
|
22
|
+
if (typeof e.content === "string" && e.content.startsWith(`${POLICY_DECISION_TYPE}:`)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Match a ledger fetch against the per-session approval tag. Kept for
|
|
29
|
+
* the audit / forensics path only: a ledger entry tagged
|
|
30
|
+
* `understanding-approved:<sid>` is no longer a sufficient signal to
|
|
31
|
+
* unblock the gate (agent-tasks/88ca4bb3: the agent has the same MCP
|
|
32
|
+
* surface and could self-write the row). Use `checkApprovalMarker`
|
|
33
|
+
* for the gate decision; this helper now serves `harness audit` /
|
|
34
|
+
* `harness explain --trace` style read paths that surface the
|
|
35
|
+
* historic ledger trail without granting approval power.
|
|
36
|
+
*/
|
|
37
|
+
export function matchLedgerEntries(entries, sessionId) {
|
|
38
|
+
const wanted = approvedLedgerTagFor(sessionId);
|
|
39
|
+
let scanned = 0;
|
|
40
|
+
for (const e of entries) {
|
|
41
|
+
if (isPolicyDecisionRow(e))
|
|
42
|
+
continue;
|
|
43
|
+
scanned += 1;
|
|
44
|
+
if (typeof e.content === "string" && e.content.includes(wanted)) {
|
|
45
|
+
return {
|
|
46
|
+
matched: true,
|
|
47
|
+
detail: `audit: ledger tag ${wanted} present at ${e.createdAt} (no longer satisfies the gate; see harness.generated/${APPROVAL_MARKER_DIRNAME}/${sessionId})`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
matched: false,
|
|
53
|
+
detail: `no ledger entry matched ${wanted} (scanned ${scanned} non-policy_decision row(s))`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/ledger.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sEAAsE;AACtE,iDAAiD;AACjD,uEAAuE;AACvE,kCAAkC;AAElC,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,CAAC,MAAM,0BAA0B,GAAG,yBAAyB,CAAC;AAEpE,8DAA8D;AAC9D,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,OAAO,GAAG,0BAA0B,GAAG,SAAS,EAAE,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAc;IAChD,IAAI,CAAC,CAAC,IAAI,KAAK,oBAAoB;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,oBAAoB,GAAG,CAAC,EAAE,CAAC;QACtF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAsB,EACtB,SAAiB;IAEjB,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,mBAAmB,CAAC,CAAC,CAAC;YAAE,SAAS;QACrC,OAAO,IAAI,CAAC,CAAC;QACb,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChE,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,qBAAqB,MAAM,eAAe,CAAC,CAAC,SAAS,yDAAyD,uBAAuB,IAAI,SAAS,GAAG;aAC9J,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO;QACL,OAAO,EAAE,KAAK;QACd,MAAM,EAAE,2BAA2B,MAAM,aAAa,OAAO,8BAA8B;KAC5F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ApprovalLifecycle {
|
|
2
|
+
/** Tool-name patterns whose successful PostToolUse expires the marker. */
|
|
3
|
+
expireOnToolMatch: string[];
|
|
4
|
+
/** Pre-compiled regex patterns matched against `Bash` tool_input.command. */
|
|
5
|
+
expireOnBashMatch: RegExp[];
|
|
6
|
+
/** Max marker age in milliseconds. Undefined means no TTL. */
|
|
7
|
+
maxAgeMs?: number;
|
|
8
|
+
/** Whether the operator explicitly opted out via `{ mode: "session" }`. */
|
|
9
|
+
legacyMode: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parse the optional `approval_lifecycle` block from a pack config.
|
|
13
|
+
* Best-effort: malformed values fall back to the default (no expiry,
|
|
14
|
+
* legacyMode=false) and write a one-line warning to the supplied
|
|
15
|
+
* stderr. The PreToolUse / PostToolUse hooks must keep working even
|
|
16
|
+
* when the operator typed a typo in the YAML.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseApprovalLifecycle(raw: unknown, stderr?: {
|
|
19
|
+
write: (s: string) => void;
|
|
20
|
+
} | null): ApprovalLifecycle;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// `approval_lifecycle` pack-config parsing, split out of the former
|
|
2
|
+
// monolithic understanding-before-execution-runtime.ts (structural
|
|
3
|
+
// concentration slice 2, agent-tasks 348a4d42). Pure move: see
|
|
4
|
+
// src/policy-packs/builtin/understanding-before-execution/index.ts for
|
|
5
|
+
// the re-exported public surface.
|
|
6
|
+
import { InvalidDurationError, parseDurationSeconds } from "../../../policies/index.js";
|
|
7
|
+
const DEFAULT_LIFECYCLE = {
|
|
8
|
+
expireOnToolMatch: [],
|
|
9
|
+
expireOnBashMatch: [],
|
|
10
|
+
legacyMode: false,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Parse the optional `approval_lifecycle` block from a pack config.
|
|
14
|
+
* Best-effort: malformed values fall back to the default (no expiry,
|
|
15
|
+
* legacyMode=false) and write a one-line warning to the supplied
|
|
16
|
+
* stderr. The PreToolUse / PostToolUse hooks must keep working even
|
|
17
|
+
* when the operator typed a typo in the YAML.
|
|
18
|
+
*/
|
|
19
|
+
export function parseApprovalLifecycle(raw, stderr) {
|
|
20
|
+
if (raw === undefined || raw === null)
|
|
21
|
+
return DEFAULT_LIFECYCLE;
|
|
22
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
23
|
+
stderr?.write(`harness pack hook: config.approval_lifecycle ignored (expected object, got ${typeof raw})\n`);
|
|
24
|
+
return DEFAULT_LIFECYCLE;
|
|
25
|
+
}
|
|
26
|
+
const obj = raw;
|
|
27
|
+
if (obj["mode"] === "session") {
|
|
28
|
+
return { expireOnToolMatch: [], expireOnBashMatch: [], legacyMode: true };
|
|
29
|
+
}
|
|
30
|
+
const expireOnToolMatch = [];
|
|
31
|
+
const list = obj["expire_on_tool_match"];
|
|
32
|
+
if (Array.isArray(list)) {
|
|
33
|
+
for (const v of list) {
|
|
34
|
+
if (typeof v === "string" && v.length > 0)
|
|
35
|
+
expireOnToolMatch.push(v);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else if (list !== undefined) {
|
|
39
|
+
stderr?.write(`harness pack hook: config.approval_lifecycle.expire_on_tool_match ignored (expected string[], got ${typeof list})\n`);
|
|
40
|
+
}
|
|
41
|
+
const expireOnBashMatch = [];
|
|
42
|
+
const bashList = obj["expire_on_bash_match"];
|
|
43
|
+
if (Array.isArray(bashList)) {
|
|
44
|
+
for (const v of bashList) {
|
|
45
|
+
if (typeof v !== "string" || v.length === 0)
|
|
46
|
+
continue;
|
|
47
|
+
try {
|
|
48
|
+
expireOnBashMatch.push(new RegExp(v));
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
stderr?.write(`harness pack hook: config.approval_lifecycle.expire_on_bash_match entry ignored ("${v}"): ${err.message}\n`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (bashList !== undefined) {
|
|
56
|
+
stderr?.write(`harness pack hook: config.approval_lifecycle.expire_on_bash_match ignored (expected string[], got ${typeof bashList})\n`);
|
|
57
|
+
}
|
|
58
|
+
let maxAgeMs;
|
|
59
|
+
const maxAgeRaw = obj["max_age"];
|
|
60
|
+
if (typeof maxAgeRaw === "string" && maxAgeRaw.length > 0) {
|
|
61
|
+
try {
|
|
62
|
+
maxAgeMs = parseDurationSeconds(maxAgeRaw) * 1_000;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const msg = err instanceof InvalidDurationError ? err.message : String(err);
|
|
66
|
+
stderr?.write(`harness pack hook: config.approval_lifecycle.max_age ignored: ${msg}\n`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (maxAgeRaw !== undefined) {
|
|
70
|
+
stderr?.write(`harness pack hook: config.approval_lifecycle.max_age ignored (expected duration string like "4h", got ${typeof maxAgeRaw})\n`);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
expireOnToolMatch,
|
|
74
|
+
expireOnBashMatch,
|
|
75
|
+
...(maxAgeMs !== undefined && { maxAgeMs }),
|
|
76
|
+
legacyMode: false,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export declare const APPROVAL_MARKER_DIRNAME = ".approvals";
|
|
2
|
+
/** Filesystem path of the per-session approval marker. */
|
|
3
|
+
export declare function approvalMarkerPathFor(generatedDir: string, sessionId: string): string;
|
|
4
|
+
export interface ApprovalMarker {
|
|
5
|
+
approvedAt: string;
|
|
6
|
+
approvedBy: string;
|
|
7
|
+
/**
|
|
8
|
+
* sha256 hex of the persisted-report content this approval is bound to
|
|
9
|
+
* at sign-time (harness/f9485cc7). Optional on write (defaults to
|
|
10
|
+
* `null` — a marker with no report to bind, e.g. `harness approve
|
|
11
|
+
* branch-protection`, or a ledger-only approval); always present
|
|
12
|
+
* (possibly `null`) on a marker `checkApprovalMarker` returns.
|
|
13
|
+
*/
|
|
14
|
+
reportContentHash?: string | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Operator-side: write the marker file the gate consults. Atomic so a
|
|
18
|
+
* crash mid-write cannot leave a half-empty file the gate would accept
|
|
19
|
+
* as approved. Caller is `harness approve understanding`, which the
|
|
20
|
+
* operator runs from their un-hooked shell; if the agent could call
|
|
21
|
+
* this path the gate's value would collapse, so it lives behind the
|
|
22
|
+
* approve CLI rather than as a generally importable verb.
|
|
23
|
+
*
|
|
24
|
+
* The marker is HMAC-signed (harness/f9485cc7) over (sessionId,
|
|
25
|
+
* approvedAt, approvedBy, reportContentHash) using an operator-side key
|
|
26
|
+
* lazily generated at `<generatedDir>/.approval-signing.key` — see
|
|
27
|
+
* `src/runtime/approval-signing.ts` for the key-management contract and
|
|
28
|
+
* the honest trust model. Mode 0600 on the marker file itself, best-
|
|
29
|
+
* effort (matches the signing key's own permission convention); this is
|
|
30
|
+
* defense-in-depth alongside the signature, not a substitute for it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function writeApprovalMarker(generatedDir: string, sessionId: string, marker: ApprovalMarker): string;
|
|
33
|
+
export interface MarkerCheck {
|
|
34
|
+
matched: boolean;
|
|
35
|
+
detail: string;
|
|
36
|
+
marker: ApprovalMarker | null;
|
|
37
|
+
/**
|
|
38
|
+
* True when `matched` is false SPECIFICALLY because a readable marker
|
|
39
|
+
* existed but its `approvedAt` exceeded `opts.maxAgeMs`
|
|
40
|
+
* (agent-tasks/d8ee60ca) — as opposed to the marker being absent
|
|
41
|
+
* (never approved, or cleared by a task-completion boundary tool via
|
|
42
|
+
* `clearApprovalMarker`), corrupted, or the session id malformed.
|
|
43
|
+
* False whenever `matched` is true. This is the signal task 6e888423's
|
|
44
|
+
* recovery-git-commit exemption keys off: "this session/task DID have
|
|
45
|
+
* a real operator approval and it merely aged out" is safe to treat
|
|
46
|
+
* differently from "this session/task was never approved" or "a task
|
|
47
|
+
* boundary just cleared it for a new task" — see
|
|
48
|
+
* src/runtime/recovery-git-commit.ts for the full argument.
|
|
49
|
+
*/
|
|
50
|
+
expired: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* True when a marker FILE existed at the expected path but failed
|
|
53
|
+
* signature verification (harness/f9485cc7): missing `signature`/`alg`
|
|
54
|
+
* fields (a legacy pre-signing marker, or a marker planted without the
|
|
55
|
+
* operator-side key), a mismatched `alg`, or a signature that does not
|
|
56
|
+
* verify against the current key. Distinct from the marker being
|
|
57
|
+
* simply absent (`forged: false`, `matched: false`) so a caller can
|
|
58
|
+
* log/alert on an active forgery attempt instead of the routine
|
|
59
|
+
* "nobody has approved yet" case. `matched` is always false when
|
|
60
|
+
* `forged` is true — a forged marker never satisfies the gate,
|
|
61
|
+
* regardless of `approval_lifecycle.max_age`. A transient I/O read
|
|
62
|
+
* failure (`forged: false`, `matched: false`) is kept distinct from
|
|
63
|
+
* both: it is neither "no marker" nor evidence of tampering, just a
|
|
64
|
+
* marker whose integrity cannot be proven right now.
|
|
65
|
+
*/
|
|
66
|
+
forged: boolean;
|
|
67
|
+
}
|
|
68
|
+
export interface CheckApprovalMarkerOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Max marker age in milliseconds (agent-tasks/d8ee60ca). When set,
|
|
71
|
+
* a marker whose `approvedAt` is older than `now - maxAgeMs` is
|
|
72
|
+
* treated as expired and returns `matched:false` with an
|
|
73
|
+
* "expired" detail. When omitted, the marker has no TTL — the
|
|
74
|
+
* legacy contract (one approval per session, no expiry).
|
|
75
|
+
*/
|
|
76
|
+
maxAgeMs?: number;
|
|
77
|
+
/** Override "now" for deterministic tests. */
|
|
78
|
+
now?: Date;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Gate-side: is the per-session marker file present, readable, AND
|
|
82
|
+
* validly signed? (harness/f9485cc7 — this replaced the earlier
|
|
83
|
+
* "existence is enough" contract: a marker's mere presence used to
|
|
84
|
+
* satisfy the gate even with a malformed or corrupted body, on the
|
|
85
|
+
* theory that Edit/Write/Bash being gated meant only the operator could
|
|
86
|
+
* ever create the file. That theory holds only as long as no OTHER
|
|
87
|
+
* write primitive reaches `harness.generated/` — exactly the invariant
|
|
88
|
+
* this signing scheme no longer has to trust blindly.) A marker with no
|
|
89
|
+
* `signature`, a wrong `alg`, or a signature that fails to verify is
|
|
90
|
+
* REJECTED — see `MarkerCheck.forged` — with the SAME `matched: false`
|
|
91
|
+
* outcome as no marker at all, but a distinct, forgery-specific detail
|
|
92
|
+
* string so callers can tell the two apart for audit.
|
|
93
|
+
*
|
|
94
|
+
* `opts.maxAgeMs` (agent-tasks/d8ee60ca): when set, a validly-signed
|
|
95
|
+
* marker whose `approvedAt` is older than the cutoff returns
|
|
96
|
+
* `matched:false` with an "expired" detail so the agent gets the same
|
|
97
|
+
* "no approval" UX as a never-approved session and must re-approve.
|
|
98
|
+
* Expiry is only evaluated AFTER signature verification succeeds — an
|
|
99
|
+
* unsigned/forged marker is rejected outright, never "expired".
|
|
100
|
+
*/
|
|
101
|
+
export declare function checkApprovalMarker(generatedDir: string, sessionId: string, opts?: CheckApprovalMarkerOptions): MarkerCheck;
|
|
102
|
+
/** Clear the per-session marker (used by `harness approve --revoke` and tests). */
|
|
103
|
+
export declare function clearApprovalMarker(generatedDir: string, sessionId: string): void;
|