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,140 +0,0 @@
1
- /**
2
- * Per-project chat routing (v0.7.1 Phase C).
3
- *
4
- * Note on src.legacy/ placement (TG.1 (f) / WAB.2): the "legacy" label is a
5
- * pre-0.5.x architecture marker, NOT a "dead code" marker. The chat-daemon
6
- * compiled from `src.legacy/chat/` is the production runtime for inbound
7
- * Telegram + outbound chat_send. This file is actively maintained — see
8
- * `inbound_dm_user_ids` (v0.5.94 / WAB.2 Part A) below.
9
- *
10
- * Schema on disk: `~/.opensquid/projects/<uuid>/chat-routing.json`
11
- *
12
- * ```jsonc
13
- * {
14
- * "telegram": {
15
- * "report_channel": "telegram:-1001234567890", // outbound default
16
- * "report_topic_id": 15, // forum-topic id for outbound
17
- * "inbound_chat_ids": ["-1001234567890"], // accepts inbound from these chats
18
- * "inbound_topic_ids": [15], // strict whitelist when set
19
- * "inbound_dm_user_ids": ["8075471258"] // v0.5.94 — DM allowlist
20
- * },
21
- * "discord": {
22
- * "report_channel": "discord:1234567890",
23
- * "inbound_channel_ids": ["1234567890"]
24
- * },
25
- * "slack": {
26
- * "report_channel": "slack:C012345",
27
- * "inbound_channel_ids": ["C012345"]
28
- * }
29
- * }
30
- * ```
31
- *
32
- * Routing rules:
33
- * - **Outbound** (agent → chat): the MCP tool picks `report_channel`
34
- * from the active project's routing config. (Phase E wires the MCP
35
- * tools; this module just exposes the lookup.)
36
- * - **Inbound** (chat → agent): the daemon's gateway.onMessage handler
37
- * constructs a routing key (DM key, topic key, or chat-only key per
38
- * TG.1 policy decision (a)+(d)) and looks it up in the index this
39
- * module builds. Match → JSONL append to that project's inbox.
40
- * No match → JSONL append to the orphan inbox.
41
- *
42
- * UUID is the stable primary key because the project's human-friendly
43
- * `id` can be renamed via `opensquid project init` without rewriting
44
- * routing files.
45
- */
46
- export interface TelegramRouting {
47
- report_channel?: string;
48
- /**
49
- * v0.7.2 — forum-topic id within the supergroup (`report_channel`)
50
- * that outbound reports for this project should post to. When set,
51
- * outbound `chat_send` via `project:telegram` includes the
52
- * `message_thread_id` so the message lands in the right topic.
53
- */
54
- report_topic_id?: number;
55
- inbound_chat_ids?: string[];
56
- /**
57
- * v0.7.2 — when set, ONLY inbound messages with one of these
58
- * `message_thread_id` values route to this project. Empty/unset means
59
- * accept any topic (legacy v0.7.1 behavior — accepts all messages
60
- * from the listed `inbound_chat_ids`).
61
- */
62
- inbound_topic_ids?: number[];
63
- /**
64
- * v0.5.94 (WAB.2 Part A / TG.1 decision (a)) — allowlist of user IDs
65
- * whose Telegram DMs route to this project. A "DM" is detected when the
66
- * inbound `chat.id === from.id` (Telegram's canonical private-chat
67
- * shape). On match, the routing key is `telegram:dm:<user_id>`. Group
68
- * messages from the same user use `inbound_chat_ids` + topic semantics
69
- * — this field does NOT shadow group routing.
70
- *
71
- * Schema is additive: existing chat-routing.json files without this
72
- * field continue to load and route correctly (DM routing simply
73
- * disabled for that project).
74
- */
75
- inbound_dm_user_ids?: string[];
76
- }
77
- export interface DiscordRouting {
78
- report_channel?: string;
79
- inbound_channel_ids?: string[];
80
- }
81
- export interface SlackRouting {
82
- report_channel?: string;
83
- inbound_channel_ids?: string[];
84
- }
85
- export interface ProjectChatRouting {
86
- telegram?: TelegramRouting;
87
- discord?: DiscordRouting;
88
- slack?: SlackRouting;
89
- }
90
- /**
91
- * In-memory index built from all per-project routing files. Used by
92
- * the daemon's inbound handler to decide which project's inbox an
93
- * incoming message belongs to.
94
- *
95
- * Key shape: `<platform>:<native_chat_id>` — same shape as ChannelId
96
- * so the daemon can index directly off the parsed channel field of
97
- * an inbound ChatMessage.
98
- */
99
- export type RoutingIndex = Map<string, string>;
100
- export declare function projectsRootPath(dataRoot?: string): string;
101
- export declare function projectChatRoutingPath(projectUuid: string, dataRoot?: string): string;
102
- export declare function projectInboxDir(projectUuid: string, dataRoot?: string): string;
103
- export declare function orphanInboxDir(dataRoot?: string): string;
104
- /**
105
- * Read a single project's routing config. Returns null if the file
106
- * doesn't exist or fails to parse — both are non-fatal (the project
107
- * just doesn't have routing configured, so inbound for it goes to
108
- * the orphan inbox).
109
- */
110
- export declare function loadProjectChatRouting(projectUuid: string, dataRoot?: string): Promise<ProjectChatRouting | null>;
111
- /**
112
- * Scan `~/.opensquid/projects/*` and load every routing file that
113
- * exists. Returns a Map keyed by project_uuid → routing config. Used
114
- * by `buildRoutingIndex` to construct the lookup map.
115
- */
116
- export declare function loadAllProjectChatRouting(dataRoot?: string): Promise<Map<string, ProjectChatRouting>>;
117
- /**
118
- * Build the inbound chat_id → project_uuid lookup map from a collection
119
- * of per-project routing configs. Used at daemon startup AND whenever
120
- * the file watcher fires on a routing change.
121
- *
122
- * Collision handling: if two projects claim the same inbound chat_id,
123
- * the LATER one wins (Map.set overwrite). Surface a warning via the
124
- * optional `onWarn` callback so the operator can fix it.
125
- */
126
- export declare function buildRoutingIndex(configs: Map<string, ProjectChatRouting>, onWarn?: (message: string) => void): RoutingIndex;
127
- /**
128
- * Yield each inbound channel id (in `<platform>:<native_id>` shape) a
129
- * routing config declares.
130
- */
131
- export declare function collectInboundChannels(cfg: ProjectChatRouting): string[];
132
- /**
133
- * Persist a project's routing config. Creates the project directory if
134
- * missing. Atomic-ish via write-then-rename so partial writes never
135
- * leave a corrupt file.
136
- */
137
- export declare function saveProjectChatRouting(projectUuid: string, routing: ProjectChatRouting, dataRoot?: string): Promise<{
138
- path: string;
139
- }>;
140
- //# sourceMappingURL=routing.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAWH,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;;;;;;;OAWG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAoB,CAAC;AAMlE,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD;AAMD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAcpC;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAiB1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACxC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACjC,YAAY,CAcd;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAgCxE;AAMD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAO3B"}
@@ -1,198 +0,0 @@
1
- /**
2
- * Per-project chat routing (v0.7.1 Phase C).
3
- *
4
- * Note on src.legacy/ placement (TG.1 (f) / WAB.2): the "legacy" label is a
5
- * pre-0.5.x architecture marker, NOT a "dead code" marker. The chat-daemon
6
- * compiled from `src.legacy/chat/` is the production runtime for inbound
7
- * Telegram + outbound chat_send. This file is actively maintained — see
8
- * `inbound_dm_user_ids` (v0.5.94 / WAB.2 Part A) below.
9
- *
10
- * Schema on disk: `~/.opensquid/projects/<uuid>/chat-routing.json`
11
- *
12
- * ```jsonc
13
- * {
14
- * "telegram": {
15
- * "report_channel": "telegram:-1001234567890", // outbound default
16
- * "report_topic_id": 15, // forum-topic id for outbound
17
- * "inbound_chat_ids": ["-1001234567890"], // accepts inbound from these chats
18
- * "inbound_topic_ids": [15], // strict whitelist when set
19
- * "inbound_dm_user_ids": ["8075471258"] // v0.5.94 — DM allowlist
20
- * },
21
- * "discord": {
22
- * "report_channel": "discord:1234567890",
23
- * "inbound_channel_ids": ["1234567890"]
24
- * },
25
- * "slack": {
26
- * "report_channel": "slack:C012345",
27
- * "inbound_channel_ids": ["C012345"]
28
- * }
29
- * }
30
- * ```
31
- *
32
- * Routing rules:
33
- * - **Outbound** (agent → chat): the MCP tool picks `report_channel`
34
- * from the active project's routing config. (Phase E wires the MCP
35
- * tools; this module just exposes the lookup.)
36
- * - **Inbound** (chat → agent): the daemon's gateway.onMessage handler
37
- * constructs a routing key (DM key, topic key, or chat-only key per
38
- * TG.1 policy decision (a)+(d)) and looks it up in the index this
39
- * module builds. Match → JSONL append to that project's inbox.
40
- * No match → JSONL append to the orphan inbox.
41
- *
42
- * UUID is the stable primary key because the project's human-friendly
43
- * `id` can be renamed via `opensquid project init` without rewriting
44
- * routing files.
45
- */
46
- import { promises as fs } from "node:fs";
47
- import * as path from "node:path";
48
- import { resolveDataRoot } from "../../codex/store.js";
49
- // ---------------------------------------------------------------------
50
- // Paths
51
- // ---------------------------------------------------------------------
52
- export function projectsRootPath(dataRoot) {
53
- return path.join(resolveDataRoot(dataRoot), "projects");
54
- }
55
- export function projectChatRoutingPath(projectUuid, dataRoot) {
56
- return path.join(projectsRootPath(dataRoot), projectUuid, "chat-routing.json");
57
- }
58
- export function projectInboxDir(projectUuid, dataRoot) {
59
- return path.join(projectsRootPath(dataRoot), projectUuid, "inbox");
60
- }
61
- export function orphanInboxDir(dataRoot) {
62
- return path.join(resolveDataRoot(dataRoot), "inbox", "orphan");
63
- }
64
- // ---------------------------------------------------------------------
65
- // Load
66
- // ---------------------------------------------------------------------
67
- /**
68
- * Read a single project's routing config. Returns null if the file
69
- * doesn't exist or fails to parse — both are non-fatal (the project
70
- * just doesn't have routing configured, so inbound for it goes to
71
- * the orphan inbox).
72
- */
73
- export async function loadProjectChatRouting(projectUuid, dataRoot) {
74
- const p = projectChatRoutingPath(projectUuid, dataRoot);
75
- try {
76
- const raw = await fs.readFile(p, "utf8");
77
- return JSON.parse(raw);
78
- }
79
- catch (err) {
80
- if (err.code === "ENOENT")
81
- return null;
82
- // Malformed JSON: log on stderr (daemon log) and fall back to
83
- // no-routing. Better than crashing the daemon over a bad file.
84
- process.stderr.write(`[chat-routing] failed to parse ${p}: ${err instanceof Error ? err.message : err}\n`);
85
- return null;
86
- }
87
- }
88
- /**
89
- * Scan `~/.opensquid/projects/*` and load every routing file that
90
- * exists. Returns a Map keyed by project_uuid → routing config. Used
91
- * by `buildRoutingIndex` to construct the lookup map.
92
- */
93
- export async function loadAllProjectChatRouting(dataRoot) {
94
- const root = projectsRootPath(dataRoot);
95
- const out = new Map();
96
- let entries;
97
- try {
98
- entries = await fs.readdir(root);
99
- }
100
- catch (err) {
101
- if (err.code === "ENOENT")
102
- return out;
103
- throw err;
104
- }
105
- for (const uuid of entries) {
106
- // Skip hidden files; only consider directory-like entries.
107
- if (uuid.startsWith("."))
108
- continue;
109
- const routing = await loadProjectChatRouting(uuid, dataRoot);
110
- if (routing)
111
- out.set(uuid, routing);
112
- }
113
- return out;
114
- }
115
- /**
116
- * Build the inbound chat_id → project_uuid lookup map from a collection
117
- * of per-project routing configs. Used at daemon startup AND whenever
118
- * the file watcher fires on a routing change.
119
- *
120
- * Collision handling: if two projects claim the same inbound chat_id,
121
- * the LATER one wins (Map.set overwrite). Surface a structured warning
122
- * via the optional `onCollision` callback so the operator can fix it.
123
- */
124
- export function buildRoutingIndex(configs, onCollision) {
125
- const idx = new Map();
126
- for (const [projectUuid, cfg] of configs) {
127
- for (const channelKey of collectInboundChannels(cfg)) {
128
- const existing = idx.get(channelKey);
129
- if (existing && existing !== projectUuid && onCollision) {
130
- onCollision({
131
- channel_key: channelKey,
132
- existing_uuid: existing,
133
- newcomer_uuid: projectUuid,
134
- });
135
- }
136
- idx.set(channelKey, projectUuid);
137
- }
138
- }
139
- return idx;
140
- }
141
- /**
142
- * Yield each inbound channel id (in `<platform>:<native_id>` shape) a
143
- * routing config declares.
144
- */
145
- export function collectInboundChannels(cfg) {
146
- const out = [];
147
- if (cfg.telegram?.inbound_chat_ids) {
148
- // v0.7.2: if inbound_topic_ids is set, emit a more-specific key per
149
- // (chat_id, topic_id) tuple so the routing index can distinguish
150
- // multiple projects sharing one supergroup. Otherwise emit the
151
- // chat-only key (v0.7.1 behavior).
152
- const topicIds = cfg.telegram.inbound_topic_ids;
153
- for (const chatId of cfg.telegram.inbound_chat_ids) {
154
- if (topicIds && topicIds.length > 0) {
155
- for (const tid of topicIds)
156
- out.push(`telegram:${chatId}:${tid}`);
157
- }
158
- else {
159
- out.push(`telegram:${chatId}`);
160
- }
161
- }
162
- }
163
- // v0.5.94 (WAB.2 Part A / TG.1 (a)): emit DM allowlist keys as
164
- // `telegram:dm:<user_id>`. Worker's onMessage detects DMs (chat.id ===
165
- // from.id) and looks up against this key. Separate key namespace from
166
- // `telegram:<chat_id>` so a user whose user_id collides with a
167
- // supergroup chat_id (unlikely but possible — both are integers) does
168
- // not silently cross-route.
169
- if (cfg.telegram?.inbound_dm_user_ids) {
170
- for (const uid of cfg.telegram.inbound_dm_user_ids)
171
- out.push(`telegram:dm:${uid}`);
172
- }
173
- if (cfg.discord?.inbound_channel_ids) {
174
- for (const id of cfg.discord.inbound_channel_ids)
175
- out.push(`discord:${id}`);
176
- }
177
- if (cfg.slack?.inbound_channel_ids) {
178
- for (const id of cfg.slack.inbound_channel_ids)
179
- out.push(`slack:${id}`);
180
- }
181
- return out;
182
- }
183
- // ---------------------------------------------------------------------
184
- // Write
185
- // ---------------------------------------------------------------------
186
- /**
187
- * Persist a project's routing config. Creates the project directory if
188
- * missing. Atomic-ish via write-then-rename so partial writes never
189
- * leave a corrupt file.
190
- */
191
- export async function saveProjectChatRouting(projectUuid, routing, dataRoot) {
192
- const filePath = projectChatRoutingPath(projectUuid, dataRoot);
193
- await fs.mkdir(path.dirname(filePath), { recursive: true });
194
- const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
195
- await fs.writeFile(tmp, `${JSON.stringify(routing, null, 2)}\n`, "utf8");
196
- await fs.rename(tmp, filePath);
197
- return { path: filePath };
198
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"routing.js","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/routing.ts"],"names":[],"mappings":"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"}
@@ -1,259 +0,0 @@
1
- /**
2
- * routing.ts unit tests (v0.7.1 Phase C).
3
- */
4
- import { promises as fs } from "node:fs";
5
- import * as os from "node:os";
6
- import * as path from "node:path";
7
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
8
- import { buildRoutingIndex, collectInboundChannels, loadAllProjectChatRouting, loadProjectChatRouting, projectChatRoutingPath, projectInboxDir, projectsRootPath, saveProjectChatRouting, } from "./routing.js";
9
- let tmpRoot;
10
- let prevHome;
11
- beforeEach(async () => {
12
- tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "opensquid-routing-test-"));
13
- prevHome = process.env.OPENSQUID_HOME;
14
- process.env.OPENSQUID_HOME = tmpRoot;
15
- });
16
- afterEach(async () => {
17
- if (prevHome === undefined)
18
- delete process.env.OPENSQUID_HOME;
19
- else
20
- process.env.OPENSQUID_HOME = prevHome;
21
- await fs.rm(tmpRoot, { recursive: true, force: true });
22
- });
23
- describe("path derivation", () => {
24
- it("projectsRootPath = <dataRoot>/projects", () => {
25
- expect(projectsRootPath(tmpRoot)).toBe(path.join(tmpRoot, "projects"));
26
- });
27
- it("projectChatRoutingPath = <dataRoot>/projects/<uuid>/chat-routing.json", () => {
28
- expect(projectChatRoutingPath("abc-uuid", tmpRoot)).toBe(path.join(tmpRoot, "projects", "abc-uuid", "chat-routing.json"));
29
- });
30
- it("projectInboxDir = <dataRoot>/projects/<uuid>/inbox", () => {
31
- expect(projectInboxDir("abc-uuid", tmpRoot)).toBe(path.join(tmpRoot, "projects", "abc-uuid", "inbox"));
32
- });
33
- });
34
- describe("loadProjectChatRouting", () => {
35
- it("returns null when the routing file is missing", async () => {
36
- const r = await loadProjectChatRouting("missing-uuid", tmpRoot);
37
- expect(r).toBeNull();
38
- });
39
- it("loads a valid routing file", async () => {
40
- const uuid = "test-uuid";
41
- await fs.mkdir(path.join(tmpRoot, "projects", uuid), { recursive: true });
42
- await fs.writeFile(path.join(tmpRoot, "projects", uuid, "chat-routing.json"), JSON.stringify({
43
- telegram: { report_channel: "telegram:-100", inbound_chat_ids: ["-100", "-200"] },
44
- }));
45
- const r = await loadProjectChatRouting(uuid, tmpRoot);
46
- expect(r).not.toBeNull();
47
- expect(r?.telegram?.report_channel).toBe("telegram:-100");
48
- expect(r?.telegram?.inbound_chat_ids).toEqual(["-100", "-200"]);
49
- });
50
- it("returns null on malformed JSON (does not throw)", async () => {
51
- const uuid = "bad-uuid";
52
- await fs.mkdir(path.join(tmpRoot, "projects", uuid), { recursive: true });
53
- await fs.writeFile(path.join(tmpRoot, "projects", uuid, "chat-routing.json"), "{ not json");
54
- const r = await loadProjectChatRouting(uuid, tmpRoot);
55
- expect(r).toBeNull();
56
- });
57
- });
58
- describe("loadAllProjectChatRouting", () => {
59
- it("returns empty map when projects root doesn't exist", async () => {
60
- const r = await loadAllProjectChatRouting(tmpRoot);
61
- expect(r.size).toBe(0);
62
- });
63
- it("collects all valid routing files, skipping hidden entries", async () => {
64
- const projectsDir = path.join(tmpRoot, "projects");
65
- await fs.mkdir(path.join(projectsDir, "uuid-a"), { recursive: true });
66
- await fs.mkdir(path.join(projectsDir, "uuid-b"), { recursive: true });
67
- await fs.mkdir(path.join(projectsDir, ".hidden"), { recursive: true });
68
- await fs.writeFile(path.join(projectsDir, "uuid-a", "chat-routing.json"), JSON.stringify({ telegram: { inbound_chat_ids: ["1"] } }));
69
- await fs.writeFile(path.join(projectsDir, "uuid-b", "chat-routing.json"), JSON.stringify({ discord: { inbound_channel_ids: ["2"] } }));
70
- await fs.writeFile(path.join(projectsDir, ".hidden", "chat-routing.json"), JSON.stringify({ slack: { inbound_channel_ids: ["3"] } }));
71
- const all = await loadAllProjectChatRouting(tmpRoot);
72
- expect(all.size).toBe(2);
73
- expect(all.has("uuid-a")).toBe(true);
74
- expect(all.has("uuid-b")).toBe(true);
75
- expect(all.has(".hidden")).toBe(false);
76
- });
77
- });
78
- describe("collectInboundChannels", () => {
79
- it("returns empty list for empty config", () => {
80
- expect(collectInboundChannels({})).toEqual([]);
81
- });
82
- it("formats telegram ids as telegram:<id>", () => {
83
- const ch = collectInboundChannels({ telegram: { inbound_chat_ids: ["100", "200"] } });
84
- expect(ch).toEqual(["telegram:100", "telegram:200"]);
85
- });
86
- it("collects across all platforms", () => {
87
- const ch = collectInboundChannels({
88
- telegram: { inbound_chat_ids: ["t1"] },
89
- discord: { inbound_channel_ids: ["d1"] },
90
- slack: { inbound_channel_ids: ["s1"] },
91
- });
92
- expect(ch).toEqual(["telegram:t1", "discord:d1", "slack:s1"]);
93
- });
94
- });
95
- // v0.5.94 (WAB.2 Part A / TG.1 (a)) — DM allowlist key emission.
96
- describe("collectInboundChannels — v0.5.94 DM allowlist", () => {
97
- it("emits DM keys as telegram:dm:<user_id> when inbound_dm_user_ids is set", () => {
98
- const ch = collectInboundChannels({
99
- telegram: { inbound_dm_user_ids: ["123"] },
100
- });
101
- expect(ch).toEqual(["telegram:dm:123"]);
102
- });
103
- it("supports multiple DM user_ids", () => {
104
- const ch = collectInboundChannels({
105
- telegram: { inbound_dm_user_ids: ["123", "456"] },
106
- });
107
- expect(ch).toEqual(["telegram:dm:123", "telegram:dm:456"]);
108
- });
109
- it("emits both group + topic + DM keys when all are configured", () => {
110
- const ch = collectInboundChannels({
111
- telegram: {
112
- inbound_chat_ids: ["-100super"],
113
- inbound_topic_ids: [15],
114
- inbound_dm_user_ids: ["8075471258"],
115
- },
116
- });
117
- expect(ch).toEqual(["telegram:-100super:15", "telegram:dm:8075471258"]);
118
- });
119
- it("schema backwards-compat: config without inbound_dm_user_ids still works", () => {
120
- const ch = collectInboundChannels({
121
- telegram: { inbound_chat_ids: ["-100"] },
122
- });
123
- expect(ch).toEqual(["telegram:-100"]);
124
- });
125
- it("empty inbound_dm_user_ids list emits no DM keys", () => {
126
- const ch = collectInboundChannels({
127
- telegram: { inbound_dm_user_ids: [] },
128
- });
129
- expect(ch).toEqual([]);
130
- });
131
- });
132
- describe("buildRoutingIndex — v0.5.94 DM key indexing", () => {
133
- it("indexes DM keys so worker can look up by telegram:dm:<user_id>", () => {
134
- const cfgs = new Map([
135
- ["uuid-dm", { telegram: { inbound_dm_user_ids: ["8075471258"] } }],
136
- ]);
137
- const idx = buildRoutingIndex(cfgs);
138
- expect(idx.get("telegram:dm:8075471258")).toBe("uuid-dm");
139
- expect(idx.size).toBe(1);
140
- });
141
- it("two projects can each have their own DM allowlist (no collision)", () => {
142
- const cfgs = new Map([
143
- ["uuid-a", { telegram: { inbound_dm_user_ids: ["111"] } }],
144
- ["uuid-b", { telegram: { inbound_dm_user_ids: ["222"] } }],
145
- ]);
146
- const idx = buildRoutingIndex(cfgs);
147
- expect(idx.get("telegram:dm:111")).toBe("uuid-a");
148
- expect(idx.get("telegram:dm:222")).toBe("uuid-b");
149
- expect(idx.size).toBe(2);
150
- });
151
- it("same DM user across two projects: collision warned, last wins (consistent with existing behavior)", () => {
152
- const cfgs = new Map([
153
- ["uuid-a", { telegram: { inbound_dm_user_ids: ["999"] } }],
154
- ["uuid-b", { telegram: { inbound_dm_user_ids: ["999"] } }],
155
- ]);
156
- const warnings = [];
157
- const idx = buildRoutingIndex(cfgs, (m) => warnings.push(m));
158
- expect(idx.get("telegram:dm:999")).toBe("uuid-b");
159
- expect(warnings.length).toBe(1);
160
- expect(warnings[0]).toMatch(/collision.*telegram:dm:999/);
161
- });
162
- it("group + DM in same project: both keys index to same uuid", () => {
163
- const cfgs = new Map([
164
- [
165
- "uuid-x",
166
- {
167
- telegram: {
168
- inbound_chat_ids: ["-100"],
169
- inbound_topic_ids: [15],
170
- inbound_dm_user_ids: ["8075471258"],
171
- },
172
- },
173
- ],
174
- ]);
175
- const idx = buildRoutingIndex(cfgs);
176
- expect(idx.get("telegram:-100:15")).toBe("uuid-x");
177
- expect(idx.get("telegram:dm:8075471258")).toBe("uuid-x");
178
- expect(idx.get("telegram:-100")).toBeUndefined(); // strict topic whitelist preserved
179
- expect(idx.size).toBe(2);
180
- });
181
- });
182
- describe("buildRoutingIndex — v0.7.2 topic-aware keys", () => {
183
- it("emits topic-specific keys when inbound_topic_ids is set", () => {
184
- const cfgs = new Map([
185
- [
186
- "uuid-a",
187
- {
188
- telegram: {
189
- inbound_chat_ids: ["-1001234567890"],
190
- inbound_topic_ids: [5, 12],
191
- },
192
- },
193
- ],
194
- ]);
195
- const idx = buildRoutingIndex(cfgs);
196
- expect(idx.get("telegram:-1001234567890:5")).toBe("uuid-a");
197
- expect(idx.get("telegram:-1001234567890:12")).toBe("uuid-a");
198
- // Chat-only key NOT registered when topic_ids are set — we want
199
- // routing to be topic-strict.
200
- expect(idx.get("telegram:-1001234567890")).toBeUndefined();
201
- });
202
- it("two projects sharing one supergroup but different topics get distinct routing", () => {
203
- const cfgs = new Map([
204
- ["uuid-a", { telegram: { inbound_chat_ids: ["-100common"], inbound_topic_ids: [5] } }],
205
- ["uuid-b", { telegram: { inbound_chat_ids: ["-100common"], inbound_topic_ids: [10] } }],
206
- ]);
207
- const idx = buildRoutingIndex(cfgs);
208
- expect(idx.get("telegram:-100common:5")).toBe("uuid-a");
209
- expect(idx.get("telegram:-100common:10")).toBe("uuid-b");
210
- expect(idx.size).toBe(2);
211
- });
212
- it("falls back to chat-only key when inbound_topic_ids is empty/unset (legacy v0.7.1)", () => {
213
- const cfgs = new Map([["uuid-legacy", { telegram: { inbound_chat_ids: ["-100legacy"] } }]]);
214
- const idx = buildRoutingIndex(cfgs);
215
- expect(idx.get("telegram:-100legacy")).toBe("uuid-legacy");
216
- expect(idx.size).toBe(1);
217
- });
218
- });
219
- describe("buildRoutingIndex", () => {
220
- it("builds a chat_id → uuid map", () => {
221
- const cfgs = new Map([
222
- ["uuid-a", { telegram: { inbound_chat_ids: ["100", "200"] } }],
223
- ["uuid-b", { discord: { inbound_channel_ids: ["d1"] } }],
224
- ]);
225
- const idx = buildRoutingIndex(cfgs);
226
- expect(idx.get("telegram:100")).toBe("uuid-a");
227
- expect(idx.get("telegram:200")).toBe("uuid-a");
228
- expect(idx.get("discord:d1")).toBe("uuid-b");
229
- expect(idx.size).toBe(3);
230
- });
231
- it("calls onWarn on collision and lets the later project win", () => {
232
- const cfgs = new Map([
233
- ["uuid-a", { telegram: { inbound_chat_ids: ["100"] } }],
234
- ["uuid-b", { telegram: { inbound_chat_ids: ["100"] } }],
235
- ]);
236
- const warnings = [];
237
- const idx = buildRoutingIndex(cfgs, (m) => warnings.push(m));
238
- // Map iteration order on insertion → uuid-b wins.
239
- expect(idx.get("telegram:100")).toBe("uuid-b");
240
- expect(warnings.length).toBe(1);
241
- expect(warnings[0]).toMatch(/collision.*telegram:100/);
242
- });
243
- });
244
- describe("saveProjectChatRouting", () => {
245
- it("writes a routing file atomically, creating the project dir if missing", async () => {
246
- const uuid = "save-test";
247
- const res = await saveProjectChatRouting(uuid, { telegram: { report_channel: "telegram:abc", inbound_chat_ids: ["abc"] } }, tmpRoot);
248
- expect(res.path).toBe(path.join(tmpRoot, "projects", uuid, "chat-routing.json"));
249
- const back = await loadProjectChatRouting(uuid, tmpRoot);
250
- expect(back?.telegram?.report_channel).toBe("telegram:abc");
251
- });
252
- it("overwrites an existing routing file (rename-over)", async () => {
253
- const uuid = "overwrite-test";
254
- await saveProjectChatRouting(uuid, { telegram: { report_channel: "telegram:v1" } }, tmpRoot);
255
- await saveProjectChatRouting(uuid, { telegram: { report_channel: "telegram:v2" } }, tmpRoot);
256
- const back = await loadProjectChatRouting(uuid, tmpRoot);
257
- expect(back?.telegram?.report_channel).toBe("telegram:v2");
258
- });
259
- });
@@ -1,45 +0,0 @@
1
- /**
2
- * chat-daemon RPC client (v0.7.1 Phase B).
3
- *
4
- * Used by the MCP server when it wants to send a chat message but
5
- * doesn't want to start its own long-poll adapter (which would
6
- * collide with any other MCP server holding the same bot token).
7
- *
8
- * Behavior:
9
- * - One short-lived connection per request — keeps the implementation
10
- * trivial and avoids reconnect/backoff logic. Performance is fine
11
- * for v0.7.1's expected traffic (a handful of agent reports per
12
- * project session).
13
- * - Returns a typed result on success.
14
- * - Throws `DaemonUnreachableError` when the socket can't be reached
15
- * so callers can fall back to in-process send.
16
- * - Throws `DaemonRpcError` on JSON-RPC error responses.
17
- */
18
- import { type CreateTopicParams, type CreateTopicResult, type ListChannelsResult, type PingResult, type SendParams, type SendResult } from "./protocol.js";
19
- export declare class DaemonUnreachableError extends Error {
20
- constructor(message: string);
21
- }
22
- export declare class DaemonRpcError extends Error {
23
- readonly code: number;
24
- constructor(message: string, code: number);
25
- }
26
- export interface RpcClientOptions {
27
- dataRoot?: string;
28
- /** Connect timeout in ms. Default 1500 — local socket, fast. */
29
- connectTimeoutMs?: number;
30
- /** Request timeout in ms (waiting for response). Default 5000. */
31
- requestTimeoutMs?: number;
32
- }
33
- export declare class DaemonClient {
34
- private readonly address;
35
- private readonly connectTimeoutMs;
36
- private readonly requestTimeoutMs;
37
- constructor(opts?: RpcClientOptions);
38
- ping(): Promise<PingResult>;
39
- listChannels(): Promise<ListChannelsResult>;
40
- send(params: SendParams): Promise<SendResult>;
41
- createTopic(params: CreateTopicParams): Promise<CreateTopicResult>;
42
- call<R>(method: string, params: unknown): Promise<R>;
43
- private openSocket;
44
- }
45
- //# sourceMappingURL=rpc-client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/rpc-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EAGtB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,UAAU,EAEhB,MAAM,eAAe,CAAC;AAEvB,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBADrB,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM;CAKxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,IAAI,GAAE,gBAAqB;IAMvC,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAI3B,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAI3C,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7C,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAI5D,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAiB1D,OAAO,CAAC,UAAU;CAwBnB"}