opensquid 0.5.441 → 0.5.447

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 (380) 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/runtime/bootstrap.d.ts.map +1 -1
  11. package/dist/runtime/bootstrap.js +2 -0
  12. package/dist/runtime/bootstrap.js.map +1 -1
  13. package/dist/runtime/handoff/render.d.ts +5 -4
  14. package/dist/runtime/handoff/render.d.ts.map +1 -1
  15. package/dist/runtime/handoff/render.js +7 -7
  16. package/dist/runtime/handoff/render.js.map +1 -1
  17. package/dist/runtime/hooks/active_task_mirror.js +0 -0
  18. package/dist/runtime/hooks/apply_patch.js +0 -0
  19. package/dist/runtime/hooks/dispatch.js +0 -0
  20. package/dist/runtime/hooks/hook_output.js +0 -0
  21. package/dist/runtime/hooks/memory_reconcile.js +0 -0
  22. package/dist/runtime/hooks/new_project_detect.js +0 -0
  23. package/dist/runtime/hooks/profession_resolver.js +0 -0
  24. package/dist/runtime/hooks/scope_intent.js +0 -0
  25. package/dist/runtime/hooks/session_id.js +0 -0
  26. package/dist/runtime/hooks/session_liveness.js +0 -0
  27. package/dist/runtime/hooks/stop_drive.js +0 -0
  28. package/dist/runtime/hooks/stop_stream.js +0 -0
  29. package/dist/runtime/hooks/subagent_guard.js +0 -0
  30. package/dist/runtime/hooks/transcript.js +0 -0
  31. package/dist/runtime/hooks/transcript_tasks.js +0 -0
  32. package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
  33. package/dist/runtime/ralph/orchestrator.js +2 -1
  34. package/dist/runtime/ralph/orchestrator.js.map +1 -1
  35. package/dist/setup/cli/limits_state.d.ts.map +1 -1
  36. package/dist/setup/cli/limits_state.js +6 -40
  37. package/dist/setup/cli/limits_state.js.map +1 -1
  38. package/dist/setup/cli/pack_walk.d.ts +32 -0
  39. package/dist/setup/cli/pack_walk.d.ts.map +1 -0
  40. package/dist/setup/cli/pack_walk.js +76 -0
  41. package/dist/setup/cli/pack_walk.js.map +1 -0
  42. package/dist/setup/cli/permissions_state.d.ts.map +1 -1
  43. package/dist/setup/cli/permissions_state.js +6 -37
  44. package/dist/setup/cli/permissions_state.js.map +1 -1
  45. package/dist/setup/cli/triggers_state.d.ts.map +1 -1
  46. package/dist/setup/cli/triggers_state.js +3 -29
  47. package/dist/setup/cli/triggers_state.js.map +1 -1
  48. package/dist/workgraph/events.d.ts.map +1 -1
  49. package/dist/workgraph/events.js +10 -0
  50. package/dist/workgraph/events.js.map +1 -1
  51. package/dist/workgraph/store.d.ts.map +1 -1
  52. package/dist/workgraph/store.js +5 -0
  53. package/dist/workgraph/store.js.map +1 -1
  54. package/dist/workgraph/types.d.ts +2 -1
  55. package/dist/workgraph/types.d.ts.map +1 -1
  56. package/docs/ARCHITECTURE.md +268 -0
  57. package/package.json +5 -3
  58. package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
  59. package/dist/anti-drift/evaluator.d.ts +0 -88
  60. package/dist/anti-drift/evaluator.d.ts.map +0 -1
  61. package/dist/anti-drift/evaluator.js +0 -417
  62. package/dist/anti-drift/evaluator.js.map +0 -1
  63. package/dist/anti-drift/evaluator.test.js +0 -78
  64. package/dist/anti-drift/rules.d.ts +0 -80
  65. package/dist/anti-drift/rules.d.ts.map +0 -1
  66. package/dist/anti-drift/rules.js +0 -368
  67. package/dist/anti-drift/rules.js.map +0 -1
  68. package/dist/anti-drift/rules.test.js +0 -213
  69. package/dist/anti-drift/state.d.ts +0 -107
  70. package/dist/anti-drift/state.d.ts.map +0 -1
  71. package/dist/anti-drift/state.js +0 -177
  72. package/dist/anti-drift/state.js.map +0 -1
  73. package/dist/anti-drift/state.test.js +0 -120
  74. package/dist/chat/adapters/discord.d.ts +0 -41
  75. package/dist/chat/adapters/discord.d.ts.map +0 -1
  76. package/dist/chat/adapters/discord.js +0 -176
  77. package/dist/chat/adapters/discord.js.map +0 -1
  78. package/dist/chat/adapters/discord.test.js +0 -25
  79. package/dist/chat/adapters/slack.d.ts +0 -43
  80. package/dist/chat/adapters/slack.d.ts.map +0 -1
  81. package/dist/chat/adapters/slack.js +0 -172
  82. package/dist/chat/adapters/slack.js.map +0 -1
  83. package/dist/chat/adapters/slack.test.js +0 -30
  84. package/dist/chat/adapters/telegram.d.ts +0 -148
  85. package/dist/chat/adapters/telegram.d.ts.map +0 -1
  86. package/dist/chat/adapters/telegram.js +0 -498
  87. package/dist/chat/adapters/telegram.js.map +0 -1
  88. package/dist/chat/adapters/telegram.test.js +0 -94
  89. package/dist/chat/config.d.ts +0 -98
  90. package/dist/chat/config.d.ts.map +0 -1
  91. package/dist/chat/config.js +0 -185
  92. package/dist/chat/config.js.map +0 -1
  93. package/dist/chat/daemon/active-project.d.ts +0 -17
  94. package/dist/chat/daemon/active-project.d.ts.map +0 -1
  95. package/dist/chat/daemon/active-project.js +0 -23
  96. package/dist/chat/daemon/active-project.js.map +0 -1
  97. package/dist/chat/daemon/autospawn.d.ts +0 -40
  98. package/dist/chat/daemon/autospawn.d.ts.map +0 -1
  99. package/dist/chat/daemon/autospawn.js +0 -129
  100. package/dist/chat/daemon/autospawn.js.map +0 -1
  101. package/dist/chat/daemon/autospawn.test.js +0 -112
  102. package/dist/chat/daemon/cli.d.ts +0 -18
  103. package/dist/chat/daemon/cli.d.ts.map +0 -1
  104. package/dist/chat/daemon/cli.js +0 -71
  105. package/dist/chat/daemon/cli.js.map +0 -1
  106. package/dist/chat/daemon/collisions.js +0 -384
  107. package/dist/chat/daemon/health-check.d.ts +0 -69
  108. package/dist/chat/daemon/health-check.d.ts.map +0 -1
  109. package/dist/chat/daemon/health-check.js +0 -112
  110. package/dist/chat/daemon/health-check.js.map +0 -1
  111. package/dist/chat/daemon/inbox-read.d.ts +0 -35
  112. package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
  113. package/dist/chat/daemon/inbox-read.js +0 -75
  114. package/dist/chat/daemon/inbox-read.js.map +0 -1
  115. package/dist/chat/daemon/inbox-read.test.js +0 -97
  116. package/dist/chat/daemon/inbox.d.ts +0 -63
  117. package/dist/chat/daemon/inbox.d.ts.map +0 -1
  118. package/dist/chat/daemon/inbox.js +0 -56
  119. package/dist/chat/daemon/inbox.js.map +0 -1
  120. package/dist/chat/daemon/inbox.test.js +0 -110
  121. package/dist/chat/daemon/lifecycle.d.ts +0 -71
  122. package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
  123. package/dist/chat/daemon/lifecycle.js +0 -221
  124. package/dist/chat/daemon/lifecycle.js.map +0 -1
  125. package/dist/chat/daemon/lifecycle.test.js +0 -163
  126. package/dist/chat/daemon/protocol.d.ts +0 -107
  127. package/dist/chat/daemon/protocol.d.ts.map +0 -1
  128. package/dist/chat/daemon/protocol.js +0 -54
  129. package/dist/chat/daemon/protocol.js.map +0 -1
  130. package/dist/chat/daemon/routing.d.ts +0 -140
  131. package/dist/chat/daemon/routing.d.ts.map +0 -1
  132. package/dist/chat/daemon/routing.js +0 -198
  133. package/dist/chat/daemon/routing.js.map +0 -1
  134. package/dist/chat/daemon/routing.test.js +0 -259
  135. package/dist/chat/daemon/rpc-client.d.ts +0 -45
  136. package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
  137. package/dist/chat/daemon/rpc-client.js +0 -133
  138. package/dist/chat/daemon/rpc-client.js.map +0 -1
  139. package/dist/chat/daemon/rpc-server.d.ts +0 -39
  140. package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
  141. package/dist/chat/daemon/rpc-server.js +0 -385
  142. package/dist/chat/daemon/rpc-server.js.map +0 -1
  143. package/dist/chat/daemon/rpc.test.js +0 -177
  144. package/dist/chat/daemon/subscribers.js +0 -257
  145. package/dist/chat/daemon/worker.d.ts +0 -27
  146. package/dist/chat/daemon/worker.d.ts.map +0 -1
  147. package/dist/chat/daemon/worker.js +0 -313
  148. package/dist/chat/daemon/worker.js.map +0 -1
  149. package/dist/chat/daemon/workspace-topic.js +0 -324
  150. package/dist/chat/env-token.d.ts +0 -60
  151. package/dist/chat/env-token.d.ts.map +0 -1
  152. package/dist/chat/env-token.js +0 -137
  153. package/dist/chat/env-token.js.map +0 -1
  154. package/dist/chat/env-token.test.js +0 -160
  155. package/dist/chat/factory.d.ts +0 -30
  156. package/dist/chat/factory.d.ts.map +0 -1
  157. package/dist/chat/factory.js +0 -50
  158. package/dist/chat/factory.js.map +0 -1
  159. package/dist/chat/factory.test.js +0 -55
  160. package/dist/chat/gateway.d.ts +0 -176
  161. package/dist/chat/gateway.d.ts.map +0 -1
  162. package/dist/chat/gateway.js +0 -146
  163. package/dist/chat/gateway.js.map +0 -1
  164. package/dist/chat/gateway.test.js +0 -192
  165. package/dist/claude-md.d.ts +0 -39
  166. package/dist/claude-md.d.ts.map +0 -1
  167. package/dist/claude-md.js +0 -113
  168. package/dist/claude-md.js.map +0 -1
  169. package/dist/claude-md.test.js +0 -91
  170. package/dist/codex/activate.d.ts +0 -66
  171. package/dist/codex/activate.d.ts.map +0 -1
  172. package/dist/codex/activate.js +0 -329
  173. package/dist/codex/activate.js.map +0 -1
  174. package/dist/codex/activate.test.js +0 -229
  175. package/dist/codex/bundled-default/bundled-default.test.js +0 -161
  176. package/dist/codex/cli-publish.test.js +0 -133
  177. package/dist/codex/cli.d.ts +0 -35
  178. package/dist/codex/cli.d.ts.map +0 -1
  179. package/dist/codex/cli.js +0 -554
  180. package/dist/codex/cli.js.map +0 -1
  181. package/dist/codex/cli.test.js +0 -277
  182. package/dist/codex/import-skill-md.d.ts +0 -53
  183. package/dist/codex/import-skill-md.d.ts.map +0 -1
  184. package/dist/codex/import-skill-md.js +0 -236
  185. package/dist/codex/import-skill-md.js.map +0 -1
  186. package/dist/codex/import-skill-md.test.js +0 -225
  187. package/dist/codex/loader.d.ts +0 -27
  188. package/dist/codex/loader.d.ts.map +0 -1
  189. package/dist/codex/loader.js +0 -86
  190. package/dist/codex/loader.js.map +0 -1
  191. package/dist/codex/loader.test.js +0 -75
  192. package/dist/codex/parse.d.ts +0 -28
  193. package/dist/codex/parse.d.ts.map +0 -1
  194. package/dist/codex/parse.js +0 -309
  195. package/dist/codex/parse.js.map +0 -1
  196. package/dist/codex/parse.test.js +0 -241
  197. package/dist/codex/store.d.ts +0 -87
  198. package/dist/codex/store.d.ts.map +0 -1
  199. package/dist/codex/store.js +0 -205
  200. package/dist/codex/store.js.map +0 -1
  201. package/dist/codex/store.test.js +0 -242
  202. package/dist/codex/types.d.ts +0 -398
  203. package/dist/codex/types.d.ts.map +0 -1
  204. package/dist/codex/types.js +0 -21
  205. package/dist/codex/types.js.map +0 -1
  206. package/dist/config.d.ts +0 -53
  207. package/dist/config.d.ts.map +0 -1
  208. package/dist/config.js +0 -202
  209. package/dist/config.js.map +0 -1
  210. package/dist/config.test.js +0 -117
  211. package/dist/engine/cli.d.ts +0 -14
  212. package/dist/engine/cli.d.ts.map +0 -1
  213. package/dist/engine/cli.js +0 -171
  214. package/dist/engine/cli.js.map +0 -1
  215. package/dist/engine/client.d.ts +0 -219
  216. package/dist/engine/client.d.ts.map +0 -1
  217. package/dist/engine/client.js +0 -312
  218. package/dist/engine/client.js.map +0 -1
  219. package/dist/engine/config.d.ts +0 -62
  220. package/dist/engine/config.d.ts.map +0 -1
  221. package/dist/engine/config.js +0 -223
  222. package/dist/engine/config.js.map +0 -1
  223. package/dist/engine/index.d.ts +0 -17
  224. package/dist/engine/index.d.ts.map +0 -1
  225. package/dist/engine/index.js +0 -16
  226. package/dist/engine/index.js.map +0 -1
  227. package/dist/engine/resolver.d.ts +0 -62
  228. package/dist/engine/resolver.d.ts.map +0 -1
  229. package/dist/engine/resolver.js +0 -103
  230. package/dist/engine/resolver.js.map +0 -1
  231. package/dist/engine/singleton.d.ts +0 -95
  232. package/dist/engine/singleton.d.ts.map +0 -1
  233. package/dist/engine/singleton.js +0 -325
  234. package/dist/engine/singleton.js.map +0 -1
  235. package/dist/engine/types.d.ts +0 -402
  236. package/dist/engine/types.d.ts.map +0 -1
  237. package/dist/engine/types.js +0 -22
  238. package/dist/engine/types.js.map +0 -1
  239. package/dist/engine-binary-resolver.js +0 -110
  240. package/dist/engine-binary-resolver.test.js +0 -61
  241. package/dist/engine-cli.js +0 -60
  242. package/dist/engine-client.js +0 -301
  243. package/dist/engine-client.test.js +0 -118
  244. package/dist/functions/chain_state.d.ts +0 -51
  245. package/dist/functions/chain_state.d.ts.map +0 -1
  246. package/dist/functions/chain_state.js +0 -59
  247. package/dist/functions/chain_state.js.map +0 -1
  248. package/dist/hooks/drift-catalog.d.ts +0 -68
  249. package/dist/hooks/drift-catalog.d.ts.map +0 -1
  250. package/dist/hooks/drift-catalog.js +0 -184
  251. package/dist/hooks/drift-catalog.js.map +0 -1
  252. package/dist/hooks/drift-catalog.test.js +0 -154
  253. package/dist/hooks/drift-patterns.d.ts +0 -110
  254. package/dist/hooks/drift-patterns.d.ts.map +0 -1
  255. package/dist/hooks/drift-patterns.js +0 -289
  256. package/dist/hooks/drift-patterns.js.map +0 -1
  257. package/dist/hooks/drift-patterns.test.js +0 -325
  258. package/dist/hooks/engine-vocab-gate.d.ts +0 -108
  259. package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
  260. package/dist/hooks/engine-vocab-gate.js +0 -225
  261. package/dist/hooks/engine-vocab-gate.js.map +0 -1
  262. package/dist/hooks/engine-vocab-gate.test.js +0 -170
  263. package/dist/hooks/heartbeat.d.ts +0 -107
  264. package/dist/hooks/heartbeat.d.ts.map +0 -1
  265. package/dist/hooks/heartbeat.js +0 -316
  266. package/dist/hooks/heartbeat.js.map +0 -1
  267. package/dist/hooks/heartbeat.test.js +0 -393
  268. package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
  269. package/dist/hooks/honesty-ledger.d.ts +0 -123
  270. package/dist/hooks/honesty-ledger.d.ts.map +0 -1
  271. package/dist/hooks/honesty-ledger.js +0 -226
  272. package/dist/hooks/honesty-ledger.js.map +0 -1
  273. package/dist/hooks/honesty-ledger.test.js +0 -466
  274. package/dist/hooks/inline-report-check.d.ts +0 -63
  275. package/dist/hooks/inline-report-check.d.ts.map +0 -1
  276. package/dist/hooks/inline-report-check.js +0 -88
  277. package/dist/hooks/inline-report-check.js.map +0 -1
  278. package/dist/hooks/inline-report-check.test.js +0 -96
  279. package/dist/hooks/pre-tool-use.d.ts +0 -62
  280. package/dist/hooks/pre-tool-use.d.ts.map +0 -1
  281. package/dist/hooks/pre-tool-use.js +0 -342
  282. package/dist/hooks/pre-tool-use.js.map +0 -1
  283. package/dist/hooks/pre-tool-use.test.js +0 -134
  284. package/dist/hooks/session-end.d.ts +0 -15
  285. package/dist/hooks/session-end.d.ts.map +0 -1
  286. package/dist/hooks/session-end.js +0 -60
  287. package/dist/hooks/session-end.js.map +0 -1
  288. package/dist/hooks/session-end.test.js +0 -52
  289. package/dist/hooks/stop.d.ts +0 -35
  290. package/dist/hooks/stop.d.ts.map +0 -1
  291. package/dist/hooks/stop.js +0 -136
  292. package/dist/hooks/stop.js.map +0 -1
  293. package/dist/hooks/transcript-active-task.test.js +0 -342
  294. package/dist/hooks/transcript.d.ts +0 -26
  295. package/dist/hooks/transcript.d.ts.map +0 -1
  296. package/dist/hooks/transcript.js +0 -266
  297. package/dist/hooks/transcript.js.map +0 -1
  298. package/dist/hooks/transcript.test.js +0 -103
  299. package/dist/hooks/user-prompt-submit.d.ts +0 -74
  300. package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
  301. package/dist/hooks/user-prompt-submit.js +0 -256
  302. package/dist/hooks/user-prompt-submit.js.map +0 -1
  303. package/dist/hooks/user-prompt-submit.test.js +0 -118
  304. package/dist/hooks/versioning-gate.d.ts +0 -101
  305. package/dist/hooks/versioning-gate.d.ts.map +0 -1
  306. package/dist/hooks/versioning-gate.js +0 -245
  307. package/dist/hooks/versioning-gate.js.map +0 -1
  308. package/dist/hooks/versioning-gate.test.js +0 -368
  309. package/dist/hooks/workflow-gate.d.ts +0 -64
  310. package/dist/hooks/workflow-gate.d.ts.map +0 -1
  311. package/dist/hooks/workflow-gate.js +0 -152
  312. package/dist/hooks/workflow-gate.js.map +0 -1
  313. package/dist/hooks/workflow-gate.test.js +0 -197
  314. package/dist/hooks-cli.d.ts +0 -25
  315. package/dist/hooks-cli.d.ts.map +0 -1
  316. package/dist/hooks-cli.js +0 -286
  317. package/dist/hooks-cli.js.map +0 -1
  318. package/dist/hooks-cli.test.js +0 -148
  319. package/dist/origin.d.ts +0 -16
  320. package/dist/origin.d.ts.map +0 -1
  321. package/dist/origin.js +0 -92
  322. package/dist/origin.js.map +0 -1
  323. package/dist/packs/seed_lessons_ingest.d.ts +0 -30
  324. package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
  325. package/dist/packs/seed_lessons_ingest.js +0 -107
  326. package/dist/packs/seed_lessons_ingest.js.map +0 -1
  327. package/dist/project-cli.d.ts +0 -7
  328. package/dist/project-cli.d.ts.map +0 -1
  329. package/dist/project-cli.js +0 -145
  330. package/dist/project-cli.js.map +0 -1
  331. package/dist/project.d.ts +0 -127
  332. package/dist/project.d.ts.map +0 -1
  333. package/dist/project.js +0 -281
  334. package/dist/project.js.map +0 -1
  335. package/dist/project.test.js +0 -287
  336. package/dist/rag/backends/loop_engine.d.ts +0 -61
  337. package/dist/rag/backends/loop_engine.d.ts.map +0 -1
  338. package/dist/rag/backends/loop_engine.js +0 -160
  339. package/dist/rag/backends/loop_engine.js.map +0 -1
  340. package/dist/recall.d.ts +0 -82
  341. package/dist/recall.d.ts.map +0 -1
  342. package/dist/recall.js +0 -81
  343. package/dist/recall.js.map +0 -1
  344. package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
  345. package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
  346. package/dist/runtime/agent_bridge/autospawn.js +0 -251
  347. package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
  348. package/dist/runtime/chain_state.d.ts +0 -124
  349. package/dist/runtime/chain_state.d.ts.map +0 -1
  350. package/dist/runtime/chain_state.js +0 -189
  351. package/dist/runtime/chain_state.js.map +0 -1
  352. package/dist/runtime/hooks/permission_decision.d.ts +0 -34
  353. package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
  354. package/dist/runtime/hooks/permission_decision.js +0 -39
  355. package/dist/runtime/hooks/permission_decision.js.map +0 -1
  356. package/dist/runtime/workflow_fsm.d.ts +0 -21
  357. package/dist/runtime/workflow_fsm.d.ts.map +0 -1
  358. package/dist/runtime/workflow_fsm.js +0 -25
  359. package/dist/runtime/workflow_fsm.js.map +0 -1
  360. package/dist/runtime/workflow_map.d.ts +0 -26
  361. package/dist/runtime/workflow_map.d.ts.map +0 -1
  362. package/dist/runtime/workflow_map.js +0 -38
  363. package/dist/runtime/workflow_map.js.map +0 -1
  364. package/dist/scope.d.ts +0 -48
  365. package/dist/scope.d.ts.map +0 -1
  366. package/dist/scope.js +0 -111
  367. package/dist/scope.js.map +0 -1
  368. package/dist/setup/cli/topic_create_step.d.ts +0 -84
  369. package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
  370. package/dist/setup/cli/topic_create_step.js +0 -213
  371. package/dist/setup/cli/topic_create_step.js.map +0 -1
  372. package/dist/system-export.d.ts +0 -65
  373. package/dist/system-export.d.ts.map +0 -1
  374. package/dist/system-export.js +0 -194
  375. package/dist/system-export.js.map +0 -1
  376. package/dist/utterance/classifier.d.ts +0 -53
  377. package/dist/utterance/classifier.d.ts.map +0 -1
  378. package/dist/utterance/classifier.js +0 -184
  379. package/dist/utterance/classifier.js.map +0 -1
  380. package/dist/utterance/classifier.test.js +0 -147
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-end.d.ts","sourceRoot":"","sources":["../../src.legacy/hooks/session-end.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA+CvD"}
@@ -1,60 +0,0 @@
1
- /**
2
- * `opensquid hook session-end` — Claude Code SessionEnd hook handler.
3
- *
4
- * Fires when a Claude Code session terminates. Wipes the session's
5
- * honesty-ledger state (turn-ledger.jsonl + broken-promises.jsonl) so
6
- * the ledger doesn't grow unbounded across all-time sessions.
7
- *
8
- * Without this hook, every Claude Code session creates a
9
- * <data-root>/sessions/<id>/ directory with two JSONL files that
10
- * never get cleaned up. Disk usage grows linearly with session count.
11
- *
12
- * Exit 0 always — SessionEnd is cleanup, not blocking.
13
- */
14
- import { runDriftCatalogScan } from "./drift-catalog.js";
15
- import { clearSession } from "./honesty-ledger.js";
16
- export async function runSessionEndHook() {
17
- let raw = "";
18
- for await (const chunk of process.stdin) {
19
- raw += chunk;
20
- }
21
- if (!raw.trim())
22
- process.exit(0);
23
- let payload;
24
- try {
25
- payload = JSON.parse(raw);
26
- }
27
- catch {
28
- process.exit(0);
29
- }
30
- const sessionId = payload.session_id;
31
- if (!sessionId)
32
- process.exit(0);
33
- // 0.7.22 / D10 — automated drift catalog. Scan the transcript for
34
- // drift markers (user corrections, locked-rule citations, agent
35
- // mea-culpas) and append to the project's drift-catalog.jsonl. Runs
36
- // BEFORE clearSession so any session-scoped state used for context
37
- // is still available.
38
- try {
39
- const count = await runDriftCatalogScan({
40
- sessionId,
41
- transcriptPath: payload.transcript_path,
42
- cwd: payload.cwd,
43
- });
44
- if (count > 0) {
45
- process.stderr.write(`🦑 [opensquid drift-catalog] recorded ${count} drift marker(s)\n`);
46
- }
47
- }
48
- catch (err) {
49
- process.stderr.write(`[opensquid hook session-end] drift-catalog scan failed (non-fatal): ${err instanceof Error ? err.message : err}\n`);
50
- }
51
- try {
52
- await clearSession(sessionId);
53
- }
54
- catch (err) {
55
- // Cleanup failure is non-fatal — disk-space leak, not a correctness bug.
56
- process.stderr.write(`[opensquid hook session-end] clearSession failed: ${err instanceof Error ? err.message : err}\n`);
57
- }
58
- process.exit(0);
59
- }
60
- //# sourceMappingURL=session-end.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-end.js","sourceRoot":"","sources":["../../src.legacy/hooks/session-end.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAUnD,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,GAAG,IAAI,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEjC,IAAI,OAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IACrC,IAAI,CAAC,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEhC,kEAAkE;IAClE,gEAAgE;IAChE,oEAAoE;IACpE,mEAAmE;IACnE,sBAAsB;IACtB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC;YACtC,SAAS;YACT,cAAc,EAAE,OAAO,CAAC,eAAe;YACvC,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QACH,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,KAAK,oBAAoB,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uEAAuE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CACpH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,yEAAyE;QACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qDAAqD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAClG,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -1,52 +0,0 @@
1
- import * as crypto from "node:crypto";
2
- import { promises as fs } from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
- import { clearSession, readBrokenPromises, readTurnLedger, recordBrokenPromise, recordToolCall, } from "./honesty-ledger.js";
7
- let tmpRoot;
8
- const SESSION = "session-end-test";
9
- beforeEach(async () => {
10
- tmpRoot = path.join(os.tmpdir(), `oscli-session-end-${crypto.randomUUID()}`);
11
- await fs.mkdir(tmpRoot, { recursive: true });
12
- });
13
- afterEach(async () => {
14
- await fs.rm(tmpRoot, { recursive: true, force: true });
15
- });
16
- describe("SessionEnd cleanup (clearSession via the hook)", () => {
17
- it("wipes both ledger and broken-promises after population", async () => {
18
- await recordToolCall(SESSION, "Bash", "npm test", { dataRoot: tmpRoot });
19
- await recordToolCall(SESSION, "Bash", "git commit -m foo", { dataRoot: tmpRoot });
20
- await recordBrokenPromise(SESSION, {
21
- ts: "2026-05-15T00:00:00Z",
22
- claim_id: "fake",
23
- claim_label: "x",
24
- matched_text: "y",
25
- reason: "z",
26
- }, { dataRoot: tmpRoot });
27
- // Sanity-check the precondition.
28
- expect((await readTurnLedger(SESSION, { dataRoot: tmpRoot })).length).toBe(2);
29
- expect((await readBrokenPromises(SESSION, { dataRoot: tmpRoot })).length).toBe(1);
30
- // SessionEnd action.
31
- await clearSession(SESSION, { dataRoot: tmpRoot });
32
- expect(await readTurnLedger(SESSION, { dataRoot: tmpRoot })).toEqual([]);
33
- expect(await readBrokenPromises(SESSION, { dataRoot: tmpRoot })).toEqual([]);
34
- });
35
- it("session directory remains, just empty of ledger files", async () => {
36
- await recordToolCall(SESSION, "Bash", "ls", { dataRoot: tmpRoot });
37
- await clearSession(SESSION, { dataRoot: tmpRoot });
38
- // The sessions/<id>/ directory itself is preserved (cheap),
39
- // only the JSONL files inside are removed.
40
- const dir = path.join(tmpRoot, "sessions", SESSION);
41
- await expect(fs.access(dir)).resolves.toBeUndefined();
42
- const entries = await fs.readdir(dir);
43
- expect(entries).toEqual([]);
44
- });
45
- it("does NOT touch other sessions' ledgers", async () => {
46
- await recordToolCall(SESSION, "Bash", "ls", { dataRoot: tmpRoot });
47
- await recordToolCall("other-session", "Bash", "ls", { dataRoot: tmpRoot });
48
- await clearSession(SESSION, { dataRoot: tmpRoot });
49
- expect((await readTurnLedger(SESSION, { dataRoot: tmpRoot })).length).toBe(0);
50
- expect((await readTurnLedger("other-session", { dataRoot: tmpRoot })).length).toBe(1);
51
- });
52
- });
@@ -1,35 +0,0 @@
1
- /**
2
- * `opensquid hook stop` — Claude Code Stop hook handler.
3
- *
4
- * Fires at the end of every assistant turn. Two responsibilities:
5
- *
6
- * 1. Honesty ledger reconciliation: cross-reference the assistant's
7
- * final message against the session's accumulated tool-call ledger.
8
- * Any unfulfilled claim is recorded as a broken promise that the
9
- * next turn's UserPromptSubmit hook surfaces back to the agent.
10
- *
11
- * 2. Token-threshold heartbeat: estimate transcript token count, and
12
- * if the conversation has grown past the configured threshold
13
- * since the last checkpoint, arm a pending heartbeat marker so
14
- * the next UserPromptSubmit hook injects a re-anchor nudge into
15
- * the agent's context. The agent (already authenticated and in
16
- * the loop) does the actual recall + classify work inline.
17
- *
18
- * Exit 0 always — Stop hook is observational, not blocking.
19
- *
20
- * Wired in ~/.claude/settings.json:
21
- *
22
- * "Stop": [
23
- * { "hooks": [{
24
- * "type": "command",
25
- * "command": "node /path/to/opensquid/dist/index.js hook stop"
26
- * }] }
27
- * ]
28
- *
29
- * Pre-#124: this hook also spawned a detached LLM-classifier subprocess.
30
- * Removed in favor of the heartbeat path — opensquid stays in-MCP-ecosystem
31
- * (no external LLM dependency, no subprocess), and the agent does the
32
- * classification work inline per CLAUDE.md classify-and-act rules.
33
- */
34
- export declare function runStopHook(): Promise<void>;
35
- //# sourceMappingURL=stop.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../src.legacy/hooks/stop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAmBH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA4GjD"}
@@ -1,136 +0,0 @@
1
- /**
2
- * `opensquid hook stop` — Claude Code Stop hook handler.
3
- *
4
- * Fires at the end of every assistant turn. Two responsibilities:
5
- *
6
- * 1. Honesty ledger reconciliation: cross-reference the assistant's
7
- * final message against the session's accumulated tool-call ledger.
8
- * Any unfulfilled claim is recorded as a broken promise that the
9
- * next turn's UserPromptSubmit hook surfaces back to the agent.
10
- *
11
- * 2. Token-threshold heartbeat: estimate transcript token count, and
12
- * if the conversation has grown past the configured threshold
13
- * since the last checkpoint, arm a pending heartbeat marker so
14
- * the next UserPromptSubmit hook injects a re-anchor nudge into
15
- * the agent's context. The agent (already authenticated and in
16
- * the loop) does the actual recall + classify work inline.
17
- *
18
- * Exit 0 always — Stop hook is observational, not blocking.
19
- *
20
- * Wired in ~/.claude/settings.json:
21
- *
22
- * "Stop": [
23
- * { "hooks": [{
24
- * "type": "command",
25
- * "command": "node /path/to/opensquid/dist/index.js hook stop"
26
- * }] }
27
- * ]
28
- *
29
- * Pre-#124: this hook also spawned a detached LLM-classifier subprocess.
30
- * Removed in favor of the heartbeat path — opensquid stays in-MCP-ecosystem
31
- * (no external LLM dependency, no subprocess), and the agent does the
32
- * classification work inline per CLAUDE.md classify-and-act rules.
33
- */
34
- import { clearTurnLedger, reconcile, readBrokenPromises, readTurnLedger, recordBrokenPromise, } from "./honesty-ledger.js";
35
- import { checkAndMaybeArm } from "./heartbeat.js";
36
- import { checkInlineReportFormat } from "./inline-report-check.js";
37
- import { readLastAssistantText } from "./transcript.js";
38
- export async function runStopHook() {
39
- let raw = "";
40
- for await (const chunk of process.stdin) {
41
- raw += chunk;
42
- }
43
- if (!raw.trim()) {
44
- process.exit(0);
45
- }
46
- let payload;
47
- try {
48
- payload = JSON.parse(raw);
49
- }
50
- catch {
51
- process.stderr.write("[opensquid hook stop] malformed input — proceeding\n");
52
- process.exit(0);
53
- }
54
- const sessionId = payload.session_id;
55
- if (!sessionId)
56
- process.exit(0);
57
- // -- (1) Honesty-ledger reconcile ----------------------------------
58
- const assistantText = payload.transcript_path
59
- ? await readLastAssistantText(payload.transcript_path)
60
- : "";
61
- const ledger = await readTurnLedger(sessionId);
62
- const broken = reconcile(assistantText, ledger);
63
- const existing = await readBrokenPromises(sessionId);
64
- const existingKeys = new Set(existing.map((p) => `${p.claim_id}|${p.matched_text}`));
65
- const fresh = [];
66
- for (const promise of broken) {
67
- const key = `${promise.claim_id}|${promise.matched_text}`;
68
- if (existingKeys.has(key))
69
- continue;
70
- fresh.push(promise);
71
- try {
72
- await recordBrokenPromise(sessionId, promise);
73
- }
74
- catch (err) {
75
- process.stderr.write(`[opensquid hook stop] failed to record promise: ${err instanceof Error ? err.message : err}\n`);
76
- }
77
- }
78
- if (fresh.length > 0) {
79
- for (const p of fresh) {
80
- process.stderr.write(`🦑 [opensquid honesty] ${p.claim_id}: ${p.reason}\n`);
81
- }
82
- }
83
- // 0.7.30 / D3 follow-up — when the agent writes a completion-report-
84
- // shaped status update inline (vs. via mcp__opensquid__chat_send),
85
- // D3's chat_send check doesn't fire. Catch the inline case here at
86
- // Stop time and surface as a broken-promise next turn.
87
- if (assistantText) {
88
- const inlineViolation = checkInlineReportFormat(assistantText);
89
- if (inlineViolation) {
90
- const broken = {
91
- ts: new Date().toISOString(),
92
- claim_id: "inline-report-missing-phases",
93
- claim_label: "PHASES block per [[feedback_telegram_reports]]",
94
- matched_text: inlineViolation.matched_text,
95
- reason: `inline message shape suggests a completion report ` +
96
- `(version_refs=${inlineViolation.signals.version_refs}, ` +
97
- `commit_hashes=${inlineViolation.signals.hash_refs}) but the ` +
98
- `PHASES heading is missing. Catches D3 inline variant.`,
99
- };
100
- try {
101
- await recordBrokenPromise(sessionId, broken);
102
- process.stderr.write(`🦑 [opensquid honesty] ${broken.claim_id}: ${broken.reason}\n`);
103
- }
104
- catch (err) {
105
- process.stderr.write(`[opensquid hook stop] inline-report check write failed: ${err instanceof Error ? err.message : err}\n`);
106
- }
107
- }
108
- }
109
- // 0.7.8 (#162): clear the turn-ledger AFTER reconciliation so the
110
- // next turn's claims reconcile against ONLY that turn's tool calls.
111
- // Previously the ledger only cleared at SessionEnd, which meant
112
- // yesterday's git push satisfied today's "I'll push" claim on long
113
- // resumed sessions — the load-bearing #160 finding for ledger drift.
114
- try {
115
- await clearTurnLedger(sessionId);
116
- }
117
- catch (err) {
118
- process.stderr.write(`[opensquid hook stop] turn-ledger clear failed (non-fatal): ${err instanceof Error ? err.message : err}\n`);
119
- }
120
- // -- (2) Token-threshold heartbeat ---------------------------------
121
- if (payload.transcript_path) {
122
- try {
123
- const armed = await checkAndMaybeArm(sessionId, payload.transcript_path);
124
- if (armed) {
125
- // Surface to stderr too so the user sees that opensquid noticed
126
- // drift (in addition to the agent seeing it next turn via UPS).
127
- process.stderr.write(`🦑 [opensquid heartbeat-armed] ${armed}\n`);
128
- }
129
- }
130
- catch (err) {
131
- process.stderr.write(`[opensquid hook stop] heartbeat check failed (non-fatal): ${err instanceof Error ? err.message : err}\n`);
132
- }
133
- }
134
- process.exit(0);
135
- }
136
- //# sourceMappingURL=stop.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src.legacy/hooks/stop.ts"],"names":[],"mappings":"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"}
@@ -1,342 +0,0 @@
1
- /**
2
- * Tests for `readActiveTaskId` — the transcript-walking helper that
3
- * finds the most-recent TodoWrite in_progress task id. Used by the
4
- * workflow gate to figure out which task's phase ledger to query.
5
- */
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import * as crypto from "node:crypto";
8
- import { promises as fs } from "node:fs";
9
- import * as os from "node:os";
10
- import * as path from "node:path";
11
- import { fileURLToPath } from "node:url";
12
- import { readActiveTaskId } from "./transcript.js";
13
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
- let tmpDir;
15
- let transcriptPath;
16
- beforeEach(async () => {
17
- tmpDir = path.join(os.tmpdir(), `opensquid-tx-${crypto.randomUUID()}`);
18
- await fs.mkdir(tmpDir, { recursive: true });
19
- transcriptPath = path.join(tmpDir, "transcript.jsonl");
20
- });
21
- afterEach(async () => {
22
- await fs.rm(tmpDir, { recursive: true, force: true });
23
- });
24
- async function writeEvents(events) {
25
- const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
26
- await fs.writeFile(transcriptPath, lines, "utf8");
27
- }
28
- function todoWriteEvent(todos) {
29
- return {
30
- type: "assistant",
31
- message: {
32
- role: "assistant",
33
- content: [
34
- {
35
- type: "tool_use",
36
- name: "TodoWrite",
37
- input: { todos },
38
- },
39
- ],
40
- },
41
- };
42
- }
43
- describe("readActiveTaskId", () => {
44
- it("returns null when transcript doesn't exist", async () => {
45
- expect(await readActiveTaskId(path.join(tmpDir, "missing.jsonl"))).toBeNull();
46
- });
47
- it("returns null when transcript is empty", async () => {
48
- await fs.writeFile(transcriptPath, "", "utf8");
49
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
50
- });
51
- it("returns null when no TodoWrite block exists", async () => {
52
- await writeEvents([
53
- { type: "user", message: { role: "user", content: "hi" } },
54
- { type: "assistant", message: { role: "assistant", content: "hello" } },
55
- ]);
56
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
57
- });
58
- it("returns null when TodoWrite has no in_progress items", async () => {
59
- await writeEvents([
60
- todoWriteEvent([
61
- { id: "1", status: "completed" },
62
- { id: "2", status: "pending" },
63
- ]),
64
- ]);
65
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
66
- });
67
- it("returns the in_progress task id from a single TodoWrite", async () => {
68
- await writeEvents([
69
- todoWriteEvent([
70
- { id: "1", status: "completed" },
71
- { id: "2", status: "in_progress" },
72
- { id: "3", status: "pending" },
73
- ]),
74
- ]);
75
- expect(await readActiveTaskId(transcriptPath)).toBe("2");
76
- });
77
- it("prefers the MOST RECENT TodoWrite when multiple exist", async () => {
78
- await writeEvents([
79
- todoWriteEvent([{ id: "old-task", status: "in_progress" }]),
80
- todoWriteEvent([{ id: "newer-task", status: "in_progress" }]),
81
- ]);
82
- expect(await readActiveTaskId(transcriptPath)).toBe("newer-task");
83
- });
84
- it("stops at the most-recent TodoWrite even if its in_progress is null", async () => {
85
- // The MOST RECENT TodoWrite has no in_progress (all completed).
86
- // We must NOT fall back to an OLDER TodoWrite's in_progress that
87
- // may have been overwritten. Returns null.
88
- await writeEvents([
89
- todoWriteEvent([{ id: "stale-task", status: "in_progress" }]),
90
- todoWriteEvent([
91
- { id: "stale-task", status: "completed" },
92
- { id: "all-done", status: "completed" },
93
- ]),
94
- ]);
95
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
96
- });
97
- it("ignores non-assistant events between TodoWrites", async () => {
98
- await writeEvents([
99
- { type: "user", message: { role: "user", content: "do thing" } },
100
- todoWriteEvent([{ id: "active", status: "in_progress" }]),
101
- { type: "user", message: { role: "user", content: "now go" } },
102
- ]);
103
- expect(await readActiveTaskId(transcriptPath)).toBe("active");
104
- });
105
- it("ignores assistant text events without tool_use blocks", async () => {
106
- await writeEvents([
107
- todoWriteEvent([{ id: "real-active", status: "in_progress" }]),
108
- {
109
- type: "assistant",
110
- message: {
111
- role: "assistant",
112
- content: [{ type: "text", text: "thinking..." }],
113
- },
114
- },
115
- ]);
116
- expect(await readActiveTaskId(transcriptPath)).toBe("real-active");
117
- });
118
- it("ignores other tool_use events (Bash, Edit, etc.)", async () => {
119
- await writeEvents([
120
- todoWriteEvent([{ id: "active", status: "in_progress" }]),
121
- {
122
- type: "assistant",
123
- message: {
124
- role: "assistant",
125
- content: [{ type: "tool_use", name: "Bash", input: { command: "ls" } }],
126
- },
127
- },
128
- ]);
129
- expect(await readActiveTaskId(transcriptPath)).toBe("active");
130
- });
131
- it("coerces numeric ids to strings", async () => {
132
- await writeEvents([
133
- todoWriteEvent([
134
- // Some serializations encode id as number.
135
- { id: 127, status: "in_progress" },
136
- ]),
137
- ]);
138
- expect(await readActiveTaskId(transcriptPath)).toBe("127");
139
- });
140
- it("handles malformed JSON lines gracefully", async () => {
141
- await fs.writeFile(transcriptPath, [
142
- "{ malformed",
143
- JSON.stringify(todoWriteEvent([{ id: "active", status: "in_progress" }])),
144
- "still bad json",
145
- ].join("\n"), "utf8");
146
- expect(await readActiveTaskId(transcriptPath)).toBe("active");
147
- });
148
- });
149
- // =====================================================================
150
- // v0.6.2 — TaskCreate + TaskUpdate recognition (the real-world Claude
151
- // Code shape; TodoWrite was the v0.6.1 shape). My own dogfood session
152
- // today used TaskCreate/TaskUpdate exclusively → workflow gate silent-
153
- // allowed every commit because readActiveTaskId only recognized
154
- // TodoWrite. This block is the regression coverage for the fix.
155
- // =====================================================================
156
- function assistantToolUse(name, blockId, input) {
157
- return {
158
- type: "assistant",
159
- message: {
160
- role: "assistant",
161
- content: [
162
- {
163
- type: "tool_use",
164
- id: blockId,
165
- name,
166
- input,
167
- caller: { type: "direct" },
168
- },
169
- ],
170
- },
171
- };
172
- }
173
- function toolResult(toolUseId, content) {
174
- return {
175
- type: "user",
176
- message: {
177
- role: "user",
178
- content: [
179
- {
180
- type: "tool_result",
181
- tool_use_id: toolUseId,
182
- content,
183
- },
184
- ],
185
- },
186
- };
187
- }
188
- describe("readActiveTaskId — TaskUpdate (v0.6.2 fix)", () => {
189
- it("returns the taskId from TaskUpdate(status=in_progress)", async () => {
190
- await writeEvents([
191
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "131", status: "in_progress" }),
192
- ]);
193
- expect(await readActiveTaskId(transcriptPath)).toBe("131");
194
- });
195
- it("does not return tasks marked completed by a later TaskUpdate", async () => {
196
- await writeEvents([
197
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "131", status: "in_progress" }),
198
- assistantToolUse("TaskUpdate", "tu-2", { taskId: "131", status: "completed" }),
199
- ]);
200
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
201
- });
202
- it("picks the most-recently-touched in_progress task when multiple are active", async () => {
203
- await writeEvents([
204
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "100", status: "in_progress" }),
205
- assistantToolUse("TaskUpdate", "tu-2", { taskId: "200", status: "in_progress" }),
206
- assistantToolUse("TaskUpdate", "tu-3", { taskId: "300", status: "in_progress" }),
207
- ]);
208
- expect(await readActiveTaskId(transcriptPath)).toBe("300");
209
- });
210
- it("coerces numeric taskId to string", async () => {
211
- await writeEvents([
212
- assistantToolUse("TaskUpdate", "tu-1", { taskId: 131, status: "in_progress" }),
213
- ]);
214
- expect(await readActiveTaskId(transcriptPath)).toBe("131");
215
- });
216
- it("ignores TaskUpdate with deleted status", async () => {
217
- await writeEvents([
218
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "131", status: "in_progress" }),
219
- assistantToolUse("TaskUpdate", "tu-2", { taskId: "131", status: "deleted" }),
220
- ]);
221
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
222
- });
223
- });
224
- describe("readActiveTaskId — TaskCreate (v0.6.2 fix)", () => {
225
- it("does NOT return TaskCreate'd tasks (default status = pending, not in_progress)", async () => {
226
- // TaskCreate alone leaves the task as pending. Active-task detection
227
- // requires an explicit TaskUpdate(in_progress) — otherwise no gate
228
- // for tasks that were created but never started.
229
- await writeEvents([
230
- assistantToolUse("TaskCreate", "tc-1", {
231
- subject: "Some task",
232
- description: "...",
233
- }),
234
- toolResult("tc-1", "Task #131 created successfully: Some task"),
235
- ]);
236
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
237
- });
238
- it("returns the assigned id when TaskCreate is followed by TaskUpdate(in_progress)", async () => {
239
- await writeEvents([
240
- assistantToolUse("TaskCreate", "tc-1", { subject: "X", description: "..." }),
241
- toolResult("tc-1", "Task #131 created successfully: X"),
242
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "131", status: "in_progress" }),
243
- ]);
244
- expect(await readActiveTaskId(transcriptPath)).toBe("131");
245
- });
246
- it("handles TaskCreate without a matching tool_result (truncated transcript)", async () => {
247
- await writeEvents([
248
- assistantToolUse("TaskCreate", "tc-1", { subject: "X", description: "..." }),
249
- // No tool_result follows
250
- ]);
251
- // No id assigned, no in_progress → null.
252
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
253
- });
254
- });
255
- // =====================================================================
256
- // Real-world fixture — captured from an actual Claude Code session.
257
- // The fixture lives at __fixtures__/real-task-shape.jsonl. If Claude
258
- // Code ever changes the wire format for TaskCreate / TaskUpdate, this
259
- // test fails BEFORE the workflow gate silently regresses in
260
- // production. Earlier audit recommendation (v0.6.2 audit MED): synthesized
261
- // tests passed in v0.6.1 but real-world shape didn't match — the same
262
- // failure mode would have been caught here.
263
- // =====================================================================
264
- describe("readActiveTaskId — real Claude Code transcript fixture", () => {
265
- it("recognizes TaskCreate + tool_result + TaskUpdate captured from a real session", async () => {
266
- const fixturePath = path.resolve(__dirname, "__fixtures__", "real-task-shape.jsonl");
267
- // The fixture is 3 events: TaskCreate "X" → tool_result "Task #1 created" →
268
- // TaskUpdate(taskId=1, status=in_progress). Expected active task: "1".
269
- const active = await readActiveTaskId(fixturePath);
270
- expect(active).toBe("1");
271
- });
272
- });
273
- // =====================================================================
274
- // 0.7.9 (#163) — stale in_progress demotion
275
- // =====================================================================
276
- function assistantToolUseAt(name, blockId, input, timestamp) {
277
- return {
278
- type: "assistant",
279
- timestamp,
280
- message: {
281
- role: "assistant",
282
- content: [{ type: "tool_use", id: blockId, name, input, caller: { type: "direct" } }],
283
- },
284
- };
285
- }
286
- function userEventAt(timestamp, text = "hello") {
287
- return { type: "user", timestamp, message: { role: "user", content: text } };
288
- }
289
- describe("readActiveTaskId — stale-task demotion (#163)", () => {
290
- const oldDay = "2026-05-16T08:00:00Z"; // ~24h before latest
291
- const today = "2026-05-17T08:00:00Z"; // latest activity
292
- it("returns null when the only in_progress task is >1hr stale relative to latest activity", async () => {
293
- await writeEvents([
294
- assistantToolUseAt("TaskUpdate", "tu-1", { taskId: "999", status: "in_progress" }, oldDay),
295
- // Many later events with newer timestamps — none touch task 999.
296
- userEventAt(today, "new conversation today"),
297
- ]);
298
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
299
- });
300
- it("keeps the in_progress task when it was recently touched", async () => {
301
- const recent = "2026-05-17T07:30:00Z"; // 30 min before latest
302
- await writeEvents([
303
- assistantToolUseAt("TaskUpdate", "tu-1", { taskId: "42", status: "in_progress" }, recent),
304
- userEventAt(today),
305
- ]);
306
- expect(await readActiveTaskId(transcriptPath)).toBe("42");
307
- });
308
- it("picks the more-recent in_progress when two exist (one stale, one fresh)", async () => {
309
- const recent = "2026-05-17T07:45:00Z";
310
- await writeEvents([
311
- assistantToolUseAt("TaskUpdate", "tu-1", { taskId: "X", status: "in_progress" }, oldDay),
312
- assistantToolUseAt("TaskUpdate", "tu-2", { taskId: "Y", status: "in_progress" }, recent),
313
- userEventAt(today),
314
- ]);
315
- expect(await readActiveTaskId(transcriptPath)).toBe("Y");
316
- });
317
- it("falls back to line-idx pick (no demotion) when events have no timestamps", async () => {
318
- // Pre-existing behavior preserved when timestamps aren't available.
319
- await writeEvents([
320
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "no-ts", status: "in_progress" }),
321
- ]);
322
- expect(await readActiveTaskId(transcriptPath)).toBe("no-ts");
323
- });
324
- });
325
- describe("readActiveTaskId — mixed TodoWrite + TaskUpdate", () => {
326
- it("latest write wins per id, regardless of which tool", async () => {
327
- // TodoWrite snapshot says id=5 is in_progress; later TaskUpdate
328
- // marks id=5 completed. TaskUpdate is later → wins.
329
- await writeEvents([
330
- todoWriteEvent([{ id: "5", status: "in_progress" }]),
331
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "5", status: "completed" }),
332
- ]);
333
- expect(await readActiveTaskId(transcriptPath)).toBeNull();
334
- });
335
- it("TodoWrite snapshot can revive an id that TaskUpdate marked completed if it comes later", async () => {
336
- await writeEvents([
337
- assistantToolUse("TaskUpdate", "tu-1", { taskId: "5", status: "completed" }),
338
- todoWriteEvent([{ id: "5", status: "in_progress" }]),
339
- ]);
340
- expect(await readActiveTaskId(transcriptPath)).toBe("5");
341
- });
342
- });