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,393 +0,0 @@
1
- /**
2
- * Tests for #124 — token-threshold heartbeat that replaces the auto-
3
- * classifier subprocess.
4
- */
5
- import * as crypto from "node:crypto";
6
- import { promises as fs } from "node:fs";
7
- import * as os from "node:os";
8
- import * as path from "node:path";
9
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
10
- import { DEFAULT_HEARTBEAT_TOKENS, checkAndMaybeArm, consumePendingHeartbeat, estimateTokens, estimateTranscriptTokens, formatHeartbeatNudge, heartbeatSessionFiles, heartbeatThresholdTokens, readCheckpoint, writeCheckpoint, } from "./heartbeat.js";
11
- let tmpRoot;
12
- const SESSION = "heartbeat-test";
13
- beforeEach(async () => {
14
- tmpRoot = path.join(os.tmpdir(), `oscli-heartbeat-${crypto.randomUUID()}`);
15
- await fs.mkdir(tmpRoot, { recursive: true });
16
- // Ensure no env override leaks across tests.
17
- delete process.env.OPENSQUID_HEARTBEAT_TOKENS;
18
- });
19
- afterEach(async () => {
20
- await fs.rm(tmpRoot, { recursive: true, force: true });
21
- delete process.env.OPENSQUID_HEARTBEAT_TOKENS;
22
- });
23
- // ---------------------------------------------------------------------
24
- // estimateTokens / heartbeatThresholdTokens
25
- // ---------------------------------------------------------------------
26
- describe("estimateTokens", () => {
27
- it("returns 0 for empty / null-ish input", () => {
28
- expect(estimateTokens("")).toBe(0);
29
- });
30
- it("approximates chars/4", () => {
31
- expect(estimateTokens("aaaa")).toBe(1);
32
- expect(estimateTokens("a".repeat(80))).toBe(20);
33
- expect(estimateTokens("a".repeat(100))).toBe(25);
34
- });
35
- });
36
- describe("heartbeatThresholdTokens", () => {
37
- it("returns default when env unset", () => {
38
- expect(heartbeatThresholdTokens()).toBe(DEFAULT_HEARTBEAT_TOKENS);
39
- });
40
- it("honors OPENSQUID_HEARTBEAT_TOKENS positive integer", () => {
41
- process.env.OPENSQUID_HEARTBEAT_TOKENS = "5000";
42
- expect(heartbeatThresholdTokens()).toBe(5000);
43
- });
44
- it("falls back to default when env value is zero / negative / NaN", () => {
45
- for (const bad of ["0", "-1", "abc", ""]) {
46
- process.env.OPENSQUID_HEARTBEAT_TOKENS = bad;
47
- expect(heartbeatThresholdTokens()).toBe(DEFAULT_HEARTBEAT_TOKENS);
48
- }
49
- });
50
- });
51
- // ---------------------------------------------------------------------
52
- // estimateTranscriptTokens
53
- // ---------------------------------------------------------------------
54
- describe("estimateTranscriptTokens (0.7.7 #161)", () => {
55
- it("returns 0 when transcript file is missing", async () => {
56
- const r = await estimateTranscriptTokens(path.join(tmpRoot, "nope.jsonl"));
57
- expect(r).toBe(0);
58
- });
59
- it("returns 0 for an empty file", async () => {
60
- const p = path.join(tmpRoot, "empty.jsonl");
61
- await fs.writeFile(p, "");
62
- expect(await estimateTranscriptTokens(p)).toBe(0);
63
- });
64
- it("counts user.string content", async () => {
65
- const p = path.join(tmpRoot, "transcript.jsonl");
66
- const line = JSON.stringify({
67
- type: "user",
68
- message: { role: "user", content: "x".repeat(400) },
69
- });
70
- await fs.writeFile(p, line + "\n");
71
- // 400 chars / 4 = 100 tokens
72
- expect(await estimateTranscriptTokens(p)).toBe(100);
73
- });
74
- it("counts assistant text blocks", async () => {
75
- const p = path.join(tmpRoot, "transcript.jsonl");
76
- const line = JSON.stringify({
77
- type: "assistant",
78
- message: {
79
- role: "assistant",
80
- content: [
81
- { type: "text", text: "a".repeat(400) },
82
- { type: "text", text: "b".repeat(400) },
83
- ],
84
- },
85
- });
86
- await fs.writeFile(p, line + "\n");
87
- // 800 chars / 4 = 200 tokens
88
- expect(await estimateTranscriptTokens(p)).toBe(200);
89
- });
90
- it("SKIPS thinking blocks (agent internal CoT)", async () => {
91
- const p = path.join(tmpRoot, "transcript.jsonl");
92
- const line = JSON.stringify({
93
- type: "assistant",
94
- message: {
95
- content: [
96
- { type: "thinking", thinking: "x".repeat(10000), signature: "sig" },
97
- { type: "text", text: "hello" },
98
- ],
99
- },
100
- });
101
- await fs.writeFile(p, line + "\n");
102
- // Only "hello" (5 chars) counted → 2 tokens (ceiling)
103
- expect(await estimateTranscriptTokens(p)).toBe(2);
104
- });
105
- it("SKIPS tool_use blocks (compact + outbound work)", async () => {
106
- const p = path.join(tmpRoot, "transcript.jsonl");
107
- const line = JSON.stringify({
108
- type: "assistant",
109
- message: {
110
- content: [
111
- { type: "tool_use", id: "x", name: "Bash", input: { command: "ls" } },
112
- { type: "text", text: "after the tool" },
113
- ],
114
- },
115
- });
116
- await fs.writeFile(p, line + "\n");
117
- // Only the text block (14 chars) → 4 tokens
118
- expect(await estimateTranscriptTokens(p)).toBe(4);
119
- });
120
- it("CAPS tool_result content at 2000 chars (prevents tool-result inflation)", async () => {
121
- const p = path.join(tmpRoot, "transcript.jsonl");
122
- const line = JSON.stringify({
123
- type: "user",
124
- message: {
125
- role: "user",
126
- content: [
127
- {
128
- type: "tool_result",
129
- tool_use_id: "x",
130
- content: "z".repeat(50000), // huge file read
131
- },
132
- ],
133
- },
134
- });
135
- await fs.writeFile(p, line + "\n");
136
- // Capped at 2000 chars / 4 = 500 tokens (NOT 12,500)
137
- expect(await estimateTranscriptTokens(p)).toBe(500);
138
- });
139
- it("counts tool_result content array form (nested blocks)", async () => {
140
- const p = path.join(tmpRoot, "transcript.jsonl");
141
- const line = JSON.stringify({
142
- type: "user",
143
- message: {
144
- content: [
145
- {
146
- type: "tool_result",
147
- tool_use_id: "x",
148
- content: [
149
- { type: "text", text: "y".repeat(800) },
150
- { type: "text", text: "y".repeat(800) },
151
- ],
152
- },
153
- ],
154
- },
155
- });
156
- await fs.writeFile(p, line + "\n");
157
- // 1600 chars total (under 2000 cap) → 400 tokens
158
- expect(await estimateTranscriptTokens(p)).toBe(400);
159
- });
160
- it("SKIPS non-conversation line types (system / permission-mode / file-history-snapshot / etc)", async () => {
161
- const p = path.join(tmpRoot, "transcript.jsonl");
162
- const lines = [
163
- JSON.stringify({ type: "permission-mode", permissionMode: "default" }),
164
- JSON.stringify({ type: "system", text: "x".repeat(10000) }),
165
- JSON.stringify({ type: "file-history-snapshot", snapshot: {} }),
166
- JSON.stringify({ type: "attachment", message: { content: "x".repeat(10000) } }),
167
- JSON.stringify({ type: "ai-title", title: "Hello" }),
168
- JSON.stringify({ type: "last-prompt", prompt: "x".repeat(10000) }),
169
- ].join("\n");
170
- await fs.writeFile(p, lines);
171
- expect(await estimateTranscriptTokens(p)).toBe(0);
172
- });
173
- it("tolerates malformed JSON lines (skips them)", async () => {
174
- const p = path.join(tmpRoot, "transcript.jsonl");
175
- const lines = [
176
- "not json at all",
177
- JSON.stringify({ type: "user", message: { content: "hello" } }),
178
- "{partial json",
179
- ].join("\n");
180
- await fs.writeFile(p, lines);
181
- // Only "hello" counted (5 chars) → 2 tokens (ceiling)
182
- expect(await estimateTranscriptTokens(p)).toBe(2);
183
- });
184
- });
185
- // ---------------------------------------------------------------------
186
- // formatHeartbeatNudge
187
- // ---------------------------------------------------------------------
188
- describe("formatHeartbeatNudge", () => {
189
- it("includes the delta + threshold + the recall instruction", () => {
190
- const nudge = formatHeartbeatNudge(20000, 20000);
191
- expect(nudge).toContain("20,000");
192
- expect(nudge).toContain("recall");
193
- expect(nudge).toContain("memorize");
194
- expect(nudge).toContain("🦑");
195
- });
196
- });
197
- // ---------------------------------------------------------------------
198
- // Checkpoint IO
199
- // ---------------------------------------------------------------------
200
- describe("checkpoint IO", () => {
201
- it("returns null when no checkpoint file exists", async () => {
202
- expect(await readCheckpoint(SESSION, { dataRoot: tmpRoot })).toBeNull();
203
- });
204
- it("round-trips via writeCheckpoint", async () => {
205
- await writeCheckpoint(SESSION, { last_token_count: 12345, last_checkpoint_at: "2026-05-15T00:00:00Z" }, { dataRoot: tmpRoot });
206
- const back = await readCheckpoint(SESSION, { dataRoot: tmpRoot });
207
- expect(back?.last_token_count).toBe(12345);
208
- expect(back?.last_checkpoint_at).toBe("2026-05-15T00:00:00Z");
209
- });
210
- it("returns null on malformed JSON", async () => {
211
- const p = path.join(tmpRoot, "sessions", SESSION);
212
- await fs.mkdir(p, { recursive: true });
213
- await fs.writeFile(path.join(p, "heartbeat-checkpoint.json"), "not json");
214
- expect(await readCheckpoint(SESSION, { dataRoot: tmpRoot })).toBeNull();
215
- });
216
- });
217
- // ---------------------------------------------------------------------
218
- // checkAndMaybeArm — Stop hook entrypoint
219
- // ---------------------------------------------------------------------
220
- describe("checkAndMaybeArm", () => {
221
- // 0.7.7 (#161): estimator now counts only user/assistant message bodies
222
- // from valid JSONL lines, not raw file bytes. Helper writes a synthetic
223
- // user message whose content has the requested char-count so existing
224
- // crossing-math tests still work without reading a real transcript.
225
- async function writeTranscript(chars) {
226
- const p = path.join(tmpRoot, "transcript.jsonl");
227
- const line = JSON.stringify({
228
- type: "user",
229
- message: { role: "user", content: "x".repeat(chars) },
230
- });
231
- await fs.writeFile(p, line + "\n");
232
- return p;
233
- }
234
- it("returns null when transcript is missing / empty", async () => {
235
- const r = await checkAndMaybeArm(SESSION, path.join(tmpRoot, "missing.jsonl"), {
236
- dataRoot: tmpRoot,
237
- });
238
- expect(r).toBeNull();
239
- });
240
- it("arms a heartbeat on first crossing (no prior checkpoint)", async () => {
241
- // 80000 chars -> 20000 tokens -> exactly threshold
242
- const tpath = await writeTranscript(80000);
243
- const nudge = await checkAndMaybeArm(SESSION, tpath, {
244
- dataRoot: tmpRoot,
245
- thresholdTokens: 20000,
246
- });
247
- expect(nudge).not.toBeNull();
248
- expect(nudge).toContain("20,000");
249
- // Checkpoint bumped to the current count.
250
- const cp = await readCheckpoint(SESSION, { dataRoot: tmpRoot });
251
- expect(cp?.last_token_count).toBe(20000);
252
- });
253
- it("does NOT arm again until threshold crossed from the new checkpoint", async () => {
254
- // First crossing.
255
- let tpath = await writeTranscript(80000);
256
- expect(await checkAndMaybeArm(SESSION, tpath, { dataRoot: tmpRoot, thresholdTokens: 20000 })).not.toBeNull();
257
- // Drain the previous nudge so we can detect a fresh one (or its absence).
258
- await consumePendingHeartbeat(SESSION, { dataRoot: tmpRoot });
259
- // Transcript grows by less than threshold from the checkpoint.
260
- tpath = await writeTranscript(80000 + 4000); // +1000 tokens
261
- const second = await checkAndMaybeArm(SESSION, tpath, {
262
- dataRoot: tmpRoot,
263
- thresholdTokens: 20000,
264
- });
265
- expect(second).toBeNull();
266
- // Checkpoint stays at the first crossing.
267
- const cp = await readCheckpoint(SESSION, { dataRoot: tmpRoot });
268
- expect(cp?.last_token_count).toBe(20000);
269
- });
270
- it("resets stale baseline when checkpoint > 10x current (post-0.7.7 estimator migration)", async () => {
271
- // Simulate a checkpoint left by the old estimator: 31M tokens for a
272
- // 1.5M-token-real transcript. New estimator returns ~1.5M, baseline
273
- // says 31M → naive delta is negative → would never fire. Reset
274
- // logic must zero the baseline so the next crossing arms.
275
- await writeCheckpoint(SESSION, { last_token_count: 31_000_000, last_checkpoint_at: "2026-05-17T00:00:00Z" }, { dataRoot: tmpRoot });
276
- const tpath = await writeTranscript(80000); // 20K tokens
277
- const nudge = await checkAndMaybeArm(SESSION, tpath, {
278
- dataRoot: tmpRoot,
279
- thresholdTokens: 20000,
280
- });
281
- expect(nudge).not.toBeNull();
282
- const cp = await readCheckpoint(SESSION, { dataRoot: tmpRoot });
283
- expect(cp?.last_token_count).toBe(20000);
284
- });
285
- it("does NOT reset baseline when checkpoint is within reasonable range", async () => {
286
- // Baseline only 2x current — not stale, just slow growth (or
287
- // transcript shrunk via compaction). Don't reset.
288
- await writeCheckpoint(SESSION, { last_token_count: 40000, last_checkpoint_at: "2026-05-17T00:00:00Z" }, { dataRoot: tmpRoot });
289
- const tpath = await writeTranscript(80000); // 20K tokens, baseline 40K, delta = -20K
290
- const nudge = await checkAndMaybeArm(SESSION, tpath, {
291
- dataRoot: tmpRoot,
292
- thresholdTokens: 20000,
293
- });
294
- expect(nudge).toBeNull(); // negative delta, but no reset → no fire
295
- });
296
- it("arms again on each subsequent threshold crossing", async () => {
297
- // First crossing at 20K tokens.
298
- let tpath = await writeTranscript(80000);
299
- expect(await checkAndMaybeArm(SESSION, tpath, { dataRoot: tmpRoot, thresholdTokens: 20000 })).not.toBeNull();
300
- await consumePendingHeartbeat(SESSION, { dataRoot: tmpRoot });
301
- // Second crossing at 40K tokens.
302
- tpath = await writeTranscript(160000);
303
- expect(await checkAndMaybeArm(SESSION, tpath, { dataRoot: tmpRoot, thresholdTokens: 20000 })).not.toBeNull();
304
- const cp = await readCheckpoint(SESSION, { dataRoot: tmpRoot });
305
- expect(cp?.last_token_count).toBe(40000);
306
- });
307
- it("does not arm when below threshold and no prior checkpoint", async () => {
308
- const tpath = await writeTranscript(40000); // 10K tokens < 20K threshold
309
- const r = await checkAndMaybeArm(SESSION, tpath, {
310
- dataRoot: tmpRoot,
311
- thresholdTokens: 20000,
312
- });
313
- expect(r).toBeNull();
314
- // No checkpoint written when we didn't arm.
315
- expect(await readCheckpoint(SESSION, { dataRoot: tmpRoot })).toBeNull();
316
- });
317
- });
318
- // ---------------------------------------------------------------------
319
- // consumePendingHeartbeat — UserPromptSubmit hook entrypoint
320
- // ---------------------------------------------------------------------
321
- describe("consumePendingHeartbeat", () => {
322
- it("returns null when no pending marker", async () => {
323
- expect(await consumePendingHeartbeat(SESSION, { dataRoot: tmpRoot })).toBeNull();
324
- });
325
- it("returns the armed nudge and removes the marker (one-shot)", async () => {
326
- // Arm. 0.7.7 (#161): estimator now requires valid JSONL; wrap the
327
- // body content so the line parses as a user message.
328
- const tpath = path.join(tmpRoot, "transcript.jsonl");
329
- const line = JSON.stringify({ type: "user", message: { content: "x".repeat(80000) } });
330
- await fs.writeFile(tpath, line + "\n");
331
- await checkAndMaybeArm(SESSION, tpath, { dataRoot: tmpRoot, thresholdTokens: 20000 });
332
- const first = await consumePendingHeartbeat(SESSION, { dataRoot: tmpRoot });
333
- expect(first).not.toBeNull();
334
- expect(first).toContain("🦑");
335
- // Second consume returns null — marker was deleted.
336
- const second = await consumePendingHeartbeat(SESSION, { dataRoot: tmpRoot });
337
- expect(second).toBeNull();
338
- });
339
- });
340
- // ---------------------------------------------------------------------
341
- // SessionEnd cleanup hook surface
342
- // ---------------------------------------------------------------------
343
- describe("heartbeatSessionFiles", () => {
344
- it("returns the two paths SessionEnd should remove", () => {
345
- const files = heartbeatSessionFiles(SESSION, tmpRoot);
346
- expect(files.some((p) => p.endsWith("heartbeat-checkpoint.json"))).toBe(true);
347
- expect(files.some((p) => p.endsWith("heartbeat-pending.txt"))).toBe(true);
348
- });
349
- });
350
- // =====================================================================
351
- // 0.7.26 / D7 — recall-required flag (heartbeat → block until recall)
352
- // =====================================================================
353
- describe("recall-required flag (D7)", () => {
354
- let tmp;
355
- beforeEach(async () => {
356
- tmp = path.join(os.tmpdir(), `opensquid-recall-flag-${crypto.randomUUID()}`);
357
- await fs.mkdir(tmp, { recursive: true });
358
- });
359
- afterEach(async () => {
360
- await fs.rm(tmp, { recursive: true, force: true });
361
- });
362
- it("isRecallRequired returns false when flag was never set", async () => {
363
- const { isRecallRequired } = await import("./heartbeat.js");
364
- expect(await isRecallRequired("sess-1", { dataRoot: tmp })).toBe(false);
365
- });
366
- it("markRecallRequired creates the flag; isRecallRequired returns true", async () => {
367
- const { markRecallRequired, isRecallRequired } = await import("./heartbeat.js");
368
- await markRecallRequired("sess-2", { dataRoot: tmp });
369
- expect(await isRecallRequired("sess-2", { dataRoot: tmp })).toBe(true);
370
- });
371
- it("clearRecallRequired removes the flag", async () => {
372
- const { markRecallRequired, clearRecallRequired, isRecallRequired } = await import("./heartbeat.js");
373
- await markRecallRequired("sess-3", { dataRoot: tmp });
374
- expect(await isRecallRequired("sess-3", { dataRoot: tmp })).toBe(true);
375
- await clearRecallRequired("sess-3", { dataRoot: tmp });
376
- expect(await isRecallRequired("sess-3", { dataRoot: tmp })).toBe(false);
377
- });
378
- it("clearRecallRequired is idempotent (clear without prior mark is fine)", async () => {
379
- const { clearRecallRequired } = await import("./heartbeat.js");
380
- await expect(clearRecallRequired("sess-never", { dataRoot: tmp })).resolves.toBeUndefined();
381
- });
382
- it("flags are per-session — setting one session doesn't affect another", async () => {
383
- const { markRecallRequired, isRecallRequired } = await import("./heartbeat.js");
384
- await markRecallRequired("sess-A", { dataRoot: tmp });
385
- expect(await isRecallRequired("sess-A", { dataRoot: tmp })).toBe(true);
386
- expect(await isRecallRequired("sess-B", { dataRoot: tmp })).toBe(false);
387
- });
388
- it("heartbeatSessionFiles includes the recall-required flag path for SessionEnd cleanup", async () => {
389
- const { heartbeatSessionFiles } = await import("./heartbeat.js");
390
- const files = heartbeatSessionFiles("sess-1", tmp);
391
- expect(files.some((p) => p.endsWith("recall-required.flag"))).toBe(true);
392
- });
393
- });
@@ -1,100 +0,0 @@
1
- /**
2
- * Tests specifically for the session-scope fix (#114).
3
- *
4
- * Verifies that:
5
- * 1. The ledger accumulates across multiple turns within a session.
6
- * 2. A claim made in turn N is satisfied by evidence from turn 1.
7
- * 3. clearSession wipes both ledger + broken-promises.
8
- * 4. Stop hook's de-dupe behavior — re-running reconcile on the same
9
- * text doesn't double-record the broken promise.
10
- */
11
- import * as crypto from "node:crypto";
12
- import { promises as fs } from "node:fs";
13
- import * as os from "node:os";
14
- import * as path from "node:path";
15
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
16
- import { clearSession, clearTurnLedger, reconcile, readBrokenPromises, readTurnLedger, recordBrokenPromise, recordToolCall, } from "./honesty-ledger.js";
17
- let tmpRoot;
18
- const SESSION = "scope-test-session";
19
- beforeEach(async () => {
20
- tmpRoot = path.join(os.tmpdir(), `oscli-honesty-scope-${crypto.randomUUID()}`);
21
- await fs.mkdir(tmpRoot, { recursive: true });
22
- });
23
- afterEach(async () => {
24
- await fs.rm(tmpRoot, { recursive: true, force: true });
25
- });
26
- describe("session-scoped ledger (#114 fix)", () => {
27
- it("accumulates tool calls across multiple turns", async () => {
28
- // Turn 1: ran npm test
29
- await recordToolCall(SESSION, "Bash", "npm test", { dataRoot: tmpRoot });
30
- // Turn 2: ran cargo check
31
- await recordToolCall(SESSION, "Bash", "cargo check", { dataRoot: tmpRoot });
32
- // Turn 3: read a file
33
- await recordToolCall(SESSION, "Read", "/x.ts", { dataRoot: tmpRoot });
34
- const ledger = await readTurnLedger(SESSION, { dataRoot: tmpRoot });
35
- expect(ledger).toHaveLength(3);
36
- expect(ledger.map((e) => e.tool)).toEqual(["Bash", "Bash", "Read"]);
37
- });
38
- it("recap text in turn N is satisfied by tool call from turn 1 (THE FIX)", async () => {
39
- // Turn 1: actually ran tests
40
- await recordToolCall(SESSION, "Bash", "npm test", { dataRoot: tmpRoot });
41
- // Turn 5 (much later): assistant says "tests pass" in recap text
42
- const recapText = "Here's a summary. Tests pass and build is green.";
43
- const ledger = await readTurnLedger(SESSION, { dataRoot: tmpRoot });
44
- const broken = reconcile(recapText, ledger);
45
- // Before the fix: would flag "tests pass" as a broken promise.
46
- // After the fix: ledger has the prior turn's npm test, so satisfied.
47
- expect(broken.map((b) => b.claim_id)).not.toContain("running-tests");
48
- });
49
- it("genuinely lying recap is still caught (no false-negative regression)", async () => {
50
- // Turn 1: read a file
51
- await recordToolCall(SESSION, "Read", "/foo.ts", { dataRoot: tmpRoot });
52
- // Turn 2: claim "tests pass" but NO test was ever run in this session
53
- const ledger = await readTurnLedger(SESSION, { dataRoot: tmpRoot });
54
- const broken = reconcile("Tests pass.", ledger);
55
- expect(broken.map((b) => b.claim_id)).toContain("running-tests");
56
- });
57
- it("clearTurnLedger removes only the ledger file (not broken-promises)", async () => {
58
- await recordToolCall(SESSION, "Bash", "ls", { dataRoot: tmpRoot });
59
- await recordBrokenPromise(SESSION, {
60
- ts: "t",
61
- claim_id: "fake",
62
- claim_label: "fake",
63
- matched_text: "fake",
64
- reason: "fake",
65
- }, { dataRoot: tmpRoot });
66
- await clearTurnLedger(SESSION, { dataRoot: tmpRoot });
67
- expect(await readTurnLedger(SESSION, { dataRoot: tmpRoot })).toEqual([]);
68
- // broken-promises survives a turn-ledger clear
69
- expect(await readBrokenPromises(SESSION, { dataRoot: tmpRoot })).toHaveLength(1);
70
- });
71
- it("clearSession wipes BOTH ledger and broken-promises", async () => {
72
- await recordToolCall(SESSION, "Bash", "ls", { dataRoot: tmpRoot });
73
- await recordBrokenPromise(SESSION, {
74
- ts: "t",
75
- claim_id: "fake",
76
- claim_label: "fake",
77
- matched_text: "fake",
78
- reason: "fake",
79
- }, { dataRoot: tmpRoot });
80
- await clearSession(SESSION, { dataRoot: tmpRoot });
81
- expect(await readTurnLedger(SESSION, { dataRoot: tmpRoot })).toEqual([]);
82
- expect(await readBrokenPromises(SESSION, { dataRoot: tmpRoot })).toEqual([]);
83
- });
84
- it("clearSession is idempotent (no throw on already-clean session)", async () => {
85
- await clearSession(SESSION, { dataRoot: tmpRoot });
86
- await clearSession(SESSION, { dataRoot: tmpRoot });
87
- // No throw.
88
- });
89
- });
90
- describe("stuck-broken-promise dedupe (#114 fix companion)", () => {
91
- it("reconcile on the same text twice returns the same set", async () => {
92
- // No tool calls. Claim text triggers two patterns.
93
- const broken1 = reconcile("Tests pass and committed.", []);
94
- const broken2 = reconcile("Tests pass and committed.", []);
95
- expect(broken1.map((b) => b.claim_id).sort()).toEqual(broken2.map((b) => b.claim_id).sort());
96
- // Both should flag running-tests + committed
97
- expect(broken1.map((b) => b.claim_id)).toContain("running-tests");
98
- expect(broken1.map((b) => b.claim_id)).toContain("committed");
99
- });
100
- });
@@ -1,123 +0,0 @@
1
- /**
2
- * Honesty ledger — catches claim-vs-action gaps in assistant turns.
3
- *
4
- * The agent makes claims like "running tests now" or "starting research"
5
- * or "committed" in its text output. These claims are checkable against
6
- * the tool calls that actually happened in the same turn. When the
7
- * claim has no matching tool call, that's a "broken promise" — opensquid
8
- * records it so the next turn surfaces it to the agent for correction.
9
- *
10
- * Storage:
11
- * <data-root>/sessions/<session-id>/turn-ledger.jsonl
12
- * One JSON line per tool call this turn, appended by the PreToolUse
13
- * hook. Cleared at turn-end after reconciliation.
14
- *
15
- * <data-root>/sessions/<session-id>/broken-promises.jsonl
16
- * Append-only ledger of claims that lacked matching evidence.
17
- * Surfaced to the agent on the NEXT turn via SessionStart/
18
- * UserPromptSubmit hook output.
19
- */
20
- export type ClaimEvidenceShape = {
21
- kind: "any_tool";
22
- } | {
23
- kind: "bash_contains";
24
- needle: string;
25
- } | {
26
- kind: "bash_regex";
27
- pattern: string;
28
- } | {
29
- kind: "tool_called";
30
- tool: string;
31
- } | {
32
- kind: "any_of";
33
- options: ClaimEvidenceShape[];
34
- } | {
35
- kind: "input_contains";
36
- tool: string;
37
- needle: string;
38
- };
39
- export interface ClaimPattern {
40
- /** Stable id (e.g. "research-start"). */
41
- id: string;
42
- /** Regex matched against assistant text. */
43
- text_regex: string;
44
- /** What proof of action satisfies this claim. */
45
- evidence: ClaimEvidenceShape;
46
- /** Short label surfaced to the agent when the promise is broken. */
47
- promise_label: string;
48
- }
49
- /**
50
- * Claim catalog — sourced from the bundled-default codex (0.7.17,
51
- * drift-as-codex chunk 3b). Previously a hand-maintained TS array;
52
- * now loaded once at module init from
53
- * `src/codex/bundled-default/codex.yaml` via the chunk-2 loader.
54
- *
55
- * Fail-open: if the codex is unloadable, the catalog is empty and
56
- * no claims fire. Better silent under-enforcement than a hook crash.
57
- */
58
- export declare const CLAIM_PATTERNS: ClaimPattern[];
59
- /**
60
- * One tool-call entry, persisted across ALL turns in the same Claude
61
- * Code session. Cleared only at session end (or explicit
62
- * `clearSessionLedger`).
63
- *
64
- * #114 (2026-05-15) — v0.4.C.1 fix: previously this was a PER-TURN
65
- * ledger that got cleared by the Stop hook, which caused recap text
66
- * describing prior-turn work to be flagged as broken promises. Now
67
- * the ledger accumulates across the whole session so claims like
68
- * "tests pass" satisfy against any `npm test` from any earlier turn
69
- * in the same session.
70
- */
71
- export interface TurnLedgerEntry {
72
- ts: string;
73
- tool: string;
74
- /** Subset of tool_input relevant to reconciliation. */
75
- input_summary: string;
76
- }
77
- /** Called by PreToolUse hook to record what the agent is about to do. */
78
- export declare function recordToolCall(sessionId: string, tool: string, inputSummary: string, options?: {
79
- dataRoot?: string;
80
- }): Promise<void>;
81
- /** Read the session-scoped ledger (every tool call so far this session). */
82
- export declare function readTurnLedger(sessionId: string, options?: {
83
- dataRoot?: string;
84
- }): Promise<TurnLedgerEntry[]>;
85
- /**
86
- * Clear the session ledger. ONLY called at session end (or by tests).
87
- * Stop hook does NOT call this anymore — the ledger persists across
88
- * turns to avoid recap-text false-positives.
89
- */
90
- export declare function clearTurnLedger(sessionId: string, options?: {
91
- dataRoot?: string;
92
- }): Promise<void>;
93
- /**
94
- * Explicit session-end clear: wipes everything opensquid wrote under
95
- * this session's directory — turn ledger, broken promises, plus the
96
- * heartbeat checkpoint and pending marker (#124). Files are removed
97
- * individually so unrelated files in the session dir survive (in case
98
- * a future hook drops something else there).
99
- */
100
- export declare function clearSession(sessionId: string, options?: {
101
- dataRoot?: string;
102
- }): Promise<void>;
103
- export interface BrokenPromise {
104
- ts: string;
105
- claim_id: string;
106
- claim_label: string;
107
- matched_text: string;
108
- reason: string;
109
- }
110
- /**
111
- * Scan assistant text for claim phrases, reconcile against the ledger,
112
- * return any unfulfilled promises.
113
- */
114
- export declare function reconcile(assistantText: string, ledger: TurnLedgerEntry[]): BrokenPromise[];
115
- /** Append a broken promise to the session's append-only ledger. */
116
- export declare function recordBrokenPromise(sessionId: string, promise: BrokenPromise, options?: {
117
- dataRoot?: string;
118
- }): Promise<void>;
119
- /** Read all broken promises for a session (used by next turn's hook). */
120
- export declare function readBrokenPromises(sessionId: string, options?: {
121
- dataRoot?: string;
122
- }): Promise<BrokenPromise[]>;
123
- //# sourceMappingURL=honesty-ledger.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"honesty-ledger.d.ts","sourceRoot":"","sources":["../../src.legacy/hooks/honesty-ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAaH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAKrC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,kBAAkB,EAAE,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB;AA4CD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,EAAE,YAAY,EAUrC,CAAC;AAML;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;CACvB;AAgBD,yEAAyE;AACzE,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,4EAA4E;AAC5E,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,eAAe,EAAE,CAAC,CAU5B;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,IAAI,CAAC,CAcf;AAMD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CAqB3F;AA0BD,mEAAmE;AACnE,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,yEAAyE;AACzE,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,aAAa,EAAE,CAAC,CAU1B"}