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,221 +0,0 @@
1
- /**
2
- * Chat-daemon lifecycle — start / stop / status (v0.7.1 Phase A).
3
- *
4
- * The daemon owns the single long-poll connection per chat platform so
5
- * multiple Claude Code projects sharing the same machine can run their
6
- * own opensquid MCP servers without colliding on the bot token (Telegram
7
- * returns 409 Conflict when two consumers long-poll the same token —
8
- * the v0.7 cause of "last-connected wins" behavior).
9
- *
10
- * Lifecycle is single-instance per machine: PID file at
11
- * ~/.opensquid/chat-daemon.pid, log at ~/.opensquid/chat-daemon.log.
12
- * A `start` call against a running daemon is a no-op (idempotent);
13
- * `stop` is also idempotent. `status` is read-only.
14
- *
15
- * Outbound RPC (Unix socket) lands in Phase B; routing config + inbox
16
- * write-out in Phase C; auto-spawn from MCP server in Phase D.
17
- */
18
- import { spawn } from "node:child_process";
19
- import { promises as fs } from "node:fs";
20
- import { existsSync } from "node:fs";
21
- import * as os from "node:os";
22
- import * as path from "node:path";
23
- import { resolveDataRoot } from "../../codex/store.js";
24
- export function daemonPaths(dataRoot) {
25
- const root = resolveDataRoot(dataRoot);
26
- return {
27
- pidFile: path.join(root, "chat-daemon.pid"),
28
- logFile: path.join(root, "chat-daemon.log"),
29
- sockFile: path.join(root, "chat-daemon.sock"),
30
- };
31
- }
32
- /**
33
- * Read the pidfile and check whether the recorded process is alive.
34
- * A pidfile that points at a dead pid is treated as not-running (the
35
- * caller should clean it up if they want; status itself is read-only).
36
- */
37
- export async function status(dataRoot) {
38
- const paths = daemonPaths(dataRoot);
39
- if (!existsSync(paths.pidFile))
40
- return { running: false };
41
- let raw;
42
- try {
43
- raw = await fs.readFile(paths.pidFile, "utf8");
44
- }
45
- catch {
46
- return { running: false };
47
- }
48
- const pid = parseInt(raw.trim(), 10);
49
- if (!Number.isFinite(pid) || pid <= 0)
50
- return { running: false };
51
- if (!isProcessAlive(pid))
52
- return { running: false, stale_pid: pid };
53
- let uptime_ms = null;
54
- try {
55
- const stat = await fs.stat(paths.pidFile);
56
- uptime_ms = Date.now() - stat.mtimeMs;
57
- }
58
- catch {
59
- /* keep null */
60
- }
61
- return { running: true, pid, uptime_ms };
62
- }
63
- /** `kill -0 <pid>` portable check — throws ESRCH if dead, EPERM if alive-but-foreign. */
64
- function isProcessAlive(pid) {
65
- try {
66
- process.kill(pid, 0);
67
- return true;
68
- }
69
- catch (err) {
70
- const code = err.code;
71
- return code === "EPERM"; // alive but owned by another user (still counts)
72
- }
73
- }
74
- /**
75
- * Start the daemon by spawning a detached child that runs
76
- * `node dist/index.js chat-daemon-worker` (the actual long-poll loop
77
- * lives in worker.ts). Returns immediately once the child has either
78
- * forked successfully or failed.
79
- *
80
- * Idempotent: a start against an already-running daemon returns
81
- * `{ already_running: true, pid }` without launching a second process.
82
- *
83
- * Stale pidfile handling: if the pidfile points at a dead pid, it's
84
- * silently removed so this call can succeed.
85
- */
86
- export async function startDaemon(options = {}) {
87
- const cur = await status(options.dataRoot);
88
- if (cur.running)
89
- return { already_running: true, pid: cur.pid };
90
- const paths = daemonPaths(options.dataRoot);
91
- if (cur.stale_pid !== undefined) {
92
- await fs.unlink(paths.pidFile).catch(() => {
93
- /* race-tolerant */
94
- });
95
- }
96
- // The log file is opened append-mode by both the parent (header
97
- // line) and the child (stdout/stderr). fs.open with 'a' is atomic
98
- // append on POSIX, which is what we want here — no race between
99
- // start runs.
100
- await fs.mkdir(path.dirname(paths.logFile), { recursive: true });
101
- const logFd = await fs.open(paths.logFile, "a");
102
- try {
103
- await logFd.write(`\n=== chat-daemon start @ ${new Date().toISOString()} ===\n` +
104
- `host=${os.hostname()} node=${process.version} platform=${process.platform}\n`);
105
- }
106
- finally {
107
- await logFd.close();
108
- }
109
- // Reopen the log file for the child's stdio. spawn() wants raw FDs,
110
- // not FileHandles, so we open via the sync API.
111
- const fsSync = await import("node:fs");
112
- const childLogFd = fsSync.openSync(paths.logFile, "a");
113
- const nodeBin = options.nodeBin ?? process.execPath;
114
- const entrypoint = options.entrypoint ?? defaultEntrypoint();
115
- // Pass dataRoot through to the worker via OPENSQUID_HOME so the
116
- // worker's resolveDataRoot() lands in the same tree the parent
117
- // selected. Tests pass a tmpdir here; production uses the default
118
- // ~/.opensquid resolution.
119
- const childEnv = {
120
- ...process.env,
121
- OPENSQUID_CHAT_DAEMON: "1",
122
- };
123
- if (options.dataRoot)
124
- childEnv.OPENSQUID_HOME = options.dataRoot;
125
- const child = spawn(nodeBin, [entrypoint, "chat-daemon-worker"], {
126
- detached: true,
127
- stdio: ["ignore", childLogFd, childLogFd],
128
- env: childEnv,
129
- });
130
- // unref so the parent (this process) can exit without waiting for the
131
- // child. The child is now an independent process group leader because
132
- // of detached:true.
133
- child.unref();
134
- fsSync.closeSync(childLogFd);
135
- if (!child.pid) {
136
- throw new Error("chat-daemon: spawn returned no pid");
137
- }
138
- // The worker writes its OWN pidfile on startup (so the recorded pid
139
- // is the worker's, not a stale spawn-only value). Wait briefly for
140
- // the pidfile to appear; if it doesn't, the worker died on startup.
141
- // The timeout is generous (8s) because a cold-start of `node
142
- // dist/index.js chat-daemon-worker` with the optional grammy /
143
- // discord.js / @slack/* SDKs in the import graph can exceed 3s on
144
- // first invocation. Subsequent starts hit OS file cache and complete
145
- // in <500ms.
146
- const workerPid = await waitForPidfile(paths.pidFile, 8000);
147
- return { already_running: false, pid: workerPid };
148
- }
149
- /**
150
- * Stop a running daemon by sending SIGTERM, then waiting briefly for
151
- * the process to exit and the pidfile to disappear. Falls back to
152
- * SIGKILL after the grace period. Idempotent — a stop against a
153
- * not-running daemon returns `{ stopped: false }` without error.
154
- */
155
- export async function stopDaemon(options = {}) {
156
- const cur = await status(options.dataRoot);
157
- if (!cur.running) {
158
- // Clean up stale pidfile if present.
159
- if (cur.stale_pid !== undefined) {
160
- await fs.unlink(daemonPaths(options.dataRoot).pidFile).catch(() => {
161
- /* race-tolerant */
162
- });
163
- }
164
- return { stopped: false };
165
- }
166
- const grace = options.graceMs ?? 3000;
167
- try {
168
- process.kill(cur.pid, "SIGTERM");
169
- }
170
- catch {
171
- /* already gone */
172
- }
173
- const deadline = Date.now() + grace;
174
- while (Date.now() < deadline) {
175
- if (!isProcessAlive(cur.pid))
176
- break;
177
- await sleep(100);
178
- }
179
- if (isProcessAlive(cur.pid)) {
180
- try {
181
- process.kill(cur.pid, "SIGKILL");
182
- }
183
- catch {
184
- /* already gone */
185
- }
186
- }
187
- // Pidfile cleanup is the worker's job on graceful shutdown — but
188
- // best-effort here for the SIGKILL path.
189
- await fs.unlink(daemonPaths(options.dataRoot).pidFile).catch(() => {
190
- /* race-tolerant */
191
- });
192
- return { stopped: true, pid: cur.pid };
193
- }
194
- function sleep(ms) {
195
- return new Promise((r) => setTimeout(r, ms));
196
- }
197
- async function waitForPidfile(pidFile, timeoutMs) {
198
- const deadline = Date.now() + timeoutMs;
199
- while (Date.now() < deadline) {
200
- try {
201
- const raw = await fs.readFile(pidFile, "utf8");
202
- const pid = parseInt(raw.trim(), 10);
203
- if (Number.isFinite(pid) && pid > 0 && isProcessAlive(pid))
204
- return pid;
205
- }
206
- catch {
207
- /* not yet written */
208
- }
209
- await sleep(50);
210
- }
211
- throw new Error(`chat-daemon: worker did not write pidfile within ${timeoutMs}ms`);
212
- }
213
- /**
214
- * Find the on-disk path of the running opensquid binary so the child
215
- * can re-invoke the same module. Defaults to argv[1] (the script Node
216
- * was invoked with) which is `dist/index.js` for normal usage.
217
- */
218
- function defaultEntrypoint() {
219
- // process.argv[1] may be a relative path when launched via npx; resolve.
220
- return path.resolve(process.argv[1] ?? "dist/index.js");
221
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -1,163 +0,0 @@
1
- /**
2
- * chat-daemon lifecycle tests (v0.7.1 Phase A).
3
- *
4
- * Exercise the start/stop/status surface against REAL detached child
5
- * processes — the lifecycle logic IS the contract (fork-detach +
6
- * pidfile + signal handling), so synthetic stubs would prove nothing.
7
- *
8
- * Each test uses a tmpdir as the data root so multiple test runs don't
9
- * collide on the shared ~/.opensquid/chat-daemon.pid path, and so a
10
- * crashed test doesn't poison a real chat-daemon install.
11
- */
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 { daemonPaths, startDaemon, status, stopDaemon } from "./lifecycle.js";
17
- // Resolve to a real on-disk dist/index.js so the spawned worker can
18
- // actually run. Tests assume `npm run build` has been done; vitest's
19
- // suite runner triggers tsc when the build step is wired upstream.
20
- const REPO_ROOT = path.resolve(__dirname, "../../..");
21
- const ENTRYPOINT = path.join(REPO_ROOT, "dist", "index.js");
22
- let tmpDataRoot;
23
- beforeEach(async () => {
24
- tmpDataRoot = await fs.mkdtemp(path.join(os.tmpdir(), "opensquid-daemon-test-"));
25
- });
26
- afterEach(async (ctx) => {
27
- // On failure, dump the log + tmpdir contents before cleanup so we can
28
- // see what the daemon was up to. Vitest exposes the task state on ctx.
29
- if (ctx.task.result?.state === "fail") {
30
- try {
31
- const entries = await fs.readdir(tmpDataRoot);
32
- // eslint-disable-next-line no-console
33
- console.log(`[test debug] ${tmpDataRoot} entries: ${JSON.stringify(entries)}`);
34
- }
35
- catch {
36
- // eslint-disable-next-line no-console
37
- console.log(`[test debug] tmpDataRoot ${tmpDataRoot} missing`);
38
- }
39
- try {
40
- const log = await fs.readFile(path.join(tmpDataRoot, "chat-daemon.log"), "utf8");
41
- // eslint-disable-next-line no-console
42
- console.log(`[test debug] ${tmpDataRoot}/chat-daemon.log:\n${log}`);
43
- }
44
- catch {
45
- // eslint-disable-next-line no-console
46
- console.log(`[test debug] no chat-daemon.log in ${tmpDataRoot}`);
47
- }
48
- }
49
- // Best-effort: stop any daemon that's still running from a failed
50
- // test, then rm the tmp root.
51
- try {
52
- await stopDaemon({ dataRoot: tmpDataRoot });
53
- }
54
- catch {
55
- /* ignore */
56
- }
57
- await fs.rm(tmpDataRoot, { recursive: true, force: true });
58
- });
59
- function dataRootEnv() {
60
- return { dataRoot: tmpDataRoot, entrypoint: ENTRYPOINT };
61
- }
62
- describe("daemonPaths", () => {
63
- it("derives pid/log/sock paths from the data root", () => {
64
- const p = daemonPaths(tmpDataRoot);
65
- expect(p.pidFile).toBe(path.join(tmpDataRoot, "chat-daemon.pid"));
66
- expect(p.logFile).toBe(path.join(tmpDataRoot, "chat-daemon.log"));
67
- expect(p.sockFile).toBe(path.join(tmpDataRoot, "chat-daemon.sock"));
68
- });
69
- });
70
- describe("status (before any start)", () => {
71
- it("reports not-running when pidfile is absent", async () => {
72
- const s = await status(tmpDataRoot);
73
- expect(s.running).toBe(false);
74
- expect("stale_pid" in s ? s.stale_pid : undefined).toBeUndefined();
75
- });
76
- it("reports stale_pid when pidfile points at a dead pid", async () => {
77
- // pid 999999 is overwhelmingly unlikely to be a live process.
78
- const paths = daemonPaths(tmpDataRoot);
79
- await fs.mkdir(path.dirname(paths.pidFile), { recursive: true });
80
- await fs.writeFile(paths.pidFile, "999999\n");
81
- const s = await status(tmpDataRoot);
82
- expect(s.running).toBe(false);
83
- expect("stale_pid" in s ? s.stale_pid : undefined).toBe(999999);
84
- });
85
- it("reports not-running on a malformed pidfile (not a number)", async () => {
86
- const paths = daemonPaths(tmpDataRoot);
87
- await fs.mkdir(path.dirname(paths.pidFile), { recursive: true });
88
- await fs.writeFile(paths.pidFile, "garbage\n");
89
- const s = await status(tmpDataRoot);
90
- expect(s.running).toBe(false);
91
- });
92
- });
93
- describe("stopDaemon (idempotency)", () => {
94
- it("returns stopped:false when no daemon is running", async () => {
95
- const res = await stopDaemon({ dataRoot: tmpDataRoot });
96
- expect(res.stopped).toBe(false);
97
- });
98
- it("cleans up a stale pidfile during stop()", async () => {
99
- const paths = daemonPaths(tmpDataRoot);
100
- await fs.mkdir(path.dirname(paths.pidFile), { recursive: true });
101
- await fs.writeFile(paths.pidFile, "999999\n");
102
- await stopDaemon({ dataRoot: tmpDataRoot });
103
- // Pidfile should be gone.
104
- await expect(fs.access(paths.pidFile)).rejects.toThrow();
105
- });
106
- });
107
- describe("startDaemon + stopDaemon (end-to-end)", () => {
108
- it("starts, reports running, then stops cleanly", async () => {
109
- const env = dataRootEnv();
110
- const startRes = await startDaemon(env);
111
- expect(startRes.already_running).toBe(false);
112
- expect(startRes.pid).toBeGreaterThan(0);
113
- const s = await status(env.dataRoot);
114
- expect(s.running).toBe(true);
115
- if (s.running) {
116
- expect(s.pid).toBe(startRes.pid);
117
- }
118
- const stopRes = await stopDaemon({ dataRoot: env.dataRoot });
119
- expect(stopRes.stopped).toBe(true);
120
- expect(stopRes.pid).toBe(startRes.pid);
121
- // Pidfile cleaned up.
122
- const after = await status(env.dataRoot);
123
- expect(after.running).toBe(false);
124
- }, 15000);
125
- it("is idempotent: a second start() returns already_running:true with the same pid", async () => {
126
- const env = dataRootEnv();
127
- const first = await startDaemon(env);
128
- expect(first.already_running).toBe(false);
129
- const second = await startDaemon(env);
130
- expect(second.already_running).toBe(true);
131
- expect(second.pid).toBe(first.pid);
132
- await stopDaemon({ dataRoot: env.dataRoot });
133
- }, 15000);
134
- it("writes daemon boot lines to the log file", async () => {
135
- const env = dataRootEnv();
136
- await startDaemon(env);
137
- // Give the worker a beat to emit its boot lines.
138
- await new Promise((r) => setTimeout(r, 600));
139
- const log = await fs.readFile(daemonPaths(env.dataRoot).logFile, "utf8");
140
- expect(log).toContain("=== chat-daemon start @");
141
- expect(log).toContain("worker booted pid=");
142
- await stopDaemon({ dataRoot: env.dataRoot });
143
- }, 15000);
144
- it("survives a stop on a daemon spawned from a different test (stale handling)", async () => {
145
- const env = dataRootEnv();
146
- // First start, then kill the process behind status's back so the
147
- // pidfile becomes stale. Real-world: daemon crashed.
148
- const first = await startDaemon(env);
149
- try {
150
- process.kill(first.pid, "SIGKILL");
151
- }
152
- catch {
153
- /* race */
154
- }
155
- // Wait briefly for the kill to take effect.
156
- await new Promise((r) => setTimeout(r, 200));
157
- const s = await status(env.dataRoot);
158
- expect(s.running).toBe(false);
159
- // Should NOT throw and should report not-stopped (since it wasn't).
160
- const stopRes = await stopDaemon({ dataRoot: env.dataRoot });
161
- expect(stopRes.stopped).toBe(false);
162
- }, 15000);
163
- });
@@ -1,107 +0,0 @@
1
- /**
2
- * chat-daemon ↔ MCP server wire protocol (v0.7.1 Phase B).
3
- *
4
- * Shape: JSON-RPC 2.0 over newline-delimited JSON. One JSON object per
5
- * line; both directions use the same framing. Stateless — the daemon
6
- * doesn't track client connections beyond the lifetime of a single
7
- * request/response pair.
8
- *
9
- * Transport address (set by `daemonPaths().sockFile`):
10
- * - macOS/Linux: ~/.opensquid/chat-daemon.sock (AF_UNIX)
11
- * - Windows: \\.\pipe\opensquid-chat-daemon (named pipe)
12
- * Node's `net.createServer({path})` accepts both shapes transparently;
13
- * `daemonSockAddress()` below returns the right form for the host OS.
14
- *
15
- * Why not gRPC / HTTP / MCP-style stdio? gRPC drags in a 3 MB grpc-js
16
- * dependency that we already explicitly avoided in the engine's
17
- * subprocess design. HTTP needs port allocation + firewall coordination.
18
- * MCP stdio needs a parent-child relationship — but the daemon is
19
- * grandparent / orphan to the MCP servers (it outlives them). Local
20
- * socket + JSON-RPC is the smallest correct surface.
21
- */
22
- /** Send a chat message via the daemon's owned adapter. */
23
- export interface SendParams {
24
- channel: string;
25
- text: string;
26
- replyTo?: string;
27
- /** Telegram forum topic id (v0.7.2). Ignored by adapters that don't support threading. */
28
- threadId?: string;
29
- }
30
- /** v0.7.2 — Create a Telegram forum topic via the daemon's owned bot. */
31
- export interface CreateTopicParams {
32
- platform: "telegram";
33
- chat_id: string;
34
- name: string;
35
- icon_color?: number;
36
- icon_custom_emoji_id?: string;
37
- }
38
- export interface CreateTopicResult {
39
- message_thread_id: number;
40
- name: string;
41
- }
42
- export interface SendResult {
43
- ok: boolean;
44
- platform: string;
45
- message_id: string;
46
- delivered_at: string;
47
- }
48
- /** List the platforms the daemon currently has active adapters for. */
49
- export interface ListChannelsParams {
50
- }
51
- export interface ListChannelsResult {
52
- active_platforms: string[];
53
- uptime_ms: number | null;
54
- /**
55
- * 0.7.4 (#147): platforms whose long-poll lost to a 409 Conflict
56
- * and degraded to outbound-only mode. Surfaced via
57
- * `chat_daemon_status` so operators can diagnose "where did my
58
- * inbound message go?"
59
- */
60
- outbound_only_platforms?: string[];
61
- }
62
- /** Liveness probe for the auto-spawn path. */
63
- export interface PingParams {
64
- }
65
- export interface PingResult {
66
- pong: true;
67
- pid: number;
68
- version: string;
69
- }
70
- export type DaemonMethod = "send" | "list_channels" | "ping" | "create_topic";
71
- export interface JsonRpcRequest<P = unknown> {
72
- jsonrpc: "2.0";
73
- id: number | string;
74
- method: DaemonMethod;
75
- params?: P;
76
- }
77
- export interface JsonRpcSuccess<R = unknown> {
78
- jsonrpc: "2.0";
79
- id: number | string;
80
- result: R;
81
- }
82
- export interface JsonRpcFailure {
83
- jsonrpc: "2.0";
84
- id: number | string | null;
85
- error: {
86
- code: number;
87
- message: string;
88
- data?: unknown;
89
- };
90
- }
91
- export type JsonRpcResponse<R = unknown> = JsonRpcSuccess<R> | JsonRpcFailure;
92
- export declare const JSON_RPC_PARSE_ERROR = -32700;
93
- export declare const JSON_RPC_INVALID_REQUEST = -32600;
94
- export declare const JSON_RPC_METHOD_NOT_FOUND = -32601;
95
- export declare const JSON_RPC_INVALID_PARAMS = -32602;
96
- export declare const JSON_RPC_INTERNAL_ERROR = -32603;
97
- /**
98
- * Return the platform-correct socket address for `net.createServer` /
99
- * `net.connect`. On Unix this is a path on disk; on Windows this is a
100
- * named-pipe path. Node accepts both behind the same `{path}` API.
101
- *
102
- * On Windows we don't actually have a filesystem path to use, so the
103
- * pidfile name (without the .pid suffix) drives the pipe name to keep
104
- * dataRoot isolation working across multiple installs.
105
- */
106
- export declare function daemonSockAddress(dataRoot?: string): string;
107
- //# sourceMappingURL=protocol.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAWH,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,UAAU,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;CAElC;AAED,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,8CAA8C;AAC9C,MAAM,WAAW,UAAU;CAE1B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,cAAc,CAAC;AAM9E,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,CAAC;CACZ;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC;CACX;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC1D;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC;AAG9E,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAC3C,eAAO,MAAM,wBAAwB,SAAS,CAAC;AAC/C,eAAO,MAAM,yBAAyB,SAAS,CAAC;AAChD,eAAO,MAAM,uBAAuB,SAAS,CAAC;AAC9C,eAAO,MAAM,uBAAuB,SAAS,CAAC;AAM9C;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAW3D"}
@@ -1,54 +0,0 @@
1
- /**
2
- * chat-daemon ↔ MCP server wire protocol (v0.7.1 Phase B).
3
- *
4
- * Shape: JSON-RPC 2.0 over newline-delimited JSON. One JSON object per
5
- * line; both directions use the same framing. Stateless — the daemon
6
- * doesn't track client connections beyond the lifetime of a single
7
- * request/response pair.
8
- *
9
- * Transport address (set by `daemonPaths().sockFile`):
10
- * - macOS/Linux: ~/.opensquid/chat-daemon.sock (AF_UNIX)
11
- * - Windows: \\.\pipe\opensquid-chat-daemon (named pipe)
12
- * Node's `net.createServer({path})` accepts both shapes transparently;
13
- * `daemonSockAddress()` below returns the right form for the host OS.
14
- *
15
- * Why not gRPC / HTTP / MCP-style stdio? gRPC drags in a 3 MB grpc-js
16
- * dependency that we already explicitly avoided in the engine's
17
- * subprocess design. HTTP needs port allocation + firewall coordination.
18
- * MCP stdio needs a parent-child relationship — but the daemon is
19
- * grandparent / orphan to the MCP servers (it outlives them). Local
20
- * socket + JSON-RPC is the smallest correct surface.
21
- */
22
- import * as os from "node:os";
23
- import * as path from "node:path";
24
- import { daemonPaths } from "./lifecycle.js";
25
- // Standard JSON-RPC error codes (we use the public-rage subset).
26
- export const JSON_RPC_PARSE_ERROR = -32700;
27
- export const JSON_RPC_INVALID_REQUEST = -32600;
28
- export const JSON_RPC_METHOD_NOT_FOUND = -32601;
29
- export const JSON_RPC_INVALID_PARAMS = -32602;
30
- export const JSON_RPC_INTERNAL_ERROR = -32603;
31
- // ---------------------------------------------------------------------
32
- // Socket address — cross-platform
33
- // ---------------------------------------------------------------------
34
- /**
35
- * Return the platform-correct socket address for `net.createServer` /
36
- * `net.connect`. On Unix this is a path on disk; on Windows this is a
37
- * named-pipe path. Node accepts both behind the same `{path}` API.
38
- *
39
- * On Windows we don't actually have a filesystem path to use, so the
40
- * pidfile name (without the .pid suffix) drives the pipe name to keep
41
- * dataRoot isolation working across multiple installs.
42
- */
43
- export function daemonSockAddress(dataRoot) {
44
- const paths = daemonPaths(dataRoot);
45
- if (os.platform() === "win32") {
46
- // Windows named pipe — derive a stable name from the data root so
47
- // tests with tmpdirs and prod with ~/.opensquid don't collide.
48
- // Pipe names live in a flat namespace; hash the dataRoot to a
49
- // short suffix.
50
- const fingerprint = path.basename(path.dirname(paths.sockFile));
51
- return `\\\\.\\pipe\\opensquid-chat-daemon-${fingerprint}`;
52
- }
53
- return paths.sockFile;
54
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA2F7C,iEAAiE;AACjE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAK,CAAC;AAC3C,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAK,CAAC;AAC/C,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,KAAK,CAAC;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAAK,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAAK,CAAC;AAE9C,wEAAwE;AACxE,kCAAkC;AAClC,wEAAwE;AAExE;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAiB;IACjD,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC9B,kEAAkE;QAClE,+DAA+D;QAC/D,8DAA8D;QAC9D,gBAAgB;QAChB,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChE,OAAO,sCAAsC,WAAW,EAAE,CAAC;IAC7D,CAAC;IACD,OAAO,KAAK,CAAC,QAAQ,CAAC;AACxB,CAAC"}