@lannguyensi/harness 0.43.0 → 0.45.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.
Files changed (237) hide show
  1. package/CHANGELOG.md +1248 -0
  2. package/dist/cli/adopt/derive.d.ts +16 -7
  3. package/dist/cli/adopt/derive.js +19 -2
  4. package/dist/cli/adopt/derive.js.map +1 -1
  5. package/dist/cli/adopt/index.js +18 -3
  6. package/dist/cli/adopt/index.js.map +1 -1
  7. package/dist/cli/apply/apply.d.ts +1 -0
  8. package/dist/cli/apply/apply.js +62 -6
  9. package/dist/cli/apply/apply.js.map +1 -1
  10. package/dist/cli/apply/generate-codex-config.js +5 -6
  11. package/dist/cli/apply/generate-codex-config.js.map +1 -1
  12. package/dist/cli/apply/generate-opencode-config.d.ts +58 -0
  13. package/dist/cli/apply/generate-opencode-config.js +210 -0
  14. package/dist/cli/apply/generate-opencode-config.js.map +1 -0
  15. package/dist/cli/apply/generate-settings.d.ts +90 -32
  16. package/dist/cli/apply/generate-settings.js +133 -10
  17. package/dist/cli/apply/generate-settings.js.map +1 -1
  18. package/dist/cli/apply/index.d.ts +2 -1
  19. package/dist/cli/apply/index.js +2 -1
  20. package/dist/cli/apply/index.js.map +1 -1
  21. package/dist/cli/approve/stdin-report.d.ts +15 -0
  22. package/dist/cli/approve/stdin-report.js +1 -1
  23. package/dist/cli/approve/stdin-report.js.map +1 -1
  24. package/dist/cli/approve/understanding.d.ts +75 -0
  25. package/dist/cli/approve/understanding.js +150 -2
  26. package/dist/cli/approve/understanding.js.map +1 -1
  27. package/dist/cli/audit.d.ts +1 -1
  28. package/dist/cli/audit.js +3 -2
  29. package/dist/cli/audit.js.map +1 -1
  30. package/dist/cli/doctor/claude-mcp.d.ts +19 -1
  31. package/dist/cli/doctor/claude-mcp.js +36 -10
  32. package/dist/cli/doctor/claude-mcp.js.map +1 -1
  33. package/dist/cli/doctor/codex.d.ts +3 -1
  34. package/dist/cli/doctor/codex.js +6 -9
  35. package/dist/cli/doctor/codex.js.map +1 -1
  36. package/dist/cli/doctor/format.js +94 -8
  37. package/dist/cli/doctor/format.js.map +1 -1
  38. package/dist/cli/doctor/index.d.ts +23 -0
  39. package/dist/cli/doctor/index.js +106 -2
  40. package/dist/cli/doctor/index.js.map +1 -1
  41. package/dist/cli/doctor/opencode.d.ts +24 -0
  42. package/dist/cli/doctor/opencode.js +107 -0
  43. package/dist/cli/doctor/opencode.js.map +1 -0
  44. package/dist/cli/doctor/target-checks.d.ts +11 -0
  45. package/dist/cli/doctor/target-checks.js +25 -0
  46. package/dist/cli/doctor/target-checks.js.map +1 -0
  47. package/dist/cli/doctor/toolchain-parity.d.ts +63 -0
  48. package/dist/cli/doctor/toolchain-parity.js +154 -0
  49. package/dist/cli/doctor/toolchain-parity.js.map +1 -0
  50. package/dist/cli/doctor/types.d.ts +85 -1
  51. package/dist/cli/doctor/types.js +1 -1
  52. package/dist/cli/doctor/types.js.map +1 -1
  53. package/dist/cli/doctor/understanding-mode-env.d.ts +39 -0
  54. package/dist/cli/doctor/understanding-mode-env.js +83 -0
  55. package/dist/cli/doctor/understanding-mode-env.js.map +1 -0
  56. package/dist/cli/dry-run.js +34 -13
  57. package/dist/cli/dry-run.js.map +1 -1
  58. package/dist/cli/explain.d.ts +1 -1
  59. package/dist/cli/explain.js +1 -1
  60. package/dist/cli/explain.js.map +1 -1
  61. package/dist/cli/index.js +62 -7
  62. package/dist/cli/index.js.map +1 -1
  63. package/dist/cli/init/composer.js +36 -13
  64. package/dist/cli/init/composer.js.map +1 -1
  65. package/dist/cli/init/interactive.d.ts +7 -4
  66. package/dist/cli/init/interactive.js +79 -27
  67. package/dist/cli/init/interactive.js.map +1 -1
  68. package/dist/cli/init/profiles.d.ts +2 -2
  69. package/dist/cli/init/profiles.js +32 -1
  70. package/dist/cli/init/profiles.js.map +1 -1
  71. package/dist/cli/init/templates.d.ts +2 -1
  72. package/dist/cli/init/templates.js +136 -24
  73. package/dist/cli/init/templates.js.map +1 -1
  74. package/dist/cli/pack/approve-escape.js +69 -8
  75. package/dist/cli/pack/approve-escape.js.map +1 -1
  76. package/dist/cli/pack/hook-branch-protection.js +1 -1
  77. package/dist/cli/pack/hook-branch-protection.js.map +1 -1
  78. package/dist/cli/pack/hook-codex-pre-tool-use.js +27 -1
  79. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
  80. package/dist/cli/pack/hook-post-merge-gate.js +44 -26
  81. package/dist/cli/pack/hook-post-merge-gate.js.map +1 -1
  82. package/dist/cli/pack/hook-pre-tool-use.js +91 -4
  83. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  84. package/dist/cli/pack/hook-solution-acceptance.js +19 -2
  85. package/dist/cli/pack/hook-solution-acceptance.js.map +1 -1
  86. package/dist/cli/pack/understanding-report-schema-hint.d.ts +13 -0
  87. package/dist/cli/pack/understanding-report-schema-hint.js +52 -3
  88. package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
  89. package/dist/cli/pause/index.js +1 -1
  90. package/dist/cli/pause/index.js.map +1 -1
  91. package/dist/cli/policy/intercept.d.ts +148 -0
  92. package/dist/cli/policy/intercept.js +538 -79
  93. package/dist/cli/policy/intercept.js.map +1 -1
  94. package/dist/cli/session-start/index.d.ts +12 -3
  95. package/dist/cli/session-start/index.js +67 -11
  96. package/dist/cli/session-start/index.js.map +1 -1
  97. package/dist/cli/session-start/stale-base-check.d.ts +71 -0
  98. package/dist/cli/session-start/stale-base-check.js +379 -0
  99. package/dist/cli/session-start/stale-base-check.js.map +1 -0
  100. package/dist/cli/session-start/toolchain-parity.d.ts +142 -8
  101. package/dist/cli/session-start/toolchain-parity.js +244 -36
  102. package/dist/cli/session-start/toolchain-parity.js.map +1 -1
  103. package/dist/cli/smoke/assertions.d.ts +9 -2
  104. package/dist/cli/smoke/assertions.js +9 -2
  105. package/dist/cli/smoke/assertions.js.map +1 -1
  106. package/dist/cli/uninstall/index.js +18 -2
  107. package/dist/cli/uninstall/index.js.map +1 -1
  108. package/dist/cli/validate/checks.d.ts +3 -1
  109. package/dist/cli/validate/checks.js +298 -3
  110. package/dist/cli/validate/checks.js.map +1 -1
  111. package/dist/io/claude-mcp.d.ts +122 -1
  112. package/dist/io/claude-mcp.js +214 -12
  113. package/dist/io/claude-mcp.js.map +1 -1
  114. package/dist/io/duration.js.map +1 -0
  115. package/dist/io/expand-home.js.map +1 -0
  116. package/dist/io/extract.js.map +1 -0
  117. package/dist/io/harness-lock.js +1 -1
  118. package/dist/io/harness-lock.js.map +1 -1
  119. package/dist/{runtime → io}/ledger-record.d.ts +1 -1
  120. package/dist/io/ledger-record.js.map +1 -0
  121. package/dist/policies/index.d.ts +2 -2
  122. package/dist/policies/index.js +2 -2
  123. package/dist/policies/index.js.map +1 -1
  124. package/dist/policies/ledger-client.js +10 -6
  125. package/dist/policies/ledger-client.js.map +1 -1
  126. package/dist/policies/producers.js +1 -1
  127. package/dist/policies/producers.js.map +1 -1
  128. package/dist/policies/requires.js +2 -2
  129. package/dist/policies/requires.js.map +1 -1
  130. package/dist/policy-packs/builtin/branch-protection.js +28 -3
  131. package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
  132. package/dist/policy-packs/builtin/post-merge-gate-runtime.d.ts +90 -10
  133. package/dist/policy-packs/builtin/post-merge-gate-runtime.js +112 -16
  134. package/dist/policy-packs/builtin/post-merge-gate-runtime.js.map +1 -1
  135. package/dist/policy-packs/builtin/post-merge-gate.js +84 -31
  136. package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -1
  137. package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +152 -8
  138. package/dist/policy-packs/builtin/solution-acceptance-runtime.js +260 -13
  139. package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
  140. package/dist/policy-packs/builtin/solution-acceptance.js +17 -3
  141. package/dist/policy-packs/builtin/solution-acceptance.js.map +1 -1
  142. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.d.ts +25 -0
  143. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js +89 -0
  144. package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js.map +1 -0
  145. package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +13 -0
  146. package/dist/policy-packs/builtin/understanding-before-execution/index.js +39 -0
  147. package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -0
  148. package/dist/policy-packs/builtin/understanding-before-execution/ledger.d.ts +25 -0
  149. package/dist/policy-packs/builtin/understanding-before-execution/ledger.js +56 -0
  150. package/dist/policy-packs/builtin/understanding-before-execution/ledger.js.map +1 -0
  151. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.d.ts +20 -0
  152. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +79 -0
  153. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +1 -0
  154. package/dist/policy-packs/builtin/understanding-before-execution/markers.d.ts +103 -0
  155. package/dist/policy-packs/builtin/understanding-before-execution/markers.js +203 -0
  156. package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +1 -0
  157. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +177 -0
  158. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +309 -0
  159. package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -0
  160. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.d.ts +106 -0
  161. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +209 -0
  162. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +1 -0
  163. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.d.ts +78 -0
  164. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js +185 -0
  165. package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js.map +1 -0
  166. package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +1 -536
  167. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +9 -1095
  168. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
  169. package/dist/policy-packs/builtin/understanding-before-execution.d.ts +109 -1
  170. package/dist/policy-packs/builtin/understanding-before-execution.js +183 -15
  171. package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
  172. package/dist/policy-packs/registry.d.ts +11 -0
  173. package/dist/policy-packs/registry.js +1 -1
  174. package/dist/policy-packs/registry.js.map +1 -1
  175. package/dist/policy-packs/runtime.d.ts +1 -1
  176. package/dist/policy-packs/runtime.js +10 -1
  177. package/dist/policy-packs/runtime.js.map +1 -1
  178. package/dist/runtime/agent-facing.js +1 -1
  179. package/dist/runtime/agent-facing.js.map +1 -1
  180. package/dist/runtime/bash-match-facts.d.ts +9 -7
  181. package/dist/runtime/bash-match-facts.js +9 -7
  182. package/dist/runtime/bash-match-facts.js.map +1 -1
  183. package/dist/runtime/bash-match-registry.d.ts +75 -38
  184. package/dist/runtime/bash-match-registry.js +279 -232
  185. package/dist/runtime/bash-match-registry.js.map +1 -1
  186. package/dist/runtime/bash-prefix-parse.d.ts +7 -0
  187. package/dist/runtime/bash-prefix-parse.js +205 -17
  188. package/dist/runtime/bash-prefix-parse.js.map +1 -1
  189. package/dist/runtime/command-normalize.d.ts +429 -4
  190. package/dist/runtime/command-normalize.js +1146 -67
  191. package/dist/runtime/command-normalize.js.map +1 -1
  192. package/dist/runtime/hermetic-spawn-guard.js +6 -0
  193. package/dist/runtime/hermetic-spawn-guard.js.map +1 -1
  194. package/dist/runtime/index.d.ts +2 -2
  195. package/dist/runtime/index.js +2 -2
  196. package/dist/runtime/index.js.map +1 -1
  197. package/dist/runtime/intercept.d.ts +247 -4
  198. package/dist/runtime/intercept.js +553 -61
  199. package/dist/runtime/intercept.js.map +1 -1
  200. package/dist/runtime/ledger-add.js +1 -1
  201. package/dist/runtime/ledger-add.js.map +1 -1
  202. package/dist/runtime/read-only-bash.js +356 -26
  203. package/dist/runtime/read-only-bash.js.map +1 -1
  204. package/dist/runtime/shell-word.d.ts +21 -0
  205. package/dist/runtime/shell-word.js +242 -0
  206. package/dist/runtime/shell-word.js.map +1 -0
  207. package/dist/schema/doctor.d.ts +25 -0
  208. package/dist/schema/doctor.js +23 -0
  209. package/dist/schema/doctor.js.map +1 -0
  210. package/dist/schema/extract.js +1 -1
  211. package/dist/schema/extract.js.map +1 -1
  212. package/dist/schema/index.d.ts +76 -0
  213. package/dist/schema/index.js +9 -0
  214. package/dist/schema/index.js.map +1 -1
  215. package/dist/schema/requires.js +2 -2
  216. package/dist/schema/requires.js.map +1 -1
  217. package/dist/schema/risk.d.ts +7 -0
  218. package/dist/schema/risk.js +23 -0
  219. package/dist/schema/risk.js.map +1 -1
  220. package/dist/schema/stale-base-check.d.ts +18 -0
  221. package/dist/schema/stale-base-check.js +37 -0
  222. package/dist/schema/stale-base-check.js.map +1 -0
  223. package/dist/schema/toolchain-parity.d.ts +3 -0
  224. package/dist/schema/toolchain-parity.js +13 -0
  225. package/dist/schema/toolchain-parity.js.map +1 -1
  226. package/package.json +5 -2
  227. package/dist/policies/duration.js.map +0 -1
  228. package/dist/policies/extract.js.map +0 -1
  229. package/dist/runtime/expand-home.js.map +0 -1
  230. package/dist/runtime/ledger-record.js.map +0 -1
  231. /package/dist/{policies → io}/duration.d.ts +0 -0
  232. /package/dist/{policies → io}/duration.js +0 -0
  233. /package/dist/{runtime → io}/expand-home.d.ts +0 -0
  234. /package/dist/{runtime → io}/expand-home.js +0 -0
  235. /package/dist/{policies → io}/extract.d.ts +0 -0
  236. /package/dist/{policies → io}/extract.js +0 -0
  237. /package/dist/{runtime → io}/ledger-record.js +0 -0
@@ -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;
@@ -0,0 +1,203 @@
1
+ // Session-scoped approval marker file (filesystem source of the
2
+ // two-source approval check), split out of the former monolithic
3
+ // understanding-before-execution-runtime.ts (structural concentration
4
+ // slice 2, agent-tasks 348a4d42). Pure move: see
5
+ // src/policy-packs/builtin/understanding-before-execution/index.ts for
6
+ // the re-exported public surface.
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ import { atomicWriteFile } from "../../../io/atomic-write.js";
10
+ import { readRegularFileRejectingSymlink } from "../../../io/read-regular-file.js";
11
+ import { signMarker, verifyMarkerSignature } from "../../../runtime/approval-signing.js";
12
+ import { rejectMalformedSessionId } from "../../../runtime/reject-malformed-session-id.js";
13
+ import { safeJsonParse } from "./persisted-reports.js";
14
+ export const APPROVAL_MARKER_DIRNAME = ".approvals";
15
+ /** Filesystem path of the per-session approval marker. */
16
+ export function approvalMarkerPathFor(generatedDir, sessionId) {
17
+ rejectMalformedSessionId(sessionId);
18
+ return path.join(generatedDir, APPROVAL_MARKER_DIRNAME, sessionId);
19
+ }
20
+ /**
21
+ * Operator-side: write the marker file the gate consults. Atomic so a
22
+ * crash mid-write cannot leave a half-empty file the gate would accept
23
+ * as approved. Caller is `harness approve understanding`, which the
24
+ * operator runs from their un-hooked shell; if the agent could call
25
+ * this path the gate's value would collapse, so it lives behind the
26
+ * approve CLI rather than as a generally importable verb.
27
+ *
28
+ * The marker is HMAC-signed (harness/f9485cc7) over (sessionId,
29
+ * approvedAt, approvedBy, reportContentHash) using an operator-side key
30
+ * lazily generated at `<generatedDir>/.approval-signing.key` — see
31
+ * `src/runtime/approval-signing.ts` for the key-management contract and
32
+ * the honest trust model. Mode 0600 on the marker file itself, best-
33
+ * effort (matches the signing key's own permission convention); this is
34
+ * defense-in-depth alongside the signature, not a substitute for it.
35
+ */
36
+ export function writeApprovalMarker(generatedDir, sessionId, marker) {
37
+ const filePath = approvalMarkerPathFor(generatedDir, sessionId);
38
+ const signed = signMarker(generatedDir, sessionId, marker);
39
+ atomicWriteFile(filePath, `${JSON.stringify(signed, null, 2)}\n`, { mode: 0o600 });
40
+ return filePath;
41
+ }
42
+ /**
43
+ * Gate-side: is the per-session marker file present, readable, AND
44
+ * validly signed? (harness/f9485cc7 — this replaced the earlier
45
+ * "existence is enough" contract: a marker's mere presence used to
46
+ * satisfy the gate even with a malformed or corrupted body, on the
47
+ * theory that Edit/Write/Bash being gated meant only the operator could
48
+ * ever create the file. That theory holds only as long as no OTHER
49
+ * write primitive reaches `harness.generated/` — exactly the invariant
50
+ * this signing scheme no longer has to trust blindly.) A marker with no
51
+ * `signature`, a wrong `alg`, or a signature that fails to verify is
52
+ * REJECTED — see `MarkerCheck.forged` — with the SAME `matched: false`
53
+ * outcome as no marker at all, but a distinct, forgery-specific detail
54
+ * string so callers can tell the two apart for audit.
55
+ *
56
+ * `opts.maxAgeMs` (agent-tasks/d8ee60ca): when set, a validly-signed
57
+ * marker whose `approvedAt` is older than the cutoff returns
58
+ * `matched:false` with an "expired" detail so the agent gets the same
59
+ * "no approval" UX as a never-approved session and must re-approve.
60
+ * Expiry is only evaluated AFTER signature verification succeeds — an
61
+ * unsigned/forged marker is rejected outright, never "expired".
62
+ */
63
+ export function checkApprovalMarker(generatedDir, sessionId, opts = {}) {
64
+ // Construct the path defensively: a malformed sessionId must fail CLOSED
65
+ // here (no valid marker, so the gate blocks and demands approval), not
66
+ // throw out of the gate hook, which the top-level handler turns into a
67
+ // non-blocking exit that would let the gated tool proceed.
68
+ let filePath;
69
+ try {
70
+ filePath = approvalMarkerPathFor(generatedDir, sessionId);
71
+ }
72
+ catch (err) {
73
+ return {
74
+ matched: false,
75
+ detail: `invalid sessionId for approval marker: ${err instanceof Error ? err.message : String(err)}`,
76
+ marker: null,
77
+ expired: false,
78
+ forged: false,
79
+ };
80
+ }
81
+ // Shared symlink-rejecting read (src/io/read-regular-file.ts): the lstat
82
+ // reject is defense-in-depth against a planted symlink
83
+ // (agent-tasks/d39f160e); each failure mode keeps its distinct detail.
84
+ const read = readRegularFileRejectingSymlink(filePath);
85
+ if (read.kind === "missing") {
86
+ return {
87
+ matched: false,
88
+ detail: `no approval marker at ${filePath}`,
89
+ marker: null,
90
+ expired: false,
91
+ forged: false,
92
+ };
93
+ }
94
+ if (read.kind === "symlink") {
95
+ return {
96
+ matched: false,
97
+ detail: `approval marker is a symlink, refusing for safety: ${filePath}`,
98
+ marker: null,
99
+ expired: false,
100
+ forged: false,
101
+ };
102
+ }
103
+ if (read.kind === "not-regular") {
104
+ return {
105
+ matched: false,
106
+ detail: `approval marker path is not a regular file: ${filePath}`,
107
+ marker: null,
108
+ expired: false,
109
+ forged: false,
110
+ };
111
+ }
112
+ if (read.kind === "unreadable") {
113
+ // A genuine I/O error reading an existing regular file (permissions
114
+ // changed mid-flight, etc.) — not evidence of tampering, but its
115
+ // signature cannot be proven either, so fail closed. Kept distinct
116
+ // from `forged` (no claim of an active forgery attempt).
117
+ return {
118
+ matched: false,
119
+ detail: `approval marker at ${filePath} exists but could not be read (I/O error); treating as unapproved since its signature cannot be verified`,
120
+ marker: null,
121
+ expired: false,
122
+ forged: false,
123
+ };
124
+ }
125
+ // read.kind === "ok"
126
+ const parsed = safeJsonParse(read.content);
127
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
128
+ return {
129
+ matched: false,
130
+ detail: `forged/unsigned marker rejected: marker body at ${filePath} is not a JSON object`,
131
+ marker: null,
132
+ expired: false,
133
+ forged: true,
134
+ };
135
+ }
136
+ const obj = parsed;
137
+ const verification = verifyMarkerSignature(generatedDir, sessionId, obj);
138
+ if (!verification.ok) {
139
+ if (verification.kind === "key-unavailable") {
140
+ // Fail-closed I/O problem (permission error / disk issue reading or
141
+ // creating the signing key), NOT evidence of an active forgery
142
+ // attempt — a marker written by the operator with a perfectly valid
143
+ // signature would also fail this exact check if the key file became
144
+ // unreadable. Kept distinct from `forged` (review LOW 1,
145
+ // harness/f9485cc7) so a broken key file doesn't read as an attack
146
+ // in audit output, mirroring the `read.kind === "unreadable"` branch
147
+ // above.
148
+ return {
149
+ matched: false,
150
+ detail: `approval marker at ${filePath} could not be verified: ${verification.reason}; treating as unapproved`,
151
+ marker: null,
152
+ expired: false,
153
+ forged: false,
154
+ };
155
+ }
156
+ return {
157
+ matched: false,
158
+ detail: `forged/unsigned marker rejected: ${verification.reason} (${filePath})`,
159
+ marker: null,
160
+ expired: false,
161
+ forged: true,
162
+ };
163
+ }
164
+ const approvedAt = obj["approvedAt"];
165
+ const approvedBy = obj["approvedBy"];
166
+ const reportContentHash = typeof obj["reportContentHash"] === "string" ? obj["reportContentHash"] : null;
167
+ const marker = { approvedAt, approvedBy, reportContentHash };
168
+ if (opts.maxAgeMs !== undefined) {
169
+ const approvedAtMs = Date.parse(approvedAt);
170
+ if (Number.isFinite(approvedAtMs)) {
171
+ const nowMs = (opts.now ?? new Date()).getTime();
172
+ const ageMs = nowMs - approvedAtMs;
173
+ if (ageMs > opts.maxAgeMs) {
174
+ const ageMin = Math.round(ageMs / 60_000);
175
+ const maxMin = Math.round(opts.maxAgeMs / 60_000);
176
+ return {
177
+ matched: false,
178
+ detail: `approval marker ${path.basename(filePath)} expired: age ${ageMin}m > max ${maxMin}m (approved at ${approvedAt})`,
179
+ marker,
180
+ expired: true,
181
+ forged: false,
182
+ };
183
+ }
184
+ }
185
+ }
186
+ return {
187
+ matched: true,
188
+ detail: `approved via marker ${path.basename(filePath)}: approved at ${approvedAt} by ${approvedBy}, signature verified`,
189
+ marker,
190
+ expired: false,
191
+ forged: false,
192
+ };
193
+ }
194
+ /** Clear the per-session marker (used by `harness approve --revoke` and tests). */
195
+ export function clearApprovalMarker(generatedDir, sessionId) {
196
+ try {
197
+ fs.rmSync(approvalMarkerPathFor(generatedDir, sessionId));
198
+ }
199
+ catch {
200
+ /* already gone */
201
+ }
202
+ }
203
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/markers.ts"],"names":[],"mappings":"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"}