@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.
Files changed (234) hide show
  1. package/CHANGELOG.md +1245 -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/apply/next-steps.d.ts +20 -0
  22. package/dist/cli/apply/next-steps.js +60 -0
  23. package/dist/cli/apply/next-steps.js.map +1 -1
  24. package/dist/cli/approve/stdin-report.d.ts +15 -0
  25. package/dist/cli/approve/stdin-report.js +1 -1
  26. package/dist/cli/approve/stdin-report.js.map +1 -1
  27. package/dist/cli/approve/understanding.d.ts +75 -0
  28. package/dist/cli/approve/understanding.js +150 -2
  29. package/dist/cli/approve/understanding.js.map +1 -1
  30. package/dist/cli/audit.d.ts +1 -1
  31. package/dist/cli/audit.js +3 -2
  32. package/dist/cli/audit.js.map +1 -1
  33. package/dist/cli/doctor/claude-mcp.d.ts +19 -1
  34. package/dist/cli/doctor/claude-mcp.js +36 -10
  35. package/dist/cli/doctor/claude-mcp.js.map +1 -1
  36. package/dist/cli/doctor/codex.d.ts +3 -1
  37. package/dist/cli/doctor/codex.js +6 -9
  38. package/dist/cli/doctor/codex.js.map +1 -1
  39. package/dist/cli/doctor/format.js +94 -8
  40. package/dist/cli/doctor/format.js.map +1 -1
  41. package/dist/cli/doctor/index.d.ts +23 -0
  42. package/dist/cli/doctor/index.js +106 -2
  43. package/dist/cli/doctor/index.js.map +1 -1
  44. package/dist/cli/doctor/opencode.d.ts +24 -0
  45. package/dist/cli/doctor/opencode.js +107 -0
  46. package/dist/cli/doctor/opencode.js.map +1 -0
  47. package/dist/cli/doctor/target-checks.d.ts +11 -0
  48. package/dist/cli/doctor/target-checks.js +25 -0
  49. package/dist/cli/doctor/target-checks.js.map +1 -0
  50. package/dist/cli/doctor/toolchain-parity.d.ts +63 -0
  51. package/dist/cli/doctor/toolchain-parity.js +154 -0
  52. package/dist/cli/doctor/toolchain-parity.js.map +1 -0
  53. package/dist/cli/doctor/types.d.ts +85 -1
  54. package/dist/cli/doctor/types.js +1 -1
  55. package/dist/cli/doctor/types.js.map +1 -1
  56. package/dist/cli/doctor/understanding-mode-env.d.ts +39 -0
  57. package/dist/cli/doctor/understanding-mode-env.js +83 -0
  58. package/dist/cli/doctor/understanding-mode-env.js.map +1 -0
  59. package/dist/cli/dry-run.js +32 -17
  60. package/dist/cli/dry-run.js.map +1 -1
  61. package/dist/cli/explain.d.ts +1 -1
  62. package/dist/cli/explain.js +1 -1
  63. package/dist/cli/explain.js.map +1 -1
  64. package/dist/cli/index.js +64 -5
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/init/composer.js +36 -13
  67. package/dist/cli/init/composer.js.map +1 -1
  68. package/dist/cli/init/interactive.d.ts +7 -4
  69. package/dist/cli/init/interactive.js +79 -27
  70. package/dist/cli/init/interactive.js.map +1 -1
  71. package/dist/cli/init/profiles.d.ts +2 -2
  72. package/dist/cli/init/profiles.js +32 -1
  73. package/dist/cli/init/profiles.js.map +1 -1
  74. package/dist/cli/init/templates.d.ts +2 -1
  75. package/dist/cli/init/templates.js +130 -20
  76. package/dist/cli/init/templates.js.map +1 -1
  77. package/dist/cli/pack/approve-escape.js +69 -8
  78. package/dist/cli/pack/approve-escape.js.map +1 -1
  79. package/dist/cli/pack/hook-branch-protection.js +1 -1
  80. package/dist/cli/pack/hook-branch-protection.js.map +1 -1
  81. package/dist/cli/pack/hook-codex-pre-tool-use.js +27 -1
  82. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
  83. package/dist/cli/pack/hook-post-merge-gate.js +1 -1
  84. package/dist/cli/pack/hook-post-merge-gate.js.map +1 -1
  85. package/dist/cli/pack/hook-pre-tool-use.js +91 -4
  86. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  87. package/dist/cli/pack/hook-solution-acceptance.js +19 -2
  88. package/dist/cli/pack/hook-solution-acceptance.js.map +1 -1
  89. package/dist/cli/pack/understanding-report-schema-hint.d.ts +13 -0
  90. package/dist/cli/pack/understanding-report-schema-hint.js +52 -3
  91. package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
  92. package/dist/cli/pause/index.js +1 -1
  93. package/dist/cli/pause/index.js.map +1 -1
  94. package/dist/cli/policy/intercept.d.ts +148 -0
  95. package/dist/cli/policy/intercept.js +379 -48
  96. package/dist/cli/policy/intercept.js.map +1 -1
  97. package/dist/cli/session-start/index.d.ts +12 -3
  98. package/dist/cli/session-start/index.js +67 -11
  99. package/dist/cli/session-start/index.js.map +1 -1
  100. package/dist/cli/session-start/stale-base-check.d.ts +71 -0
  101. package/dist/cli/session-start/stale-base-check.js +379 -0
  102. package/dist/cli/session-start/stale-base-check.js.map +1 -0
  103. package/dist/cli/session-start/toolchain-parity.d.ts +142 -8
  104. package/dist/cli/session-start/toolchain-parity.js +244 -36
  105. package/dist/cli/session-start/toolchain-parity.js.map +1 -1
  106. package/dist/cli/smoke/assertions.d.ts +9 -2
  107. package/dist/cli/smoke/assertions.js +9 -2
  108. package/dist/cli/smoke/assertions.js.map +1 -1
  109. package/dist/cli/uninstall/index.js +18 -2
  110. package/dist/cli/uninstall/index.js.map +1 -1
  111. package/dist/cli/validate/checks.d.ts +3 -1
  112. package/dist/cli/validate/checks.js +298 -3
  113. package/dist/cli/validate/checks.js.map +1 -1
  114. package/dist/io/claude-mcp.d.ts +122 -1
  115. package/dist/io/claude-mcp.js +214 -12
  116. package/dist/io/claude-mcp.js.map +1 -1
  117. package/dist/io/duration.js.map +1 -0
  118. package/dist/io/expand-home.js.map +1 -0
  119. package/dist/io/extract.js.map +1 -0
  120. package/dist/io/harness-lock.js +1 -1
  121. package/dist/io/harness-lock.js.map +1 -1
  122. package/dist/{runtime → io}/ledger-record.d.ts +1 -1
  123. package/dist/io/ledger-record.js.map +1 -0
  124. package/dist/policies/index.d.ts +2 -2
  125. package/dist/policies/index.js +2 -2
  126. package/dist/policies/index.js.map +1 -1
  127. package/dist/policies/ledger-client.js +10 -6
  128. package/dist/policies/ledger-client.js.map +1 -1
  129. package/dist/policies/producers.js +1 -1
  130. package/dist/policies/producers.js.map +1 -1
  131. package/dist/policies/requires.js +2 -2
  132. package/dist/policies/requires.js.map +1 -1
  133. package/dist/policy-packs/builtin/branch-protection.js +28 -3
  134. package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
  135. package/dist/policy-packs/builtin/post-merge-gate.js +45 -9
  136. package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -1
  137. package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +139 -8
  138. package/dist/policy-packs/builtin/solution-acceptance-runtime.js +246 -12
  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 +112 -0
  190. package/dist/runtime/command-normalize.js +411 -38
  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 +78 -3
  198. package/dist/runtime/intercept.js +151 -39
  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 +303 -24
  203. package/dist/runtime/read-only-bash.js.map +1 -1
  204. package/dist/schema/doctor.d.ts +25 -0
  205. package/dist/schema/doctor.js +23 -0
  206. package/dist/schema/doctor.js.map +1 -0
  207. package/dist/schema/extract.js +1 -1
  208. package/dist/schema/extract.js.map +1 -1
  209. package/dist/schema/index.d.ts +76 -0
  210. package/dist/schema/index.js +9 -0
  211. package/dist/schema/index.js.map +1 -1
  212. package/dist/schema/requires.js +2 -2
  213. package/dist/schema/requires.js.map +1 -1
  214. package/dist/schema/risk.d.ts +7 -0
  215. package/dist/schema/risk.js +23 -0
  216. package/dist/schema/risk.js.map +1 -1
  217. package/dist/schema/stale-base-check.d.ts +18 -0
  218. package/dist/schema/stale-base-check.js +37 -0
  219. package/dist/schema/stale-base-check.js.map +1 -0
  220. package/dist/schema/toolchain-parity.d.ts +3 -0
  221. package/dist/schema/toolchain-parity.js +13 -0
  222. package/dist/schema/toolchain-parity.js.map +1 -1
  223. package/package.json +3 -2
  224. package/dist/policies/duration.js.map +0 -1
  225. package/dist/policies/extract.js.map +0 -1
  226. package/dist/runtime/expand-home.js.map +0 -1
  227. package/dist/runtime/ledger-record.js.map +0 -1
  228. /package/dist/{policies → io}/duration.d.ts +0 -0
  229. /package/dist/{policies → io}/duration.js +0 -0
  230. /package/dist/{runtime → io}/expand-home.d.ts +0 -0
  231. /package/dist/{runtime → io}/expand-home.js +0 -0
  232. /package/dist/{policies → io}/extract.d.ts +0 -0
  233. /package/dist/{policies → io}/extract.js +0 -0
  234. /package/dist/{runtime → io}/ledger-record.js +0 -0
@@ -0,0 +1,78 @@
1
+ import { type ApprovalMarker, type CheckApprovalMarkerOptions, type MarkerCheck } from "./markers.js";
2
+ export declare const APPROVAL_MARKER_TASK_PREFIX = "task-";
3
+ /** Filesystem path of a per-task approval marker. */
4
+ export declare function taskApprovalMarkerPathFor(generatedDir: string, taskId: string): string;
5
+ /**
6
+ * Operator-side: write a task-scoped marker file. Atomic. Caller is
7
+ * `harness approve understanding --task <id>`. The session marker is
8
+ * written separately by the same caller for back-compat.
9
+ *
10
+ * Signed the same way as `writeApprovalMarker` (harness/f9485cc7), using
11
+ * `task-<id>` (the exact string `checkActiveClaimApprovalMarker` looks
12
+ * up) as the signed markerId — so a validly-signed task marker cannot be
13
+ * copied onto a different task id (or the session marker's id) and still
14
+ * verify.
15
+ */
16
+ export declare function writeTaskApprovalMarker(generatedDir: string, taskId: string, marker: ApprovalMarker): string;
17
+ /**
18
+ * Gate-side: resolve the active agent-tasks claim (via `active-claim`)
19
+ * and check ONLY that task's approval marker. When no active claim is
20
+ * recorded, this returns `matched:false` so the caller falls through
21
+ * to the session marker — preserving the legacy contract for solo /
22
+ * non-agent-tasks workflows that never call `task_start`.
23
+ *
24
+ * Replaces the v1 "any task marker satisfies the gate" behaviour
25
+ * (PR #198): a stale approval from a different, already-completed task
26
+ * was silently authorising every Edit/Write/Bash in the next session
27
+ * because the scan returned the first existing marker regardless of
28
+ * which task the agent had actually claimed.
29
+ *
30
+ * Same safety filters as `checkApprovalMarker` (signature verification,
31
+ * symlink rejection, optional freshness via `maxAgeMs`); the only
32
+ * difference is the filename suffix derived from `active-claim`.
33
+ */
34
+ export declare function checkActiveClaimApprovalMarker(generatedDir: string, opts?: CheckApprovalMarkerOptions): MarkerCheck;
35
+ export interface OperatorMarkerApproval {
36
+ matched: boolean;
37
+ /** Which marker satisfied the gate; null when neither matched. */
38
+ source: "task" | "session" | null;
39
+ /** Detail of the decisive check (the match, or the session-scoped miss). */
40
+ detail: string;
41
+ /** Task-scoped check detail, for callers that trace the fall-through. */
42
+ taskCheckDetail: string;
43
+ /**
44
+ * True when EITHER the task-scoped or the session-scoped marker
45
+ * existed but aged past `approval_lifecycle.max_age` (task 6e888423).
46
+ * False when `matched` is true, and false when a marker was simply
47
+ * absent (never approved, or cleared by a task-completion boundary
48
+ * tool) — distinguishing "this identity had a real approval that
49
+ * merely expired" from "this identity was never approved" / "a new
50
+ * task just started". PreToolUse hooks use this (never on its own —
51
+ * always alongside `isRecoveryGitCommit`) to decide whether a bare
52
+ * recovery `git commit` may proceed without a fresh Understanding
53
+ * Report; see src/runtime/recovery-git-commit.ts.
54
+ */
55
+ expired: boolean;
56
+ /**
57
+ * True when EITHER the task-scoped or the session-scoped marker
58
+ * existed but FAILED signature verification (harness/f9485cc7) —
59
+ * missing/invalid signature, wrong `alg`, or tampered payload. Distinct
60
+ * from a marker simply being absent, so a caller can log a forgery
61
+ * attempt distinctly from the routine "not approved yet" case.
62
+ */
63
+ forged: boolean;
64
+ }
65
+ /**
66
+ * Shared marker resolution for the Claude and Codex understanding-gate
67
+ * PreToolUse hooks (task e7c2ec3c): parse `approval_lifecycle` from the
68
+ * pack config, consult the task-scoped (active-claim) marker first and
69
+ * the session-scoped marker second, both under the same TTL. One code
70
+ * path on purpose — the Codex hook previously called the bare session
71
+ * check, so `max_age` and task-scoping silently applied only to Claude
72
+ * sessions.
73
+ */
74
+ export declare function checkOperatorApprovalMarkers(generatedDir: string, sessionId: string, packConfig: unknown, stderr?: {
75
+ write: (s: string) => void;
76
+ } | null): OperatorMarkerApproval;
77
+ /** Clear a specific task-scoped marker. Used by the post-tool-use hook. */
78
+ export declare function clearTaskApprovalMarker(generatedDir: string, taskId: string): void;
@@ -0,0 +1,185 @@
1
+ // Task-scoped approval markers (harness/1ee26e77), split out of the
2
+ // former 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
+ import { signMarker } from "../../../runtime/approval-signing.js";
10
+ import { readActiveClaim } from "./active-claim.js";
11
+ import { parseApprovalLifecycle } from "./lifecycle.js";
12
+ import { APPROVAL_MARKER_DIRNAME, checkApprovalMarker, } from "./markers.js";
13
+ // Task-scoped approval markers (harness/1ee26e77). When the operator
14
+ // passes `--task <id>` to `harness approve understanding`, a second
15
+ // marker file is written next to the session marker, keyed by the
16
+ // agent-tasks task id and prefixed `task-` so a directory scan can
17
+ // distinguish them from session-keyed markers (UUIDs do not start with
18
+ // `task-`). Either marker satisfies the gate; the session marker
19
+ // remains for back-compat with operators who don't pass --task and for
20
+ // non-agent-tasks workflows.
21
+ //
22
+ // Why task-scope: a session can span many distinct tasks; the operator's
23
+ // Understanding Report for task A doesn't transfer trust to task B
24
+ // (different surface, different acceptance criteria). Without per-task
25
+ // markers a multi-task session re-uses the first task's approval for
26
+ // every subsequent task. The expire_on_tool_match PostToolUse hook
27
+ // already deletes the session marker on `task_finish`, but if delivery
28
+ // is unreliable (e.g. PostToolUse skipped for an MCP tool) the marker
29
+ // silently persists. A task-keyed marker side-steps that delivery
30
+ // concern because the next task's id is different even if the previous
31
+ // marker file outlives its scope.
32
+ export const APPROVAL_MARKER_TASK_PREFIX = "task-";
33
+ /**
34
+ * Reject taskIds that would escape the approvals/ namespace via path
35
+ * traversal or directory separators. The operator's --task flag value
36
+ * lands here verbatim; an accidental shell-expanded `..` or `/` would
37
+ * otherwise write to a sibling directory. This is defensive — the
38
+ * caller is the operator's own shell — but pins the trust boundary.
39
+ */
40
+ function rejectMalformedTaskId(taskId) {
41
+ if (taskId.length === 0) {
42
+ throw new Error("taskId is empty");
43
+ }
44
+ if (taskId.includes("/") || taskId.includes("\\") || taskId.includes("..")) {
45
+ throw new Error(`taskId contains path-separator or traversal characters: ${JSON.stringify(taskId)}`);
46
+ }
47
+ }
48
+ /** Filesystem path of a per-task approval marker. */
49
+ export function taskApprovalMarkerPathFor(generatedDir, taskId) {
50
+ rejectMalformedTaskId(taskId);
51
+ return path.join(generatedDir, APPROVAL_MARKER_DIRNAME, `${APPROVAL_MARKER_TASK_PREFIX}${taskId}`);
52
+ }
53
+ /**
54
+ * Operator-side: write a task-scoped marker file. Atomic. Caller is
55
+ * `harness approve understanding --task <id>`. The session marker is
56
+ * written separately by the same caller for back-compat.
57
+ *
58
+ * Signed the same way as `writeApprovalMarker` (harness/f9485cc7), using
59
+ * `task-<id>` (the exact string `checkActiveClaimApprovalMarker` looks
60
+ * up) as the signed markerId — so a validly-signed task marker cannot be
61
+ * copied onto a different task id (or the session marker's id) and still
62
+ * verify.
63
+ */
64
+ export function writeTaskApprovalMarker(generatedDir, taskId, marker) {
65
+ const filePath = taskApprovalMarkerPathFor(generatedDir, taskId);
66
+ const markerId = `${APPROVAL_MARKER_TASK_PREFIX}${taskId}`;
67
+ const signed = signMarker(generatedDir, markerId, marker);
68
+ atomicWriteFile(filePath, `${JSON.stringify(signed, null, 2)}\n`, { mode: 0o600 });
69
+ return filePath;
70
+ }
71
+ /**
72
+ * Gate-side: resolve the active agent-tasks claim (via `active-claim`)
73
+ * and check ONLY that task's approval marker. When no active claim is
74
+ * recorded, this returns `matched:false` so the caller falls through
75
+ * to the session marker — preserving the legacy contract for solo /
76
+ * non-agent-tasks workflows that never call `task_start`.
77
+ *
78
+ * Replaces the v1 "any task marker satisfies the gate" behaviour
79
+ * (PR #198): a stale approval from a different, already-completed task
80
+ * was silently authorising every Edit/Write/Bash in the next session
81
+ * because the scan returned the first existing marker regardless of
82
+ * which task the agent had actually claimed.
83
+ *
84
+ * Same safety filters as `checkApprovalMarker` (signature verification,
85
+ * symlink rejection, optional freshness via `maxAgeMs`); the only
86
+ * difference is the filename suffix derived from `active-claim`.
87
+ */
88
+ export function checkActiveClaimApprovalMarker(generatedDir, opts = {}) {
89
+ const claim = readActiveClaim(generatedDir);
90
+ if (claim === null) {
91
+ return {
92
+ matched: false,
93
+ detail: `no active-claim recorded; task-scoped check skipped`,
94
+ marker: null,
95
+ expired: false,
96
+ forged: false,
97
+ };
98
+ }
99
+ const markerName = `${APPROVAL_MARKER_TASK_PREFIX}${claim}`;
100
+ const check = checkApprovalMarker(generatedDir, markerName, opts);
101
+ if (check.matched) {
102
+ return {
103
+ matched: true,
104
+ detail: `task-scoped marker for active-claim ${claim}: ${check.detail}`,
105
+ marker: check.marker,
106
+ expired: false,
107
+ forged: false,
108
+ };
109
+ }
110
+ return {
111
+ matched: false,
112
+ detail: `active-claim ${claim} has no fresh task marker (${check.detail})`,
113
+ marker: null,
114
+ expired: check.expired,
115
+ forged: check.forged,
116
+ };
117
+ }
118
+ /**
119
+ * Shared marker resolution for the Claude and Codex understanding-gate
120
+ * PreToolUse hooks (task e7c2ec3c): parse `approval_lifecycle` from the
121
+ * pack config, consult the task-scoped (active-claim) marker first and
122
+ * the session-scoped marker second, both under the same TTL. One code
123
+ * path on purpose — the Codex hook previously called the bare session
124
+ * check, so `max_age` and task-scoping silently applied only to Claude
125
+ * sessions.
126
+ */
127
+ export function checkOperatorApprovalMarkers(generatedDir, sessionId, packConfig, stderr) {
128
+ const lifecycleRaw = packConfig !== null && typeof packConfig === "object" && !Array.isArray(packConfig)
129
+ ? packConfig["approval_lifecycle"]
130
+ : undefined;
131
+ const lifecycle = parseApprovalLifecycle(lifecycleRaw, stderr);
132
+ const ageOpts = lifecycle.maxAgeMs !== undefined ? { maxAgeMs: lifecycle.maxAgeMs } : {};
133
+ const taskMarker = checkActiveClaimApprovalMarker(generatedDir, ageOpts);
134
+ if (taskMarker.matched) {
135
+ return {
136
+ matched: true,
137
+ source: "task",
138
+ detail: taskMarker.detail,
139
+ taskCheckDetail: taskMarker.detail,
140
+ expired: false,
141
+ forged: false,
142
+ };
143
+ }
144
+ const sessionMarker = checkApprovalMarker(generatedDir, sessionId, ageOpts);
145
+ if (sessionMarker.matched) {
146
+ return {
147
+ matched: true,
148
+ source: "session",
149
+ detail: sessionMarker.detail,
150
+ taskCheckDetail: taskMarker.detail,
151
+ expired: false,
152
+ forged: false,
153
+ };
154
+ }
155
+ return {
156
+ matched: false,
157
+ source: null,
158
+ detail: sessionMarker.detail,
159
+ taskCheckDetail: taskMarker.detail,
160
+ // `expired` is computed ONLY on this non-matched path, preserving the
161
+ // "false when matched is true" invariant (task 6e888423 review):
162
+ // e.g. a FRESH session marker (matched:true, returned above) must not
163
+ // read expired:true just because a STALE sibling task marker also
164
+ // exists — that sibling is irrelevant once the session marker itself
165
+ // satisfied the gate. Either marker aged out counts here: a
166
+ // task-scoped approval that expired (even though the session-scoped
167
+ // check may simply have never existed) is just as legitimate a "this
168
+ // had approval, it lapsed" signal as a session-scoped expiry.
169
+ expired: taskMarker.expired || sessionMarker.expired,
170
+ // Surfaced so a forgery attempt against EITHER marker is visible to
171
+ // the caller even though the overall result is (correctly) unmatched
172
+ // — mirrors the `expired` OR-merge above.
173
+ forged: taskMarker.forged || sessionMarker.forged,
174
+ };
175
+ }
176
+ /** Clear a specific task-scoped marker. Used by the post-tool-use hook. */
177
+ export function clearTaskApprovalMarker(generatedDir, taskId) {
178
+ try {
179
+ fs.rmSync(taskApprovalMarkerPathFor(generatedDir, taskId));
180
+ }
181
+ catch {
182
+ /* already gone */
183
+ }
184
+ }
185
+ //# sourceMappingURL=task-markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-markers.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/task-markers.ts"],"names":[],"mappings":"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"}