opensquid 0.5.441 → 0.5.449

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 (391) hide show
  1. package/README.md +1 -0
  2. package/dist/functions/arm_scope.d.ts +27 -0
  3. package/dist/functions/arm_scope.d.ts.map +1 -0
  4. package/dist/functions/arm_scope.js +52 -0
  5. package/dist/functions/arm_scope.js.map +1 -0
  6. package/dist/functions/index.d.ts +1 -0
  7. package/dist/functions/index.d.ts.map +1 -1
  8. package/dist/functions/index.js +1 -0
  9. package/dist/functions/index.js.map +1 -1
  10. package/dist/functions/recall_pre_inject.d.ts.map +1 -1
  11. package/dist/functions/recall_pre_inject.js +12 -0
  12. package/dist/functions/recall_pre_inject.js.map +1 -1
  13. package/dist/rag/store_git.d.ts +23 -0
  14. package/dist/rag/store_git.d.ts.map +1 -0
  15. package/dist/rag/store_git.js +57 -0
  16. package/dist/rag/store_git.js.map +1 -0
  17. package/dist/runtime/bootstrap.d.ts.map +1 -1
  18. package/dist/runtime/bootstrap.js +2 -0
  19. package/dist/runtime/bootstrap.js.map +1 -1
  20. package/dist/runtime/handoff/render.d.ts +5 -4
  21. package/dist/runtime/handoff/render.d.ts.map +1 -1
  22. package/dist/runtime/handoff/render.js +7 -7
  23. package/dist/runtime/handoff/render.js.map +1 -1
  24. package/dist/runtime/hooks/active_task_mirror.js +0 -0
  25. package/dist/runtime/hooks/apply_patch.js +0 -0
  26. package/dist/runtime/hooks/dispatch.js +0 -0
  27. package/dist/runtime/hooks/hook_output.js +0 -0
  28. package/dist/runtime/hooks/memory_reconcile.js +0 -0
  29. package/dist/runtime/hooks/new_project_detect.js +0 -0
  30. package/dist/runtime/hooks/profession_resolver.js +0 -0
  31. package/dist/runtime/hooks/scope_intent.js +0 -0
  32. package/dist/runtime/hooks/session-end.js +11 -0
  33. package/dist/runtime/hooks/session-end.js.map +1 -1
  34. package/dist/runtime/hooks/session_id.js +0 -0
  35. package/dist/runtime/hooks/session_liveness.js +0 -0
  36. package/dist/runtime/hooks/stop_drive.js +0 -0
  37. package/dist/runtime/hooks/stop_stream.js +0 -0
  38. package/dist/runtime/hooks/subagent_guard.js +0 -0
  39. package/dist/runtime/hooks/transcript.js +0 -0
  40. package/dist/runtime/hooks/transcript_tasks.js +0 -0
  41. package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
  42. package/dist/runtime/ralph/orchestrator.js +2 -1
  43. package/dist/runtime/ralph/orchestrator.js.map +1 -1
  44. package/dist/setup/cli/limits_state.d.ts.map +1 -1
  45. package/dist/setup/cli/limits_state.js +6 -40
  46. package/dist/setup/cli/limits_state.js.map +1 -1
  47. package/dist/setup/cli/pack_walk.d.ts +32 -0
  48. package/dist/setup/cli/pack_walk.d.ts.map +1 -0
  49. package/dist/setup/cli/pack_walk.js +76 -0
  50. package/dist/setup/cli/pack_walk.js.map +1 -0
  51. package/dist/setup/cli/permissions_state.d.ts.map +1 -1
  52. package/dist/setup/cli/permissions_state.js +6 -37
  53. package/dist/setup/cli/permissions_state.js.map +1 -1
  54. package/dist/setup/cli/triggers_state.d.ts.map +1 -1
  55. package/dist/setup/cli/triggers_state.js +3 -29
  56. package/dist/setup/cli/triggers_state.js.map +1 -1
  57. package/dist/workgraph/events.d.ts.map +1 -1
  58. package/dist/workgraph/events.js +10 -0
  59. package/dist/workgraph/events.js.map +1 -1
  60. package/dist/workgraph/store.d.ts.map +1 -1
  61. package/dist/workgraph/store.js +5 -0
  62. package/dist/workgraph/store.js.map +1 -1
  63. package/dist/workgraph/types.d.ts +2 -1
  64. package/dist/workgraph/types.d.ts.map +1 -1
  65. package/docs/ARCHITECTURE.md +268 -0
  66. package/docs/pack-runtime.md +15 -12
  67. package/docs/skill-grammar-guide.md +4 -4
  68. package/package.json +5 -3
  69. package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
  70. package/dist/anti-drift/evaluator.d.ts +0 -88
  71. package/dist/anti-drift/evaluator.d.ts.map +0 -1
  72. package/dist/anti-drift/evaluator.js +0 -417
  73. package/dist/anti-drift/evaluator.js.map +0 -1
  74. package/dist/anti-drift/evaluator.test.js +0 -78
  75. package/dist/anti-drift/rules.d.ts +0 -80
  76. package/dist/anti-drift/rules.d.ts.map +0 -1
  77. package/dist/anti-drift/rules.js +0 -368
  78. package/dist/anti-drift/rules.js.map +0 -1
  79. package/dist/anti-drift/rules.test.js +0 -213
  80. package/dist/anti-drift/state.d.ts +0 -107
  81. package/dist/anti-drift/state.d.ts.map +0 -1
  82. package/dist/anti-drift/state.js +0 -177
  83. package/dist/anti-drift/state.js.map +0 -1
  84. package/dist/anti-drift/state.test.js +0 -120
  85. package/dist/chat/adapters/discord.d.ts +0 -41
  86. package/dist/chat/adapters/discord.d.ts.map +0 -1
  87. package/dist/chat/adapters/discord.js +0 -176
  88. package/dist/chat/adapters/discord.js.map +0 -1
  89. package/dist/chat/adapters/discord.test.js +0 -25
  90. package/dist/chat/adapters/slack.d.ts +0 -43
  91. package/dist/chat/adapters/slack.d.ts.map +0 -1
  92. package/dist/chat/adapters/slack.js +0 -172
  93. package/dist/chat/adapters/slack.js.map +0 -1
  94. package/dist/chat/adapters/slack.test.js +0 -30
  95. package/dist/chat/adapters/telegram.d.ts +0 -148
  96. package/dist/chat/adapters/telegram.d.ts.map +0 -1
  97. package/dist/chat/adapters/telegram.js +0 -498
  98. package/dist/chat/adapters/telegram.js.map +0 -1
  99. package/dist/chat/adapters/telegram.test.js +0 -94
  100. package/dist/chat/config.d.ts +0 -98
  101. package/dist/chat/config.d.ts.map +0 -1
  102. package/dist/chat/config.js +0 -185
  103. package/dist/chat/config.js.map +0 -1
  104. package/dist/chat/daemon/active-project.d.ts +0 -17
  105. package/dist/chat/daemon/active-project.d.ts.map +0 -1
  106. package/dist/chat/daemon/active-project.js +0 -23
  107. package/dist/chat/daemon/active-project.js.map +0 -1
  108. package/dist/chat/daemon/autospawn.d.ts +0 -40
  109. package/dist/chat/daemon/autospawn.d.ts.map +0 -1
  110. package/dist/chat/daemon/autospawn.js +0 -129
  111. package/dist/chat/daemon/autospawn.js.map +0 -1
  112. package/dist/chat/daemon/autospawn.test.js +0 -112
  113. package/dist/chat/daemon/cli.d.ts +0 -18
  114. package/dist/chat/daemon/cli.d.ts.map +0 -1
  115. package/dist/chat/daemon/cli.js +0 -71
  116. package/dist/chat/daemon/cli.js.map +0 -1
  117. package/dist/chat/daemon/collisions.js +0 -384
  118. package/dist/chat/daemon/health-check.d.ts +0 -69
  119. package/dist/chat/daemon/health-check.d.ts.map +0 -1
  120. package/dist/chat/daemon/health-check.js +0 -112
  121. package/dist/chat/daemon/health-check.js.map +0 -1
  122. package/dist/chat/daemon/inbox-read.d.ts +0 -35
  123. package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
  124. package/dist/chat/daemon/inbox-read.js +0 -75
  125. package/dist/chat/daemon/inbox-read.js.map +0 -1
  126. package/dist/chat/daemon/inbox-read.test.js +0 -97
  127. package/dist/chat/daemon/inbox.d.ts +0 -63
  128. package/dist/chat/daemon/inbox.d.ts.map +0 -1
  129. package/dist/chat/daemon/inbox.js +0 -56
  130. package/dist/chat/daemon/inbox.js.map +0 -1
  131. package/dist/chat/daemon/inbox.test.js +0 -110
  132. package/dist/chat/daemon/lifecycle.d.ts +0 -71
  133. package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
  134. package/dist/chat/daemon/lifecycle.js +0 -221
  135. package/dist/chat/daemon/lifecycle.js.map +0 -1
  136. package/dist/chat/daemon/lifecycle.test.js +0 -163
  137. package/dist/chat/daemon/protocol.d.ts +0 -107
  138. package/dist/chat/daemon/protocol.d.ts.map +0 -1
  139. package/dist/chat/daemon/protocol.js +0 -54
  140. package/dist/chat/daemon/protocol.js.map +0 -1
  141. package/dist/chat/daemon/routing.d.ts +0 -140
  142. package/dist/chat/daemon/routing.d.ts.map +0 -1
  143. package/dist/chat/daemon/routing.js +0 -198
  144. package/dist/chat/daemon/routing.js.map +0 -1
  145. package/dist/chat/daemon/routing.test.js +0 -259
  146. package/dist/chat/daemon/rpc-client.d.ts +0 -45
  147. package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
  148. package/dist/chat/daemon/rpc-client.js +0 -133
  149. package/dist/chat/daemon/rpc-client.js.map +0 -1
  150. package/dist/chat/daemon/rpc-server.d.ts +0 -39
  151. package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
  152. package/dist/chat/daemon/rpc-server.js +0 -385
  153. package/dist/chat/daemon/rpc-server.js.map +0 -1
  154. package/dist/chat/daemon/rpc.test.js +0 -177
  155. package/dist/chat/daemon/subscribers.js +0 -257
  156. package/dist/chat/daemon/worker.d.ts +0 -27
  157. package/dist/chat/daemon/worker.d.ts.map +0 -1
  158. package/dist/chat/daemon/worker.js +0 -313
  159. package/dist/chat/daemon/worker.js.map +0 -1
  160. package/dist/chat/daemon/workspace-topic.js +0 -324
  161. package/dist/chat/env-token.d.ts +0 -60
  162. package/dist/chat/env-token.d.ts.map +0 -1
  163. package/dist/chat/env-token.js +0 -137
  164. package/dist/chat/env-token.js.map +0 -1
  165. package/dist/chat/env-token.test.js +0 -160
  166. package/dist/chat/factory.d.ts +0 -30
  167. package/dist/chat/factory.d.ts.map +0 -1
  168. package/dist/chat/factory.js +0 -50
  169. package/dist/chat/factory.js.map +0 -1
  170. package/dist/chat/factory.test.js +0 -55
  171. package/dist/chat/gateway.d.ts +0 -176
  172. package/dist/chat/gateway.d.ts.map +0 -1
  173. package/dist/chat/gateway.js +0 -146
  174. package/dist/chat/gateway.js.map +0 -1
  175. package/dist/chat/gateway.test.js +0 -192
  176. package/dist/claude-md.d.ts +0 -39
  177. package/dist/claude-md.d.ts.map +0 -1
  178. package/dist/claude-md.js +0 -113
  179. package/dist/claude-md.js.map +0 -1
  180. package/dist/claude-md.test.js +0 -91
  181. package/dist/codex/activate.d.ts +0 -66
  182. package/dist/codex/activate.d.ts.map +0 -1
  183. package/dist/codex/activate.js +0 -329
  184. package/dist/codex/activate.js.map +0 -1
  185. package/dist/codex/activate.test.js +0 -229
  186. package/dist/codex/bundled-default/bundled-default.test.js +0 -161
  187. package/dist/codex/cli-publish.test.js +0 -133
  188. package/dist/codex/cli.d.ts +0 -35
  189. package/dist/codex/cli.d.ts.map +0 -1
  190. package/dist/codex/cli.js +0 -554
  191. package/dist/codex/cli.js.map +0 -1
  192. package/dist/codex/cli.test.js +0 -277
  193. package/dist/codex/import-skill-md.d.ts +0 -53
  194. package/dist/codex/import-skill-md.d.ts.map +0 -1
  195. package/dist/codex/import-skill-md.js +0 -236
  196. package/dist/codex/import-skill-md.js.map +0 -1
  197. package/dist/codex/import-skill-md.test.js +0 -225
  198. package/dist/codex/loader.d.ts +0 -27
  199. package/dist/codex/loader.d.ts.map +0 -1
  200. package/dist/codex/loader.js +0 -86
  201. package/dist/codex/loader.js.map +0 -1
  202. package/dist/codex/loader.test.js +0 -75
  203. package/dist/codex/parse.d.ts +0 -28
  204. package/dist/codex/parse.d.ts.map +0 -1
  205. package/dist/codex/parse.js +0 -309
  206. package/dist/codex/parse.js.map +0 -1
  207. package/dist/codex/parse.test.js +0 -241
  208. package/dist/codex/store.d.ts +0 -87
  209. package/dist/codex/store.d.ts.map +0 -1
  210. package/dist/codex/store.js +0 -205
  211. package/dist/codex/store.js.map +0 -1
  212. package/dist/codex/store.test.js +0 -242
  213. package/dist/codex/types.d.ts +0 -398
  214. package/dist/codex/types.d.ts.map +0 -1
  215. package/dist/codex/types.js +0 -21
  216. package/dist/codex/types.js.map +0 -1
  217. package/dist/config.d.ts +0 -53
  218. package/dist/config.d.ts.map +0 -1
  219. package/dist/config.js +0 -202
  220. package/dist/config.js.map +0 -1
  221. package/dist/config.test.js +0 -117
  222. package/dist/engine/cli.d.ts +0 -14
  223. package/dist/engine/cli.d.ts.map +0 -1
  224. package/dist/engine/cli.js +0 -171
  225. package/dist/engine/cli.js.map +0 -1
  226. package/dist/engine/client.d.ts +0 -219
  227. package/dist/engine/client.d.ts.map +0 -1
  228. package/dist/engine/client.js +0 -312
  229. package/dist/engine/client.js.map +0 -1
  230. package/dist/engine/config.d.ts +0 -62
  231. package/dist/engine/config.d.ts.map +0 -1
  232. package/dist/engine/config.js +0 -223
  233. package/dist/engine/config.js.map +0 -1
  234. package/dist/engine/index.d.ts +0 -17
  235. package/dist/engine/index.d.ts.map +0 -1
  236. package/dist/engine/index.js +0 -16
  237. package/dist/engine/index.js.map +0 -1
  238. package/dist/engine/resolver.d.ts +0 -62
  239. package/dist/engine/resolver.d.ts.map +0 -1
  240. package/dist/engine/resolver.js +0 -103
  241. package/dist/engine/resolver.js.map +0 -1
  242. package/dist/engine/singleton.d.ts +0 -95
  243. package/dist/engine/singleton.d.ts.map +0 -1
  244. package/dist/engine/singleton.js +0 -325
  245. package/dist/engine/singleton.js.map +0 -1
  246. package/dist/engine/types.d.ts +0 -402
  247. package/dist/engine/types.d.ts.map +0 -1
  248. package/dist/engine/types.js +0 -22
  249. package/dist/engine/types.js.map +0 -1
  250. package/dist/engine-binary-resolver.js +0 -110
  251. package/dist/engine-binary-resolver.test.js +0 -61
  252. package/dist/engine-cli.js +0 -60
  253. package/dist/engine-client.js +0 -301
  254. package/dist/engine-client.test.js +0 -118
  255. package/dist/functions/chain_state.d.ts +0 -51
  256. package/dist/functions/chain_state.d.ts.map +0 -1
  257. package/dist/functions/chain_state.js +0 -59
  258. package/dist/functions/chain_state.js.map +0 -1
  259. package/dist/hooks/drift-catalog.d.ts +0 -68
  260. package/dist/hooks/drift-catalog.d.ts.map +0 -1
  261. package/dist/hooks/drift-catalog.js +0 -184
  262. package/dist/hooks/drift-catalog.js.map +0 -1
  263. package/dist/hooks/drift-catalog.test.js +0 -154
  264. package/dist/hooks/drift-patterns.d.ts +0 -110
  265. package/dist/hooks/drift-patterns.d.ts.map +0 -1
  266. package/dist/hooks/drift-patterns.js +0 -289
  267. package/dist/hooks/drift-patterns.js.map +0 -1
  268. package/dist/hooks/drift-patterns.test.js +0 -325
  269. package/dist/hooks/engine-vocab-gate.d.ts +0 -108
  270. package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
  271. package/dist/hooks/engine-vocab-gate.js +0 -225
  272. package/dist/hooks/engine-vocab-gate.js.map +0 -1
  273. package/dist/hooks/engine-vocab-gate.test.js +0 -170
  274. package/dist/hooks/heartbeat.d.ts +0 -107
  275. package/dist/hooks/heartbeat.d.ts.map +0 -1
  276. package/dist/hooks/heartbeat.js +0 -316
  277. package/dist/hooks/heartbeat.js.map +0 -1
  278. package/dist/hooks/heartbeat.test.js +0 -393
  279. package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
  280. package/dist/hooks/honesty-ledger.d.ts +0 -123
  281. package/dist/hooks/honesty-ledger.d.ts.map +0 -1
  282. package/dist/hooks/honesty-ledger.js +0 -226
  283. package/dist/hooks/honesty-ledger.js.map +0 -1
  284. package/dist/hooks/honesty-ledger.test.js +0 -466
  285. package/dist/hooks/inline-report-check.d.ts +0 -63
  286. package/dist/hooks/inline-report-check.d.ts.map +0 -1
  287. package/dist/hooks/inline-report-check.js +0 -88
  288. package/dist/hooks/inline-report-check.js.map +0 -1
  289. package/dist/hooks/inline-report-check.test.js +0 -96
  290. package/dist/hooks/pre-tool-use.d.ts +0 -62
  291. package/dist/hooks/pre-tool-use.d.ts.map +0 -1
  292. package/dist/hooks/pre-tool-use.js +0 -342
  293. package/dist/hooks/pre-tool-use.js.map +0 -1
  294. package/dist/hooks/pre-tool-use.test.js +0 -134
  295. package/dist/hooks/session-end.d.ts +0 -15
  296. package/dist/hooks/session-end.d.ts.map +0 -1
  297. package/dist/hooks/session-end.js +0 -60
  298. package/dist/hooks/session-end.js.map +0 -1
  299. package/dist/hooks/session-end.test.js +0 -52
  300. package/dist/hooks/stop.d.ts +0 -35
  301. package/dist/hooks/stop.d.ts.map +0 -1
  302. package/dist/hooks/stop.js +0 -136
  303. package/dist/hooks/stop.js.map +0 -1
  304. package/dist/hooks/transcript-active-task.test.js +0 -342
  305. package/dist/hooks/transcript.d.ts +0 -26
  306. package/dist/hooks/transcript.d.ts.map +0 -1
  307. package/dist/hooks/transcript.js +0 -266
  308. package/dist/hooks/transcript.js.map +0 -1
  309. package/dist/hooks/transcript.test.js +0 -103
  310. package/dist/hooks/user-prompt-submit.d.ts +0 -74
  311. package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
  312. package/dist/hooks/user-prompt-submit.js +0 -256
  313. package/dist/hooks/user-prompt-submit.js.map +0 -1
  314. package/dist/hooks/user-prompt-submit.test.js +0 -118
  315. package/dist/hooks/versioning-gate.d.ts +0 -101
  316. package/dist/hooks/versioning-gate.d.ts.map +0 -1
  317. package/dist/hooks/versioning-gate.js +0 -245
  318. package/dist/hooks/versioning-gate.js.map +0 -1
  319. package/dist/hooks/versioning-gate.test.js +0 -368
  320. package/dist/hooks/workflow-gate.d.ts +0 -64
  321. package/dist/hooks/workflow-gate.d.ts.map +0 -1
  322. package/dist/hooks/workflow-gate.js +0 -152
  323. package/dist/hooks/workflow-gate.js.map +0 -1
  324. package/dist/hooks/workflow-gate.test.js +0 -197
  325. package/dist/hooks-cli.d.ts +0 -25
  326. package/dist/hooks-cli.d.ts.map +0 -1
  327. package/dist/hooks-cli.js +0 -286
  328. package/dist/hooks-cli.js.map +0 -1
  329. package/dist/hooks-cli.test.js +0 -148
  330. package/dist/origin.d.ts +0 -16
  331. package/dist/origin.d.ts.map +0 -1
  332. package/dist/origin.js +0 -92
  333. package/dist/origin.js.map +0 -1
  334. package/dist/packs/seed_lessons_ingest.d.ts +0 -30
  335. package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
  336. package/dist/packs/seed_lessons_ingest.js +0 -107
  337. package/dist/packs/seed_lessons_ingest.js.map +0 -1
  338. package/dist/project-cli.d.ts +0 -7
  339. package/dist/project-cli.d.ts.map +0 -1
  340. package/dist/project-cli.js +0 -145
  341. package/dist/project-cli.js.map +0 -1
  342. package/dist/project.d.ts +0 -127
  343. package/dist/project.d.ts.map +0 -1
  344. package/dist/project.js +0 -281
  345. package/dist/project.js.map +0 -1
  346. package/dist/project.test.js +0 -287
  347. package/dist/rag/backends/loop_engine.d.ts +0 -61
  348. package/dist/rag/backends/loop_engine.d.ts.map +0 -1
  349. package/dist/rag/backends/loop_engine.js +0 -160
  350. package/dist/rag/backends/loop_engine.js.map +0 -1
  351. package/dist/recall.d.ts +0 -82
  352. package/dist/recall.d.ts.map +0 -1
  353. package/dist/recall.js +0 -81
  354. package/dist/recall.js.map +0 -1
  355. package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
  356. package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
  357. package/dist/runtime/agent_bridge/autospawn.js +0 -251
  358. package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
  359. package/dist/runtime/chain_state.d.ts +0 -124
  360. package/dist/runtime/chain_state.d.ts.map +0 -1
  361. package/dist/runtime/chain_state.js +0 -189
  362. package/dist/runtime/chain_state.js.map +0 -1
  363. package/dist/runtime/hooks/permission_decision.d.ts +0 -34
  364. package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
  365. package/dist/runtime/hooks/permission_decision.js +0 -39
  366. package/dist/runtime/hooks/permission_decision.js.map +0 -1
  367. package/dist/runtime/workflow_fsm.d.ts +0 -21
  368. package/dist/runtime/workflow_fsm.d.ts.map +0 -1
  369. package/dist/runtime/workflow_fsm.js +0 -25
  370. package/dist/runtime/workflow_fsm.js.map +0 -1
  371. package/dist/runtime/workflow_map.d.ts +0 -26
  372. package/dist/runtime/workflow_map.d.ts.map +0 -1
  373. package/dist/runtime/workflow_map.js +0 -38
  374. package/dist/runtime/workflow_map.js.map +0 -1
  375. package/dist/scope.d.ts +0 -48
  376. package/dist/scope.d.ts.map +0 -1
  377. package/dist/scope.js +0 -111
  378. package/dist/scope.js.map +0 -1
  379. package/dist/setup/cli/topic_create_step.d.ts +0 -84
  380. package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
  381. package/dist/setup/cli/topic_create_step.js +0 -213
  382. package/dist/setup/cli/topic_create_step.js.map +0 -1
  383. package/dist/system-export.d.ts +0 -65
  384. package/dist/system-export.d.ts.map +0 -1
  385. package/dist/system-export.js +0 -194
  386. package/dist/system-export.js.map +0 -1
  387. package/dist/utterance/classifier.d.ts +0 -53
  388. package/dist/utterance/classifier.d.ts.map +0 -1
  389. package/dist/utterance/classifier.js +0 -184
  390. package/dist/utterance/classifier.js.map +0 -1
  391. package/dist/utterance/classifier.test.js +0 -147
@@ -1,368 +0,0 @@
1
- /**
2
- * Anti-drift declarative rule list (0.7.33 — unified-evaluator track).
3
- *
4
- * Per loop/docs/opensquid-anti-drift-unified-evaluator-design.md: one
5
- * declarative `Rule[]` list replaces the per-file hook handlers in
6
- * src/hooks/*. Each rule:
7
- *
8
- * - `id` stable identifier
9
- * - `catches` which D-entry (or "preexisting") this rule covers
10
- * - `hook` which lifecycle event this rule binds to
11
- * - `when` (cheap, synchronous) gate — short-circuits before
12
- * expensive `check` work
13
- * - `check` (async) returns a Verdict
14
- * - `bypass` optional env var that emergency-disables the rule
15
- * - `rationale` one-line agent-facing reason (used in error messages)
16
- *
17
- * Today the check functions DELEGATE to existing src/hooks/* helpers
18
- * (engine-vocab-gate, versioning-gate, workflow-gate, drift-patterns,
19
- * inline-report-check, etc.) so this patch ships the declarative
20
- * SURFACE without re-implementing every gate. The 0.7.35 cutover
21
- * moves the helper bodies into src/anti-drift/* and deletes the old
22
- * per-hook files.
23
- *
24
- * The evaluator (0.7.34) walks this list at each hook event, runs
25
- * applicable rules, aggregates verdicts (most-restrictive wins for
26
- * PreToolUse permissions; all surfaces accumulate at Stop/UPS).
27
- */
28
- import { findDrifts, decide as decideDriftPatterns } from "../hooks/drift-patterns.js";
29
- import { evaluateEngineVocabGate } from "../hooks/engine-vocab-gate.js";
30
- import { isRecallRequired, clearRecallRequired } from "../hooks/heartbeat.js";
31
- import { checkInlineReportFormat } from "../hooks/inline-report-check.js";
32
- import { checkChatSendReportFormat } from "../hooks/pre-tool-use.js";
33
- import { evaluateVersioningGate } from "../hooks/versioning-gate.js";
34
- import { evaluateWorkflowGate } from "../hooks/workflow-gate.js";
35
- // =====================================================================
36
- // Helpers shared across rules
37
- // =====================================================================
38
- function getCommand(ctx) {
39
- const v = ctx.toolInput?.command;
40
- return typeof v === "string" ? v : null;
41
- }
42
- function isBypassed(rule) {
43
- if (!rule.bypass)
44
- return false;
45
- return process.env[rule.bypass] === "1";
46
- }
47
- /** Pass-through verdict factory keeps the rule list legible. */
48
- const PASS = { kind: "pass" };
49
- // =====================================================================
50
- // The rule list (18 entries — matches the design doc)
51
- // =====================================================================
52
- export const RULES = [
53
- // ---------- Hard blocks (PreToolUse) ----------
54
- {
55
- id: "active-task-required",
56
- catches: "D1",
57
- hook: "PreToolUse",
58
- bypass: "OPENSQUID_SKIP_ACTIVE_TASK_GATE",
59
- rationale: "log_phase / chat_send without an in_progress task means the workflow-gate has nothing to validate against — exactly the D1 headline drift.",
60
- when: (ctx) => ctx.toolName === "mcp__opensquid__log_phase" || ctx.toolName === "mcp__opensquid__chat_send",
61
- check: async (ctx) => {
62
- // Delegated to the existing readActiveTaskId for now (transcript
63
- // parsing). 0.7.35 cutover swaps this to state.readActiveTask.
64
- if (!ctx.transcriptPath)
65
- return PASS;
66
- const { readActiveTaskId } = await import("../hooks/transcript.js");
67
- try {
68
- const id = await readActiveTaskId(ctx.transcriptPath);
69
- if (id)
70
- return PASS;
71
- }
72
- catch {
73
- return PASS;
74
- }
75
- return {
76
- kind: "block",
77
- message: `🦑 [opensquid] ${ctx.toolName} called without an in_progress task. Call TaskCreate first.`,
78
- };
79
- },
80
- },
81
- {
82
- id: "never-amend",
83
- catches: "preexisting",
84
- hook: "PreToolUse",
85
- rationale: "git commit --amend hides iteration history per feedback_auto_commit; use a follow-up commit instead.",
86
- when: (ctx) => ctx.toolName === "Bash",
87
- check: async (ctx) => deriveDriftPatternVerdict(ctx, "never-amend"),
88
- },
89
- {
90
- id: "no-implicit-push",
91
- catches: "preexisting",
92
- hook: "PreToolUse",
93
- bypass: "OPENSQUID_SKIP_DRIFT",
94
- rationale: "git push requires explicit user authorization; commits stay local by default.",
95
- when: (ctx) => ctx.toolName === "Bash",
96
- check: async (ctx) => deriveDriftPatternVerdict(ctx, "no-implicit-push"),
97
- },
98
- {
99
- id: "no-force-push-main",
100
- catches: "preexisting",
101
- hook: "PreToolUse",
102
- rationale: "force-push to main/master is destructive and bypasses normal authorization.",
103
- when: (ctx) => ctx.toolName === "Bash",
104
- check: async (ctx) => deriveDriftPatternVerdict(ctx, "no-force-push-main"),
105
- },
106
- {
107
- id: "engine-vocab-leak",
108
- catches: "D6",
109
- hook: "PreToolUse",
110
- bypass: "OPENSQUID_SKIP_ENGINE_VOCAB_GATE",
111
- rationale: "engine artifacts must not name consumer products per feedback_engine_vocabulary_discipline.",
112
- when: (ctx) => ctx.toolName === "Bash" && /\bgit\s+commit\b/.test(getCommand(ctx) ?? ""),
113
- check: async (ctx) => {
114
- const r = await evaluateEngineVocabGate({
115
- cwd: ctx.cwd,
116
- bashCommand: getCommand(ctx) ?? undefined,
117
- });
118
- if (r.block)
119
- return { kind: "block", message: r.stderr };
120
- if (r.stderr)
121
- return { kind: "warn", message: r.stderr };
122
- return PASS;
123
- },
124
- },
125
- {
126
- id: "versioning-bump-required",
127
- catches: "D5+preexisting",
128
- hook: "PreToolUse",
129
- bypass: "OPENSQUID_SKIP_VERSION_GATE",
130
- rationale: "every src commit ships a matching patch bump per feedback_pre1_versioning v4 PATCH-ONLY rule.",
131
- when: (ctx) => ctx.toolName === "Bash" && /\bgit\s+commit\b/.test(getCommand(ctx) ?? ""),
132
- check: async (ctx) => {
133
- const r = await evaluateVersioningGate({ cwd: ctx.cwd });
134
- if (r.block)
135
- return { kind: "block", message: r.stderr };
136
- if (r.stderr)
137
- return { kind: "warn", message: r.stderr };
138
- return PASS;
139
- },
140
- },
141
- {
142
- id: "workflow-phases-required",
143
- catches: "preexisting",
144
- hook: "PreToolUse",
145
- bypass: "OPENSQUID_SKIP_WORKFLOW_GATE",
146
- rationale: "git commit requires required phases logged for the active task per feedback_workflow_cycle.",
147
- when: (ctx) => ctx.toolName === "Bash" && /\bgit\s+commit\b/.test(getCommand(ctx) ?? ""),
148
- check: async (ctx) => {
149
- const r = await evaluateWorkflowGate({ transcriptPath: ctx.transcriptPath });
150
- if (r.block)
151
- return { kind: "block", message: r.stderr };
152
- if (r.stderr)
153
- return { kind: "warn", message: r.stderr };
154
- return PASS;
155
- },
156
- },
157
- {
158
- id: "seven-phase-report-format",
159
- catches: "D3",
160
- hook: "PreToolUse",
161
- rationale: "chat_send body matching the task-report marker must include the PHASES heading per feedback_telegram_reports.",
162
- when: (ctx) => ctx.toolName === "mcp__opensquid__chat_send",
163
- check: async (ctx) => {
164
- const msg = checkChatSendReportFormat({
165
- tool: ctx.toolName ?? "",
166
- input: ctx.toolInput ?? {},
167
- });
168
- return msg ? { kind: "warn", message: msg } : PASS;
169
- },
170
- },
171
- {
172
- id: "heartbeat-recall-required",
173
- catches: "D7",
174
- hook: "PreToolUse",
175
- bypass: "OPENSQUID_SKIP_RECALL_GATE",
176
- rationale: "after a heartbeat surfaces, the next mcp__opensquid__* call must be recall to re-anchor.",
177
- when: (ctx) => typeof ctx.toolName === "string" && ctx.toolName.startsWith("mcp__opensquid__"),
178
- check: async (ctx) => {
179
- if (!ctx.sessionId)
180
- return PASS;
181
- const required = await isRecallRequired(ctx.sessionId);
182
- if (!required)
183
- return PASS;
184
- if (ctx.toolName === "mcp__opensquid__recall") {
185
- await clearRecallRequired(ctx.sessionId);
186
- return PASS;
187
- }
188
- return {
189
- kind: "block",
190
- message: `🦑 [opensquid recall-gate] ${ctx.toolName} blocked — heartbeat surfaced this turn; call mcp__opensquid__recall first.`,
191
- };
192
- },
193
- },
194
- // ---------- Soft warns (PreToolUse) ----------
195
- {
196
- id: "telegram-redirect-report",
197
- catches: "D2",
198
- hook: "PreToolUse",
199
- rationale: "task-completion reports go via mcp__opensquid__chat_send to the project report_channel, not plugin:telegram reply (DM).",
200
- when: (ctx) => ctx.toolName === "mcp__plugin_telegram_telegram__reply",
201
- check: async (ctx) => deriveDriftPatternVerdict(ctx, "telegram-redirect-report"),
202
- },
203
- {
204
- id: "bundled-commit",
205
- catches: "D4",
206
- hook: "PreToolUse",
207
- rationale: "prefer multiple small logical commits over one large catchall per feedback_auto_commit.",
208
- when: (ctx) => ctx.toolName === "Bash",
209
- check: async (ctx) => deriveDriftPatternVerdict(ctx, "bundled-commit"),
210
- },
211
- // ---------- Stop hook ----------
212
- {
213
- id: "inline-report-missing-phases",
214
- catches: "D3-inline",
215
- hook: "Stop",
216
- rationale: "in-session status reports must include the PHASES heading too, not just chat_send — feedback_telegram_reports applies to both surfaces.",
217
- when: (ctx) => Boolean(ctx.assistantText),
218
- check: async (ctx) => {
219
- const v = checkInlineReportFormat(ctx.assistantText ?? "");
220
- if (!v)
221
- return PASS;
222
- return {
223
- kind: "surface",
224
- message: `inline-report-missing-phases: message shape suggests a completion report (version_refs=${v.signals.version_refs}, commit_hashes=${v.signals.hash_refs}) but PHASES heading missing.`,
225
- };
226
- },
227
- },
228
- {
229
- id: "false-stop-haiku",
230
- catches: "D9",
231
- hook: "Stop",
232
- rationale: "trailing pause-prompts violate feedback_full_automation_mode; Claude Code's native prompt-type Stop hook (installed via hooks-cli) handles the Haiku classification + re-prompt loop.",
233
- when: () => false, // implemented as a `type: "prompt"` settings.json entry, not via this Rule.check path
234
- check: async () => PASS,
235
- },
236
- // ---------- UserPromptSubmit ----------
237
- {
238
- id: "multi-task-plan-injection",
239
- catches: "D8",
240
- hook: "UserPromptSubmit",
241
- rationale: "when the user lists multiple task identifiers, mirror back the parsed plan before executing to catch a misread per feedback_user_words_have_top_weight.",
242
- when: (ctx) => typeof ctx.userPrompt === "string",
243
- check: async (ctx) => {
244
- const { detectMultiTaskDirective } = await import("../hooks/user-prompt-submit.js");
245
- const msg = detectMultiTaskDirective(ctx.userPrompt ?? "");
246
- return msg ? { kind: "surface", message: msg } : PASS;
247
- },
248
- },
249
- // ---------- SessionEnd ----------
250
- {
251
- id: "drift-catalog-auto",
252
- catches: "D10",
253
- hook: "SessionEnd",
254
- rationale: "auto-catalog drift markers (user corrections, rule citations, mea culpas) so the dogfood proof writes itself.",
255
- when: () => true,
256
- check: async (ctx) => {
257
- if (!ctx.sessionId)
258
- return PASS;
259
- const { runDriftCatalogScan } = await import("../hooks/drift-catalog.js");
260
- try {
261
- const count = await runDriftCatalogScan({
262
- sessionId: ctx.sessionId,
263
- transcriptPath: ctx.transcriptPath,
264
- cwd: ctx.cwd,
265
- });
266
- if (count > 0) {
267
- return {
268
- kind: "warn",
269
- message: `🦑 [opensquid drift-catalog] recorded ${count} drift marker(s)`,
270
- };
271
- }
272
- }
273
- catch {
274
- /* fail-open per Stop/SessionEnd precedent */
275
- }
276
- return PASS;
277
- },
278
- },
279
- {
280
- id: "session-state-cleanup",
281
- catches: "preexisting",
282
- hook: "SessionEnd",
283
- rationale: "remove per-session ledger + state files to bound disk usage.",
284
- when: () => true,
285
- check: async (ctx) => {
286
- if (!ctx.sessionId)
287
- return PASS;
288
- const { clearSession } = await import("../hooks/honesty-ledger.js");
289
- try {
290
- await clearSession(ctx.sessionId);
291
- }
292
- catch {
293
- /* non-fatal */
294
- }
295
- return PASS;
296
- },
297
- },
298
- // ---------- Honesty + heartbeat (Stop hook auto-actions) ----------
299
- {
300
- id: "honesty-reconcile",
301
- catches: "preexisting",
302
- hook: "Stop",
303
- rationale: "reconcile claims-vs-actions for the just-completed turn; broken promises surface next UPS.",
304
- when: (ctx) => Boolean(ctx.sessionId && ctx.transcriptPath),
305
- check: async () => PASS, // orchestrated by the evaluator (multi-step IO); kept here for catalog completeness
306
- },
307
- {
308
- id: "heartbeat-arm",
309
- catches: "preexisting",
310
- hook: "Stop",
311
- rationale: "arm the heartbeat nudge when the transcript crosses the configured token threshold.",
312
- when: (ctx) => Boolean(ctx.sessionId && ctx.transcriptPath),
313
- check: async () => PASS, // orchestrated by the evaluator; same kept-for-catalog reason
314
- },
315
- ];
316
- // =====================================================================
317
- // Evaluation helpers — used by the evaluator (0.7.34) to walk RULES
318
- // =====================================================================
319
- /**
320
- * Pick the rules that apply to a given hook event AND that aren't
321
- * bypassed. Exported so the evaluator and tests share the same filter.
322
- */
323
- export function rulesForEvent(event) {
324
- return RULES.filter((r) => r.hook === event && !isBypassed(r));
325
- }
326
- /**
327
- * Walk applicable rules and collect verdicts. Stops at the first
328
- * `block` for PreToolUse events; aggregates all surfaces/warns for
329
- * Stop/UPS/SessionEnd events.
330
- */
331
- export async function evaluateRules(ctx) {
332
- const verdicts = [];
333
- for (const rule of rulesForEvent(ctx.hookEvent)) {
334
- if (!rule.when(ctx))
335
- continue;
336
- const v = await rule.check(ctx);
337
- verdicts.push(v);
338
- // Short-circuit on PreToolUse block — match the existing
339
- // most-restrictive-wins semantic.
340
- if (ctx.hookEvent === "PreToolUse" && v.kind === "block")
341
- break;
342
- }
343
- return verdicts;
344
- }
345
- // =====================================================================
346
- // Internal: drift-patterns delegation (shared by 4 rules)
347
- // =====================================================================
348
- /**
349
- * Run the existing drift-patterns catalog against the tool call and
350
- * extract the verdict for a specific pattern id. Returns PASS when
351
- * the pattern doesn't fire OR when the verdict is bypass-only.
352
- *
353
- * Lets RULES delegate to the legacy catalog while presenting the
354
- * declarative shape externally. The 0.7.35 cutover inlines these.
355
- */
356
- async function deriveDriftPatternVerdict(ctx, patternId) {
357
- if (!ctx.toolName)
358
- return PASS;
359
- const call = { tool: ctx.toolName, input: ctx.toolInput ?? {} };
360
- const hits = findDrifts(call).filter((h) => h.pattern.id === patternId);
361
- if (hits.length === 0)
362
- return PASS;
363
- const { exit, stderr } = decideDriftPatterns(hits, call);
364
- if (exit === 2)
365
- return { kind: "block", message: stderr };
366
- return { kind: "warn", message: stderr };
367
- }
368
- //# sourceMappingURL=rules.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src.legacy/anti-drift/rules.ts"],"names":[],"mappings":"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"}
@@ -1,213 +0,0 @@
1
- /**
2
- * Tests for anti-drift/rules.ts (0.7.33 unified-evaluator track).
3
- *
4
- * Validates:
5
- * 1. Catalog shape — every rule has the required fields
6
- * 2. rulesForEvent filtering — by event + bypass env var
7
- * 3. evaluateRules behavior — PreToolUse short-circuits on block
8
- * 4. Specific rule when() predicates fire correctly
9
- *
10
- * Deep behavioral coverage of each rule's check() lives in the
11
- * existing src/hooks/*.test.ts suites (since the rules delegate
12
- * there). The 0.7.35 cutover migrates those tests alongside.
13
- */
14
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
15
- import { RULES, evaluateRules, rulesForEvent, } from "./rules.js";
16
- describe("RULES catalog shape", () => {
17
- it("has at least 16 rules (matches the design doc's 18, allowing 1-2 implementation merges)", () => {
18
- expect(RULES.length).toBeGreaterThanOrEqual(16);
19
- });
20
- it("every rule has all required fields populated", () => {
21
- for (const r of RULES) {
22
- expect(r.id).toBeTruthy();
23
- expect(r.catches).toBeTruthy();
24
- expect(["PreToolUse", "Stop", "UserPromptSubmit", "SessionEnd"]).toContain(r.hook);
25
- expect(typeof r.when).toBe("function");
26
- expect(typeof r.check).toBe("function");
27
- expect(r.rationale).toBeTruthy();
28
- }
29
- });
30
- it("rule ids are unique", () => {
31
- const ids = RULES.map((r) => r.id);
32
- expect(new Set(ids).size).toBe(ids.length);
33
- });
34
- it("covers all 10 drift D-entries (by `catches` field)", () => {
35
- const covered = new Set(RULES.map((r) => r.catches).flatMap((c) => c.split("+").map((s) => s.trim())));
36
- for (const drift of ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "D10"]) {
37
- expect([...covered].some((c) => c === drift || c.startsWith(drift)), `expected catches to include ${drift}`).toBe(true);
38
- }
39
- });
40
- });
41
- describe("rulesForEvent", () => {
42
- it("filters by hook event", () => {
43
- const preRules = rulesForEvent("PreToolUse");
44
- expect(preRules.every((r) => r.hook === "PreToolUse")).toBe(true);
45
- expect(preRules.length).toBeGreaterThan(0);
46
- });
47
- it("excludes bypassed rules", () => {
48
- const target = RULES.find((r) => r.id === "active-task-required");
49
- expect(target).toBeDefined();
50
- expect(target.bypass).toBe("OPENSQUID_SKIP_ACTIVE_TASK_GATE");
51
- process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE = "1";
52
- try {
53
- const ids = rulesForEvent("PreToolUse").map((r) => r.id);
54
- expect(ids).not.toContain("active-task-required");
55
- }
56
- finally {
57
- delete process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE;
58
- }
59
- });
60
- it("bypass env var must be exactly '1' (other values do NOT bypass)", () => {
61
- process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE = "true";
62
- try {
63
- const ids = rulesForEvent("PreToolUse").map((r) => r.id);
64
- expect(ids).toContain("active-task-required");
65
- }
66
- finally {
67
- delete process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE;
68
- }
69
- });
70
- });
71
- describe("rule when() predicates", () => {
72
- const preCtx = (toolName, command) => ({
73
- hookEvent: "PreToolUse",
74
- toolName,
75
- toolInput: command !== undefined ? { command } : {},
76
- });
77
- it("active-task-required fires only on log_phase / chat_send", () => {
78
- const rule = RULES.find((r) => r.id === "active-task-required");
79
- expect(rule.when(preCtx("mcp__opensquid__log_phase"))).toBe(true);
80
- expect(rule.when(preCtx("mcp__opensquid__chat_send"))).toBe(true);
81
- expect(rule.when(preCtx("Bash", "ls"))).toBe(false);
82
- expect(rule.when(preCtx("Read"))).toBe(false);
83
- });
84
- it("never-amend.when fires on Bash tool", () => {
85
- const rule = RULES.find((r) => r.id === "never-amend");
86
- expect(rule.when(preCtx("Bash", "git commit --amend"))).toBe(true);
87
- // Filter happens inside check via drift-patterns; when() just gates by tool name.
88
- expect(rule.when(preCtx("Edit"))).toBe(false);
89
- });
90
- it("engine-vocab-leak fires only on git commit Bash commands", () => {
91
- const rule = RULES.find((r) => r.id === "engine-vocab-leak");
92
- expect(rule.when(preCtx("Bash", "git commit -m 'foo'"))).toBe(true);
93
- expect(rule.when(preCtx("Bash", "git status"))).toBe(false);
94
- expect(rule.when(preCtx("Bash"))).toBe(false);
95
- });
96
- it("heartbeat-recall-required fires on any mcp__opensquid__* tool", () => {
97
- const rule = RULES.find((r) => r.id === "heartbeat-recall-required");
98
- expect(rule.when(preCtx("mcp__opensquid__recall"))).toBe(true);
99
- expect(rule.when(preCtx("mcp__opensquid__log_phase"))).toBe(true);
100
- expect(rule.when(preCtx("mcp__opensquid__chat_send"))).toBe(true);
101
- expect(rule.when(preCtx("mcp__plugin_telegram_telegram__reply"))).toBe(false);
102
- expect(rule.when(preCtx("Bash"))).toBe(false);
103
- });
104
- it("telegram-redirect-report fires only on plugin:telegram reply", () => {
105
- const rule = RULES.find((r) => r.id === "telegram-redirect-report");
106
- expect(rule.when(preCtx("mcp__plugin_telegram_telegram__reply"))).toBe(true);
107
- expect(rule.when(preCtx("mcp__opensquid__chat_send"))).toBe(false);
108
- });
109
- it("inline-report-missing-phases fires on Stop with assistantText", () => {
110
- const rule = RULES.find((r) => r.id === "inline-report-missing-phases");
111
- expect(rule.when({ hookEvent: "Stop", assistantText: "hi" })).toBe(true);
112
- expect(rule.when({ hookEvent: "Stop" })).toBe(false);
113
- });
114
- it("multi-task-plan-injection fires on UPS with userPrompt", () => {
115
- const rule = RULES.find((r) => r.id === "multi-task-plan-injection");
116
- expect(rule.when({ hookEvent: "UserPromptSubmit", userPrompt: "166 then 168" })).toBe(true);
117
- expect(rule.when({ hookEvent: "UserPromptSubmit" })).toBe(false);
118
- });
119
- });
120
- describe("evaluateRules — short-circuit on PreToolUse block", () => {
121
- let originalRules;
122
- beforeEach(() => {
123
- originalRules = [...RULES];
124
- });
125
- afterEach(() => {
126
- RULES.length = 0;
127
- RULES.push(...originalRules);
128
- });
129
- it("PreToolUse: stops at the first block verdict (most-restrictive-wins)", async () => {
130
- // Replace catalog with two fakes that both apply; the first blocks.
131
- RULES.length = 0;
132
- let secondRan = false;
133
- RULES.push({
134
- id: "fake-block",
135
- catches: "test",
136
- hook: "PreToolUse",
137
- when: () => true,
138
- check: async () => ({ kind: "block", message: "first" }),
139
- rationale: "test",
140
- }, {
141
- id: "fake-second",
142
- catches: "test",
143
- hook: "PreToolUse",
144
- when: () => true,
145
- check: async () => {
146
- secondRan = true;
147
- return { kind: "pass" };
148
- },
149
- rationale: "test",
150
- });
151
- const verdicts = await evaluateRules({ hookEvent: "PreToolUse", toolName: "Bash" });
152
- expect(verdicts.length).toBe(1);
153
- expect(verdicts[0]).toEqual({ kind: "block", message: "first" });
154
- expect(secondRan).toBe(false);
155
- });
156
- it("Stop: accumulates all verdicts (no short-circuit on first surface)", async () => {
157
- RULES.length = 0;
158
- RULES.push({
159
- id: "fake-surface-a",
160
- catches: "test",
161
- hook: "Stop",
162
- when: () => true,
163
- check: async () => ({ kind: "surface", message: "a" }),
164
- rationale: "test",
165
- }, {
166
- id: "fake-surface-b",
167
- catches: "test",
168
- hook: "Stop",
169
- when: () => true,
170
- check: async () => ({ kind: "surface", message: "b" }),
171
- rationale: "test",
172
- });
173
- const verdicts = await evaluateRules({ hookEvent: "Stop", assistantText: "test" });
174
- expect(verdicts.length).toBe(2);
175
- expect(verdicts.map((v) => v.kind === "surface" && v.message)).toEqual(["a", "b"]);
176
- });
177
- it("skips rules whose when() returns false", async () => {
178
- RULES.length = 0;
179
- RULES.push({
180
- id: "fake-not-applicable",
181
- catches: "test",
182
- hook: "PreToolUse",
183
- when: () => false,
184
- check: async () => ({ kind: "block", message: "should not fire" }),
185
- rationale: "test",
186
- });
187
- const verdicts = await evaluateRules({ hookEvent: "PreToolUse", toolName: "Bash" });
188
- expect(verdicts).toEqual([]);
189
- });
190
- });
191
- describe("Verdict shape — pass / block / warn / surface", () => {
192
- it("PASS verdicts have no message field", () => {
193
- const v = { kind: "pass" };
194
- expect(v.kind).toBe("pass");
195
- expect("message" in v).toBe(false);
196
- });
197
- it("non-pass verdicts carry a message", () => {
198
- const block = { kind: "block", message: "x" };
199
- const warn = { kind: "warn", message: "y" };
200
- const surface = { kind: "surface", message: "z" };
201
- expect(block.message).toBe("x");
202
- expect(warn.message).toBe("y");
203
- expect(surface.message).toBe("z");
204
- });
205
- });
206
- describe("hook-event coverage", () => {
207
- it("each of the 4 hook events has at least one rule", () => {
208
- for (const event of ["PreToolUse", "Stop", "UserPromptSubmit", "SessionEnd"]) {
209
- const rules = RULES.filter((r) => r.hook === event);
210
- expect(rules.length, `expected at least one rule for ${event}`).toBeGreaterThan(0);
211
- }
212
- });
213
- });