@openagentsy/hooks 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +32 -2
  3. package/dist/accountability.d.ts +29 -0
  4. package/dist/accountability.d.ts.map +1 -0
  5. package/dist/accountability.js +21 -0
  6. package/dist/accountability.js.map +1 -0
  7. package/dist/accountability.test.d.ts +2 -0
  8. package/dist/accountability.test.d.ts.map +1 -0
  9. package/dist/accountability.test.js +42 -0
  10. package/dist/accountability.test.js.map +1 -0
  11. package/dist/approvals.d.ts +110 -0
  12. package/dist/approvals.d.ts.map +1 -0
  13. package/dist/approvals.js +464 -0
  14. package/dist/approvals.js.map +1 -0
  15. package/dist/approvals.test.d.ts +2 -0
  16. package/dist/approvals.test.d.ts.map +1 -0
  17. package/dist/approvals.test.js +307 -0
  18. package/dist/approvals.test.js.map +1 -0
  19. package/dist/base-tool.d.ts +26 -0
  20. package/dist/base-tool.d.ts.map +1 -0
  21. package/dist/base-tool.js +37 -0
  22. package/dist/base-tool.js.map +1 -0
  23. package/dist/base-tool.test.d.ts +2 -0
  24. package/dist/base-tool.test.d.ts.map +1 -0
  25. package/dist/base-tool.test.js +34 -0
  26. package/dist/base-tool.test.js.map +1 -0
  27. package/dist/eval/index.d.ts +3 -0
  28. package/dist/eval/index.d.ts.map +1 -0
  29. package/dist/eval/index.js +3 -0
  30. package/dist/eval/index.js.map +1 -0
  31. package/dist/eval/runner.d.ts +179 -0
  32. package/dist/eval/runner.d.ts.map +1 -0
  33. package/dist/eval/runner.js +616 -0
  34. package/dist/eval/runner.js.map +1 -0
  35. package/dist/eval/runner.test.d.ts +2 -0
  36. package/dist/eval/runner.test.d.ts.map +1 -0
  37. package/dist/eval/runner.test.js +431 -0
  38. package/dist/eval/runner.test.js.map +1 -0
  39. package/dist/eval/types.d.ts +173 -0
  40. package/dist/eval/types.d.ts.map +1 -0
  41. package/dist/eval/types.js +104 -0
  42. package/dist/eval/types.js.map +1 -0
  43. package/dist/event.d.ts +59 -0
  44. package/dist/event.d.ts.map +1 -0
  45. package/dist/event.js +178 -0
  46. package/dist/event.js.map +1 -0
  47. package/dist/event.test.d.ts +2 -0
  48. package/dist/event.test.d.ts.map +1 -0
  49. package/dist/event.test.js +49 -0
  50. package/dist/event.test.js.map +1 -0
  51. package/dist/index.d.ts +30 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +29 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/lessons/capture.d.ts +66 -0
  56. package/dist/lessons/capture.d.ts.map +1 -0
  57. package/dist/lessons/capture.js +166 -0
  58. package/dist/lessons/capture.js.map +1 -0
  59. package/dist/lessons/capture.test.d.ts +2 -0
  60. package/dist/lessons/capture.test.d.ts.map +1 -0
  61. package/dist/lessons/capture.test.js +200 -0
  62. package/dist/lessons/capture.test.js.map +1 -0
  63. package/dist/lessons/decide.d.ts +90 -0
  64. package/dist/lessons/decide.d.ts.map +1 -0
  65. package/dist/lessons/decide.js +334 -0
  66. package/dist/lessons/decide.js.map +1 -0
  67. package/dist/lessons/decide.test.d.ts +2 -0
  68. package/dist/lessons/decide.test.d.ts.map +1 -0
  69. package/dist/lessons/decide.test.js +527 -0
  70. package/dist/lessons/decide.test.js.map +1 -0
  71. package/dist/lessons/id.d.ts +5 -0
  72. package/dist/lessons/id.d.ts.map +1 -0
  73. package/dist/lessons/id.js +20 -0
  74. package/dist/lessons/id.js.map +1 -0
  75. package/dist/lessons/index.d.ts +5 -0
  76. package/dist/lessons/index.d.ts.map +1 -0
  77. package/dist/lessons/index.js +5 -0
  78. package/dist/lessons/index.js.map +1 -0
  79. package/dist/lessons/model.d.ts +163 -0
  80. package/dist/lessons/model.d.ts.map +1 -0
  81. package/dist/lessons/model.js +181 -0
  82. package/dist/lessons/model.js.map +1 -0
  83. package/dist/lessons/model.test.d.ts +2 -0
  84. package/dist/lessons/model.test.d.ts.map +1 -0
  85. package/dist/lessons/model.test.js +178 -0
  86. package/dist/lessons/model.test.js.map +1 -0
  87. package/dist/lessons/permissions.test.d.ts +2 -0
  88. package/dist/lessons/permissions.test.d.ts.map +1 -0
  89. package/dist/lessons/permissions.test.js +88 -0
  90. package/dist/lessons/permissions.test.js.map +1 -0
  91. package/dist/lessons/store.d.ts +74 -0
  92. package/dist/lessons/store.d.ts.map +1 -0
  93. package/dist/lessons/store.js +231 -0
  94. package/dist/lessons/store.js.map +1 -0
  95. package/dist/output-checks.d.ts +64 -0
  96. package/dist/output-checks.d.ts.map +1 -0
  97. package/dist/output-checks.js +248 -0
  98. package/dist/output-checks.js.map +1 -0
  99. package/dist/output-checks.test.d.ts +2 -0
  100. package/dist/output-checks.test.d.ts.map +1 -0
  101. package/dist/output-checks.test.js +58 -0
  102. package/dist/output-checks.test.js.map +1 -0
  103. package/dist/paths.d.ts +37 -0
  104. package/dist/paths.d.ts.map +1 -0
  105. package/dist/paths.js +70 -0
  106. package/dist/paths.js.map +1 -0
  107. package/dist/paths.test.d.ts +2 -0
  108. package/dist/paths.test.d.ts.map +1 -0
  109. package/dist/paths.test.js +36 -0
  110. package/dist/paths.test.js.map +1 -0
  111. package/dist/policies/agi-framing.d.ts +12 -0
  112. package/dist/policies/agi-framing.d.ts.map +1 -0
  113. package/dist/policies/agi-framing.js +33 -0
  114. package/dist/policies/agi-framing.js.map +1 -0
  115. package/dist/policies/anti-anthropomorphism.d.ts +13 -0
  116. package/dist/policies/anti-anthropomorphism.d.ts.map +1 -0
  117. package/dist/policies/anti-anthropomorphism.js +34 -0
  118. package/dist/policies/anti-anthropomorphism.js.map +1 -0
  119. package/dist/policies/anti-dependence.d.ts +12 -0
  120. package/dist/policies/anti-dependence.d.ts.map +1 -0
  121. package/dist/policies/anti-dependence.js +33 -0
  122. package/dist/policies/anti-dependence.js.map +1 -0
  123. package/dist/policies/anti-sycophancy.d.ts +13 -0
  124. package/dist/policies/anti-sycophancy.d.ts.map +1 -0
  125. package/dist/policies/anti-sycophancy.js +34 -0
  126. package/dist/policies/anti-sycophancy.js.map +1 -0
  127. package/dist/policies/dark-pattern.d.ts +14 -0
  128. package/dist/policies/dark-pattern.d.ts.map +1 -0
  129. package/dist/policies/dark-pattern.js +44 -0
  130. package/dist/policies/dark-pattern.js.map +1 -0
  131. package/dist/policies/destructive-needs-approval.d.ts +14 -0
  132. package/dist/policies/destructive-needs-approval.d.ts.map +1 -0
  133. package/dist/policies/destructive-needs-approval.js +31 -0
  134. package/dist/policies/destructive-needs-approval.js.map +1 -0
  135. package/dist/policies/discovery.d.ts +12 -0
  136. package/dist/policies/discovery.d.ts.map +1 -0
  137. package/dist/policies/discovery.js +130 -0
  138. package/dist/policies/discovery.js.map +1 -0
  139. package/dist/policies/high-risk.d.ts +25 -0
  140. package/dist/policies/high-risk.d.ts.map +1 -0
  141. package/dist/policies/high-risk.js +105 -0
  142. package/dist/policies/high-risk.js.map +1 -0
  143. package/dist/policies/human-only-decisions.d.ts +14 -0
  144. package/dist/policies/human-only-decisions.d.ts.map +1 -0
  145. package/dist/policies/human-only-decisions.js +33 -0
  146. package/dist/policies/human-only-decisions.js.map +1 -0
  147. package/dist/policies/index.d.ts +29 -0
  148. package/dist/policies/index.d.ts.map +1 -0
  149. package/dist/policies/index.js +82 -0
  150. package/dist/policies/index.js.map +1 -0
  151. package/dist/policies/no-skip-git-hooks.d.ts +14 -0
  152. package/dist/policies/no-skip-git-hooks.d.ts.map +1 -0
  153. package/dist/policies/no-skip-git-hooks.js +31 -0
  154. package/dist/policies/no-skip-git-hooks.js.map +1 -0
  155. package/dist/policies/package-manager.d.ts +12 -0
  156. package/dist/policies/package-manager.d.ts.map +1 -0
  157. package/dist/policies/package-manager.js +29 -0
  158. package/dist/policies/package-manager.js.map +1 -0
  159. package/dist/policies/patterns.d.ts +17 -0
  160. package/dist/policies/patterns.d.ts.map +1 -0
  161. package/dist/policies/patterns.js +30 -0
  162. package/dist/policies/patterns.js.map +1 -0
  163. package/dist/policies/policies.test.d.ts +2 -0
  164. package/dist/policies/policies.test.d.ts.map +1 -0
  165. package/dist/policies/policies.test.js +210 -0
  166. package/dist/policies/policies.test.js.map +1 -0
  167. package/dist/policies/privacy-transparency.d.ts +14 -0
  168. package/dist/policies/privacy-transparency.d.ts.map +1 -0
  169. package/dist/policies/privacy-transparency.js +50 -0
  170. package/dist/policies/privacy-transparency.js.map +1 -0
  171. package/dist/policies/professional-displacement.d.ts +15 -0
  172. package/dist/policies/professional-displacement.d.ts.map +1 -0
  173. package/dist/policies/professional-displacement.js +45 -0
  174. package/dist/policies/professional-displacement.js.map +1 -0
  175. package/dist/policies/protect-policy.d.ts +14 -0
  176. package/dist/policies/protect-policy.d.ts.map +1 -0
  177. package/dist/policies/protect-policy.js +35 -0
  178. package/dist/policies/protect-policy.js.map +1 -0
  179. package/dist/policies/scope-enforcement.d.ts +14 -0
  180. package/dist/policies/scope-enforcement.d.ts.map +1 -0
  181. package/dist/policies/scope-enforcement.js +34 -0
  182. package/dist/policies/scope-enforcement.js.map +1 -0
  183. package/dist/policies/secret-scan-writes.d.ts +13 -0
  184. package/dist/policies/secret-scan-writes.d.ts.map +1 -0
  185. package/dist/policies/secret-scan-writes.js +38 -0
  186. package/dist/policies/secret-scan-writes.js.map +1 -0
  187. package/dist/policies/selection.d.ts +20 -0
  188. package/dist/policies/selection.d.ts.map +1 -0
  189. package/dist/policies/selection.js +93 -0
  190. package/dist/policies/selection.js.map +1 -0
  191. package/dist/policies/structural-bias.d.ts +14 -0
  192. package/dist/policies/structural-bias.d.ts.map +1 -0
  193. package/dist/policies/structural-bias.js +41 -0
  194. package/dist/policies/structural-bias.js.map +1 -0
  195. package/dist/policies/third-party-impact.d.ts +13 -0
  196. package/dist/policies/third-party-impact.d.ts.map +1 -0
  197. package/dist/policies/third-party-impact.js +38 -0
  198. package/dist/policies/third-party-impact.js.map +1 -0
  199. package/dist/policies/tool-call.d.ts +38 -0
  200. package/dist/policies/tool-call.d.ts.map +1 -0
  201. package/dist/policies/tool-call.js +53 -0
  202. package/dist/policies/tool-call.js.map +1 -0
  203. package/dist/policies/truthfulness-uncertainty.d.ts +13 -0
  204. package/dist/policies/truthfulness-uncertainty.d.ts.map +1 -0
  205. package/dist/policies/truthfulness-uncertainty.js +45 -0
  206. package/dist/policies/truthfulness-uncertainty.js.map +1 -0
  207. package/dist/policies/workspace-writes-only.d.ts +25 -0
  208. package/dist/policies/workspace-writes-only.d.ts.map +1 -0
  209. package/dist/policies/workspace-writes-only.js +54 -0
  210. package/dist/policies/workspace-writes-only.js.map +1 -0
  211. package/dist/policy-engine.d.ts +69 -0
  212. package/dist/policy-engine.d.ts.map +1 -0
  213. package/dist/policy-engine.js +145 -0
  214. package/dist/policy-engine.js.map +1 -0
  215. package/dist/policy-registry.d.ts +62 -0
  216. package/dist/policy-registry.d.ts.map +1 -0
  217. package/dist/policy-registry.js +63 -0
  218. package/dist/policy-registry.js.map +1 -0
  219. package/dist/policy-registry.test.d.ts +2 -0
  220. package/dist/policy-registry.test.d.ts.map +1 -0
  221. package/dist/policy-registry.test.js +79 -0
  222. package/dist/policy-registry.test.js.map +1 -0
  223. package/dist/policy-release.d.ts +44 -0
  224. package/dist/policy-release.d.ts.map +1 -0
  225. package/dist/policy-release.js +65 -0
  226. package/dist/policy-release.js.map +1 -0
  227. package/dist/policy.d.ts +141 -0
  228. package/dist/policy.d.ts.map +1 -0
  229. package/dist/policy.js +22 -0
  230. package/dist/policy.js.map +1 -0
  231. package/dist/quality/audit.d.ts +56 -0
  232. package/dist/quality/audit.d.ts.map +1 -0
  233. package/dist/quality/audit.js +52 -0
  234. package/dist/quality/audit.js.map +1 -0
  235. package/dist/quality/findings.d.ts +34 -0
  236. package/dist/quality/findings.d.ts.map +1 -0
  237. package/dist/quality/findings.js +121 -0
  238. package/dist/quality/findings.js.map +1 -0
  239. package/dist/quality/findings.test.d.ts +2 -0
  240. package/dist/quality/findings.test.d.ts.map +1 -0
  241. package/dist/quality/findings.test.js +79 -0
  242. package/dist/quality/findings.test.js.map +1 -0
  243. package/dist/quality/index.d.ts +20 -0
  244. package/dist/quality/index.d.ts.map +1 -0
  245. package/dist/quality/index.js +20 -0
  246. package/dist/quality/index.js.map +1 -0
  247. package/dist/quality/rule.d.ts +43 -0
  248. package/dist/quality/rule.d.ts.map +1 -0
  249. package/dist/quality/rule.js +162 -0
  250. package/dist/quality/rule.js.map +1 -0
  251. package/dist/quality/rule.test.d.ts +2 -0
  252. package/dist/quality/rule.test.d.ts.map +1 -0
  253. package/dist/quality/rule.test.js +187 -0
  254. package/dist/quality/rule.test.js.map +1 -0
  255. package/dist/quality/tools.d.ts +30 -0
  256. package/dist/quality/tools.d.ts.map +1 -0
  257. package/dist/quality/tools.js +33 -0
  258. package/dist/quality/tools.js.map +1 -0
  259. package/dist/quality/tools.test.d.ts +2 -0
  260. package/dist/quality/tools.test.d.ts.map +1 -0
  261. package/dist/quality/tools.test.js +41 -0
  262. package/dist/quality/tools.test.js.map +1 -0
  263. package/dist/quality/waive.d.ts +37 -0
  264. package/dist/quality/waive.d.ts.map +1 -0
  265. package/dist/quality/waive.js +39 -0
  266. package/dist/quality/waive.js.map +1 -0
  267. package/dist/quality/waivers.d.ts +63 -0
  268. package/dist/quality/waivers.d.ts.map +1 -0
  269. package/dist/quality/waivers.js +132 -0
  270. package/dist/quality/waivers.js.map +1 -0
  271. package/dist/quality/waivers.test.d.ts +2 -0
  272. package/dist/quality/waivers.test.d.ts.map +1 -0
  273. package/dist/quality/waivers.test.js +138 -0
  274. package/dist/quality/waivers.test.js.map +1 -0
  275. package/dist/registry.d.ts +86 -0
  276. package/dist/registry.d.ts.map +1 -0
  277. package/dist/registry.js +92 -0
  278. package/dist/registry.js.map +1 -0
  279. package/dist/registry.test.d.ts +2 -0
  280. package/dist/registry.test.d.ts.map +1 -0
  281. package/dist/registry.test.js +117 -0
  282. package/dist/registry.test.js.map +1 -0
  283. package/dist/rules.d.ts +107 -0
  284. package/dist/rules.d.ts.map +1 -0
  285. package/dist/rules.js +632 -0
  286. package/dist/rules.js.map +1 -0
  287. package/dist/rules.test.d.ts +2 -0
  288. package/dist/rules.test.d.ts.map +1 -0
  289. package/dist/rules.test.js +488 -0
  290. package/dist/rules.test.js.map +1 -0
  291. package/dist/secrets.d.ts +17 -0
  292. package/dist/secrets.d.ts.map +1 -0
  293. package/dist/secrets.js +17 -0
  294. package/dist/secrets.js.map +1 -0
  295. package/dist/secrets.test.d.ts +2 -0
  296. package/dist/secrets.test.d.ts.map +1 -0
  297. package/dist/secrets.test.js +20 -0
  298. package/dist/secrets.test.js.map +1 -0
  299. package/dist/shell-env.d.ts +23 -0
  300. package/dist/shell-env.d.ts.map +1 -0
  301. package/dist/shell-env.js +34 -0
  302. package/dist/shell-env.js.map +1 -0
  303. package/dist/shell-env.test.d.ts +2 -0
  304. package/dist/shell-env.test.d.ts.map +1 -0
  305. package/dist/shell-env.test.js +45 -0
  306. package/dist/shell-env.test.js.map +1 -0
  307. package/package.json +49 -4
@@ -0,0 +1,464 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { link, mkdir, open, readFile, rename, rm, stat, utimes, writeFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { appendAudit, POLICIES } from '@openagentsy/core';
5
+ import { z } from 'zod';
6
+ import { readPreToolCall } from './event.js';
7
+ /** How long a grant lives when no other time is given. */
8
+ export const DEFAULT_APPROVAL_TTL_MS = 15 * 60 * 1000;
9
+ /**
10
+ * The largest moment a date can name, in milliseconds since the epoch.
11
+ *
12
+ * A grant that runs out at a moment no date can name never runs out, and a
13
+ * grant that never runs out is a permanent permission. The store refuses one
14
+ * that would.
15
+ */
16
+ const MAX_DATE_MS = 8_640_000_000_000_000;
17
+ /**
18
+ * Whether a moment is one a date can name.
19
+ */
20
+ function isRepresentable(expiresAt) {
21
+ return Number.isFinite(expiresAt) && expiresAt > 0 && expiresAt <= MAX_DATE_MS;
22
+ }
23
+ /** The grants, as they are written down. */
24
+ const EntrySchema = z.object({ callID: z.string(), expiresAt: z.number() });
25
+ const GrantsSchema = z.array(EntrySchema);
26
+ /** How long a writer waits for the lock, in milliseconds. */
27
+ const LOCK_TIMEOUT_MS = 2000;
28
+ /**
29
+ * How old a lock must be before it is presumed abandoned.
30
+ *
31
+ * It is four times the wait, so a writer that is alive and slow is never
32
+ * mistaken for a writer that died. The two bounds are apart on purpose: a
33
+ * bound that is the same as the wait would take the lock away from a
34
+ * writer that is still holding it.
35
+ */
36
+ const STALE_LOCK_MS = LOCK_TIMEOUT_MS * 4;
37
+ /**
38
+ * How often the lock is refreshed while the action runs, in milliseconds.
39
+ *
40
+ * The refresh is what makes a lock stale only when its owner died: a
41
+ * writer that is slow keeps its lock alive.
42
+ */
43
+ const HEARTBEAT_MS = STALE_LOCK_MS / 4;
44
+ /** The pause between two tries for the lock, in milliseconds. */
45
+ const LOCK_RETRY_MS = 5;
46
+ /**
47
+ * Where the grants live.
48
+ *
49
+ * The file sits in the state root next to the board, so every session of
50
+ * the project reads the same grants. The path is a protected one: the file
51
+ * is written by a person and by the gate, never by an agent.
52
+ */
53
+ export function approvalsPath(stateRoot) {
54
+ return join(stateRoot, '.openagentsy', 'approvals.json');
55
+ }
56
+ /**
57
+ * Where the audit of the project lives.
58
+ *
59
+ * It is the board's own log, beside the grants: the same file the leases and
60
+ * the records are audited in.
61
+ */
62
+ function auditPathFor(approvalsFile) {
63
+ return join(dirname(approvalsFile), 'audit.jsonl');
64
+ }
65
+ /**
66
+ * Whether an error is the one the file system raises for a code.
67
+ */
68
+ function isFileError(error, code) {
69
+ return typeof error === 'object' && error !== null && error.code === code;
70
+ }
71
+ /**
72
+ * A pause.
73
+ */
74
+ function sleep(ms) {
75
+ return new Promise((resolve) => {
76
+ setTimeout(resolve, ms);
77
+ });
78
+ }
79
+ /**
80
+ * The lock that serialises a claim.
81
+ *
82
+ * Two calls that spend the same grant at the same time must not both pass,
83
+ * so the read and the write of the store are one step under a lock file
84
+ * that is created exclusively. The second caller waits and then finds the
85
+ * grant gone.
86
+ */
87
+ export async function withLock(path, timing, action) {
88
+ const lockPath = `${path}.lock`;
89
+ await mkdir(dirname(lockPath), { recursive: true });
90
+ const release = await acquireLock(lockPath, timing);
91
+ const heartbeat = setInterval(() => {
92
+ // The refresh is not awaited: the action is what matters, and a
93
+ // refresh that fails leaves the lock as it was.
94
+ void utimes(lockPath, new Date(), new Date()).catch(() => undefined);
95
+ }, timing.heartbeatMs);
96
+ heartbeat.unref?.();
97
+ try {
98
+ return await action();
99
+ }
100
+ finally {
101
+ clearInterval(heartbeat);
102
+ await release();
103
+ }
104
+ }
105
+ /**
106
+ * The lock file, taken.
107
+ *
108
+ * The file is created exclusively, so only one writer holds it, and it
109
+ * carries the token of the writer that holds it, so a writer only gives
110
+ * back its own lock.
111
+ *
112
+ * A writer that cannot take the lock waits. Whether the lock may be taken
113
+ * over is decided once, when the lock is first seen: a lock that was
114
+ * alive then is not taken away from the writer that holds it now. A
115
+ * writer that waits for too long gives up with an error: the store is not
116
+ * left open.
117
+ */
118
+ async function acquireLock(lockPath, timing) {
119
+ const deadline = Date.now() + timing.timeoutMs;
120
+ const token = randomUUID();
121
+ let seen = false;
122
+ for (;;) {
123
+ try {
124
+ const handle = await open(lockPath, 'wx');
125
+ await handle.writeFile(token);
126
+ await handle.close();
127
+ return async () => {
128
+ await releaseLock(lockPath, token);
129
+ };
130
+ }
131
+ catch (error) {
132
+ if (!isFileError(error, 'EEXIST'))
133
+ throw error;
134
+ if (!seen) {
135
+ seen = true;
136
+ await takeStaleLock(lockPath, timing);
137
+ }
138
+ if (Date.now() > deadline) {
139
+ throw new Error(`the approval store is locked by another writer: ${lockPath}`);
140
+ }
141
+ await sleep(LOCK_RETRY_MS);
142
+ }
143
+ }
144
+ }
145
+ /**
146
+ * A stale lock is taken over, once.
147
+ *
148
+ * The taking over is exclusive: exactly one waiter may take a stale lock
149
+ * over. The one that does not win waits and then fails cleanly, because
150
+ * two approvals out of one stale lock is one approval too many.
151
+ */
152
+ async function takeStaleLock(lockPath, timing) {
153
+ if (!(await isStale(lockPath, timing)))
154
+ return;
155
+ if (await reclaimStaleLock(lockPath, timing))
156
+ return;
157
+ throw new Error(`the approval store is locked by another writer: ${lockPath}`);
158
+ }
159
+ /**
160
+ * The lock is given back.
161
+ *
162
+ * The lock is moved to a private path with a rename, which is one atomic
163
+ * step, and the token is read there: a lock that was taken away from this
164
+ * writer and given to another one is not removed by mistake, and the lock
165
+ * of the other writer is put back.
166
+ */
167
+ export async function releaseLock(lockPath, token) {
168
+ const privatePath = `${lockPath}.release.${token}`;
169
+ try {
170
+ await rename(lockPath, privatePath);
171
+ }
172
+ catch (error) {
173
+ if (isFileError(error, 'ENOENT'))
174
+ return;
175
+ throw error;
176
+ }
177
+ let content;
178
+ try {
179
+ content = await readFile(privatePath, 'utf8');
180
+ }
181
+ catch (error) {
182
+ if (isFileError(error, 'ENOENT'))
183
+ return;
184
+ throw error;
185
+ }
186
+ if (content !== token) {
187
+ // The lock is not this writer's: it goes back where it was.
188
+ await restoreLock(privatePath, lockPath);
189
+ return;
190
+ }
191
+ await rm(privatePath, { force: true });
192
+ }
193
+ /**
194
+ * A lock that was moved aside goes back where it was.
195
+ *
196
+ * It is not put over a lock that is held now: the writer that holds that
197
+ * one would lose it. The link is one atomic step, so a lock that is taken
198
+ * in the window between the move and the restore is not lost either.
199
+ */
200
+ export async function restoreLock(privatePath, lockPath) {
201
+ try {
202
+ await link(privatePath, lockPath);
203
+ }
204
+ catch (error) {
205
+ // A lock that is held now is not overwritten, and a lock that is
206
+ // gone cannot be restored.
207
+ if (isFileError(error, 'EEXIST') || isFileError(error, 'ENOENT'))
208
+ return;
209
+ throw error;
210
+ }
211
+ await rm(privatePath, { force: true });
212
+ }
213
+ /**
214
+ * Whether a lock is old enough to be presumed abandoned.
215
+ */
216
+ async function isStale(path, timing) {
217
+ try {
218
+ const stats = await stat(path);
219
+ return Date.now() - stats.mtimeMs > timing.staleMs;
220
+ }
221
+ catch (error) {
222
+ if (isFileError(error, 'ENOENT'))
223
+ return true;
224
+ throw error;
225
+ }
226
+ }
227
+ /**
228
+ * A file that outlived its writer is removed.
229
+ *
230
+ * It answers whether the file was there to be removed.
231
+ */
232
+ async function removeIfStale(path, timing) {
233
+ if (!(await isStale(path, timing)))
234
+ return false;
235
+ await rm(path, { force: true });
236
+ return true;
237
+ }
238
+ /**
239
+ * A stale lock is taken over.
240
+ *
241
+ * The taking over is exclusive: a second file says who is taking it
242
+ * over, so two waiters do not move the lock at the same time. The
243
+ * staleness is checked again under it, so a lock that a live writer took
244
+ * in the meantime is left alone, and the lock is moved aside with a
245
+ * rename, which is one atomic step: only one waiter moves it.
246
+ *
247
+ * It answers whether this waiter took the lock over: the one that does
248
+ * not goes on to wait, and then fails.
249
+ */
250
+ async function reclaimStaleLock(lockPath, timing) {
251
+ const reclaimPath = `${lockPath}.reclaim`;
252
+ let handle;
253
+ try {
254
+ handle = await open(reclaimPath, 'wx');
255
+ }
256
+ catch (error) {
257
+ if (isFileError(error, 'EEXIST')) {
258
+ // An orphan reclaim file blocks every later reclaim, so it is
259
+ // removed when it is old enough to be one, and the reclaim is tried
260
+ // again.
261
+ if (!(await removeIfStale(reclaimPath, timing)))
262
+ return false;
263
+ return reclaimStaleLock(lockPath, timing);
264
+ }
265
+ throw error;
266
+ }
267
+ try {
268
+ if (!(await isStale(lockPath, timing)))
269
+ return false;
270
+ const aside = `${lockPath}.stale`;
271
+ await rm(aside, { force: true });
272
+ try {
273
+ await rename(lockPath, aside);
274
+ }
275
+ catch (error) {
276
+ if (isFileError(error, 'ENOENT'))
277
+ return false;
278
+ throw error;
279
+ }
280
+ await rm(aside, { force: true });
281
+ return true;
282
+ }
283
+ finally {
284
+ await handle.close();
285
+ await rm(reclaimPath, { force: true });
286
+ }
287
+ }
288
+ /**
289
+ * The grants, without the ones that have run out.
290
+ *
291
+ * A file that is not there yet is no grants. A file that cannot be read,
292
+ * or that holds something else, is an error: the error travels to the
293
+ * caller, so a grant is never granted on top of a file that was not
294
+ * understood.
295
+ */
296
+ async function loadGrants(path, now) {
297
+ let raw;
298
+ try {
299
+ raw = await readFile(path, 'utf8');
300
+ }
301
+ catch (error) {
302
+ if (isFileError(error, 'ENOENT'))
303
+ return { entries: [], pruned: false };
304
+ throw error;
305
+ }
306
+ let parsed;
307
+ try {
308
+ parsed = JSON.parse(raw);
309
+ }
310
+ catch (error) {
311
+ throw new Error(`the approval store at ${path} does not hold JSON`, { cause: error });
312
+ }
313
+ const result = GrantsSchema.safeParse(parsed);
314
+ if (!result.success) {
315
+ throw new Error(`the approval store at ${path} does not hold a list of grants`);
316
+ }
317
+ const entries = result.data.filter((entry) => entry.expiresAt > now);
318
+ return { entries, pruned: entries.length !== result.data.length };
319
+ }
320
+ /**
321
+ * The grants, written down.
322
+ *
323
+ * The directory is made first: the file is the first thing in the state
324
+ * root of a fresh project, and a write that fails on a missing directory
325
+ * is a grant nobody can give.
326
+ */
327
+ async function saveGrants(path, entries) {
328
+ await mkdir(dirname(path), { recursive: true });
329
+ await writeFile(path, `${JSON.stringify(entries, null, 2)}\n`, 'utf8');
330
+ }
331
+ /**
332
+ * The store of grants for one project.
333
+ */
334
+ export function createApprovalStore(stateRoot, timing = {}) {
335
+ const path = approvalsPath(stateRoot);
336
+ const resolved = {
337
+ timeoutMs: timing.timeoutMs ?? LOCK_TIMEOUT_MS,
338
+ staleMs: timing.staleMs ?? STALE_LOCK_MS,
339
+ heartbeatMs: timing.heartbeatMs ?? HEARTBEAT_MS,
340
+ };
341
+ return {
342
+ path,
343
+ async isApproved(callID, now) {
344
+ return withLock(path, resolved, async () => {
345
+ const { entries, pruned } = await loadGrants(path, now);
346
+ if (pruned)
347
+ await saveGrants(path, entries);
348
+ return entries.some((entry) => entry.callID === callID);
349
+ });
350
+ },
351
+ async approve(callID, now, ttlMs = DEFAULT_APPROVAL_TTL_MS) {
352
+ return withLock(path, resolved, async () => {
353
+ // The window has to be a window: a grant that runs out at once is no
354
+ // grant, and a grant that never runs out is a permanent permission.
355
+ // The bound lives here, where the grant is written, so a caller that
356
+ // does not go through the command cannot write one either.
357
+ if (!Number.isFinite(ttlMs) || ttlMs <= 0) {
358
+ throw new Error(`the grant would not live for a moment: ${ttlMs}`);
359
+ }
360
+ const { entries } = await loadGrants(path, now);
361
+ const held = entries.find((entry) => entry.callID === callID);
362
+ // A grant that is already on file keeps the expiry it has: two
363
+ // people approving the same call do not move the window under each
364
+ // other, and the caller is told the grant was found.
365
+ if (held !== undefined)
366
+ return { entry: held, created: false };
367
+ const entry = { callID, expiresAt: now + ttlMs };
368
+ if (!isRepresentable(entry.expiresAt)) {
369
+ throw new Error(`the grant would not run out at a moment a date can name: ${entry.expiresAt}`);
370
+ }
371
+ await saveGrants(path, [...entries, entry]);
372
+ return { entry, created: true };
373
+ });
374
+ },
375
+ async consumeApproval(callID, now, actor) {
376
+ return withLock(path, resolved, async () => {
377
+ const { entries, pruned } = await loadGrants(path, now);
378
+ const held = entries.find((entry) => entry.callID === callID);
379
+ if (held === undefined) {
380
+ if (pruned)
381
+ await saveGrants(path, entries);
382
+ return false;
383
+ }
384
+ await saveGrants(path, entries.filter((entry) => entry.callID !== callID));
385
+ // The grant is spent, and the trail is part of the spend: a spend
386
+ // that cannot be written down takes the grant back, so a call is
387
+ // never approved and not on the record at the same time.
388
+ try {
389
+ await appendAudit(auditPathFor(path), {
390
+ policyId: POLICIES.approveUsed.id,
391
+ policyVersion: POLICIES.approveUsed.version,
392
+ actor: actor ?? 'unknown',
393
+ action: 'approve_use',
394
+ resource: callID,
395
+ effect: 'allow',
396
+ detail: 'the grant was spent',
397
+ });
398
+ }
399
+ catch (error) {
400
+ await saveGrants(path, entries);
401
+ throw error;
402
+ }
403
+ return true;
404
+ });
405
+ },
406
+ async revokeApproval(callID, now) {
407
+ return withLock(path, resolved, async () => {
408
+ const { entries } = await loadGrants(path, now);
409
+ if (!entries.some((entry) => entry.callID === callID))
410
+ return false;
411
+ await saveGrants(path, entries.filter((entry) => entry.callID !== callID));
412
+ return true;
413
+ });
414
+ },
415
+ };
416
+ }
417
+ /**
418
+ * The message of an error, when it has one.
419
+ */
420
+ function describe(error) {
421
+ return error instanceof Error ? error.message : String(error);
422
+ }
423
+ /**
424
+ * The rule handler that asks for an approval.
425
+ *
426
+ * A command that matches needs a grant. A pattern is enough for a single
427
+ * shape, and a matcher is enough for the shapes a pattern cannot say: the
428
+ * flags of `rm` in any order, for example.
429
+ *
430
+ * A store that cannot be read passes nothing: the command is refused, and
431
+ * the reason names the failure. A refusal is the safe answer, and the
432
+ * failure is not swallowed: it is in the reason.
433
+ */
434
+ export function approvalGate(matcher, store, now = Date.now) {
435
+ return async (event) => {
436
+ const call = readPreToolCall(event);
437
+ if (call === undefined || call.command === undefined)
438
+ return { continue: true };
439
+ const matches = typeof matcher === 'function' ? matcher(call.command) : matcher.test(call.command);
440
+ if (!matches)
441
+ return { continue: true };
442
+ const callID = call.callID;
443
+ if (callID !== undefined) {
444
+ try {
445
+ // The role is the agent the call was made for, and it is the actor
446
+ // of the spend: the person who granted it is not the one that used
447
+ // it.
448
+ if (await store.consumeApproval(callID, now(), call.role))
449
+ return { continue: true };
450
+ }
451
+ catch (error) {
452
+ return {
453
+ continue: false,
454
+ reason: `The approval store could not be read, so the command is refused. ${describe(error)}`,
455
+ };
456
+ }
457
+ }
458
+ return {
459
+ continue: false,
460
+ reason: `A destructive command needs a person to approve it. The escape hatch is /oa approve <callID>${callID === undefined ? '' : ` (this call is ${callID})`}.`,
461
+ };
462
+ };
463
+ }
464
+ //# sourceMappingURL=approvals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.js","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=approvals.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.test.d.ts","sourceRoot":"","sources":["../src/approvals.test.ts"],"names":[],"mappings":""}