@decibelsystems/tools 2.2.0-beta.0 → 3.1.0

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 (435) hide show
  1. package/CHANGELOG.md +226 -0
  2. package/dist/agentCommands.d.ts +0 -1
  3. package/dist/agentCommands.d.ts.map +1 -1
  4. package/dist/agentCommands.js +19 -8
  5. package/dist/agentCommands.js.map +1 -1
  6. package/dist/agentPresence.d.ts +82 -0
  7. package/dist/agentPresence.d.ts.map +1 -1
  8. package/dist/agentPresence.js +194 -26
  9. package/dist/agentPresence.js.map +1 -1
  10. package/dist/agentic/index.d.ts +18 -8
  11. package/dist/agentic/index.d.ts.map +1 -1
  12. package/dist/agentic/index.js +18 -144
  13. package/dist/agentic/index.js.map +1 -1
  14. package/dist/agentic/types.d.ts +106 -0
  15. package/dist/agentic/types.d.ts.map +1 -1
  16. package/dist/agentic/types.js +77 -1
  17. package/dist/agentic/types.js.map +1 -1
  18. package/dist/architectAdrs.d.ts +27 -3
  19. package/dist/architectAdrs.d.ts.map +1 -1
  20. package/dist/architectAdrs.js +92 -4
  21. package/dist/architectAdrs.js.map +1 -1
  22. package/dist/artifacts.d.ts +60 -0
  23. package/dist/artifacts.d.ts.map +1 -0
  24. package/dist/artifacts.js +208 -0
  25. package/dist/artifacts.js.map +1 -0
  26. package/dist/asks.d.ts +93 -0
  27. package/dist/asks.d.ts.map +1 -0
  28. package/dist/asks.js +383 -0
  29. package/dist/asks.js.map +1 -0
  30. package/dist/chat.d.ts +47 -0
  31. package/dist/chat.d.ts.map +1 -0
  32. package/dist/chat.js +288 -0
  33. package/dist/chat.js.map +1 -0
  34. package/dist/client/transports.d.ts.map +1 -1
  35. package/dist/client/transports.js +13 -3
  36. package/dist/client/transports.js.map +1 -1
  37. package/dist/daemon.d.ts +33 -3
  38. package/dist/daemon.d.ts.map +1 -1
  39. package/dist/daemon.js +225 -52
  40. package/dist/daemon.js.map +1 -1
  41. package/dist/daemonConfig.d.ts +46 -0
  42. package/dist/daemonConfig.d.ts.map +1 -1
  43. package/dist/daemonConfig.js +57 -12
  44. package/dist/daemonConfig.js.map +1 -1
  45. package/dist/decibelPaths.d.ts.map +1 -1
  46. package/dist/decibelPaths.js +39 -6
  47. package/dist/decibelPaths.js.map +1 -1
  48. package/dist/domain/issue.d.ts +81 -0
  49. package/dist/domain/issue.d.ts.map +1 -0
  50. package/dist/domain/issue.js +106 -0
  51. package/dist/domain/issue.js.map +1 -0
  52. package/dist/domain/issueCodec.d.ts +39 -0
  53. package/dist/domain/issueCodec.d.ts.map +1 -0
  54. package/dist/domain/issueCodec.js +308 -0
  55. package/dist/domain/issueCodec.js.map +1 -0
  56. package/dist/domain/issueRepository.d.ts +124 -0
  57. package/dist/domain/issueRepository.d.ts.map +1 -0
  58. package/dist/domain/issueRepository.js +296 -0
  59. package/dist/domain/issueRepository.js.map +1 -0
  60. package/dist/domain/uid.d.ts +16 -0
  61. package/dist/domain/uid.d.ts.map +1 -0
  62. package/dist/domain/uid.js +43 -0
  63. package/dist/domain/uid.js.map +1 -0
  64. package/dist/facades/definitions.d.ts +0 -1
  65. package/dist/facades/definitions.d.ts.map +1 -1
  66. package/dist/facades/definitions.js +140 -116
  67. package/dist/facades/definitions.js.map +1 -1
  68. package/dist/facades/index.d.ts +2 -1
  69. package/dist/facades/index.d.ts.map +1 -1
  70. package/dist/facades/index.js +1 -1
  71. package/dist/facades/index.js.map +1 -1
  72. package/dist/facades/types.d.ts +17 -0
  73. package/dist/facades/types.d.ts.map +1 -1
  74. package/dist/hq/defineAgent.d.ts.map +1 -1
  75. package/dist/hq/defineAgent.js +7 -11
  76. package/dist/hq/defineAgent.js.map +1 -1
  77. package/dist/httpArgs.d.ts +11 -0
  78. package/dist/httpArgs.d.ts.map +1 -0
  79. package/dist/httpArgs.js +39 -0
  80. package/dist/httpArgs.js.map +1 -0
  81. package/dist/httpServer.d.ts +1 -12
  82. package/dist/httpServer.d.ts.map +1 -1
  83. package/dist/httpServer.js +298 -144
  84. package/dist/httpServer.js.map +1 -1
  85. package/dist/identity.d.ts +123 -0
  86. package/dist/identity.d.ts.map +1 -0
  87. package/dist/identity.js +312 -0
  88. package/dist/identity.js.map +1 -0
  89. package/dist/kernel.d.ts +46 -1
  90. package/dist/kernel.d.ts.map +1 -1
  91. package/dist/kernel.js +334 -26
  92. package/dist/kernel.js.map +1 -1
  93. package/dist/killSwitch.d.ts +117 -0
  94. package/dist/killSwitch.d.ts.map +1 -0
  95. package/dist/killSwitch.js +161 -0
  96. package/dist/killSwitch.js.map +1 -0
  97. package/dist/lib/agent-services/assumptions.d.ts.map +1 -1
  98. package/dist/lib/agent-services/assumptions.js +8 -2
  99. package/dist/lib/agent-services/assumptions.js.map +1 -1
  100. package/dist/lib/atomicWrite.d.ts +49 -0
  101. package/dist/lib/atomicWrite.d.ts.map +1 -0
  102. package/dist/lib/atomicWrite.js +157 -0
  103. package/dist/lib/atomicWrite.js.map +1 -0
  104. package/dist/lib/envelope.d.ts +31 -0
  105. package/dist/lib/envelope.d.ts.map +1 -0
  106. package/dist/lib/envelope.js +80 -0
  107. package/dist/lib/envelope.js.map +1 -0
  108. package/dist/lib/fileLock.d.ts +38 -0
  109. package/dist/lib/fileLock.d.ts.map +1 -0
  110. package/dist/lib/fileLock.js +124 -0
  111. package/dist/lib/fileLock.js.map +1 -0
  112. package/dist/lib/recordIdAllocator.d.ts +102 -0
  113. package/dist/lib/recordIdAllocator.d.ts.map +1 -0
  114. package/dist/lib/recordIdAllocator.js +179 -0
  115. package/dist/lib/recordIdAllocator.js.map +1 -0
  116. package/dist/orgId.d.ts +23 -0
  117. package/dist/orgId.d.ts.map +1 -0
  118. package/dist/orgId.js +73 -0
  119. package/dist/orgId.js.map +1 -0
  120. package/dist/pairCli.d.ts +2 -0
  121. package/dist/pairCli.d.ts.map +1 -0
  122. package/dist/pairCli.js +97 -0
  123. package/dist/pairCli.js.map +1 -0
  124. package/dist/peerBroker.d.ts +40 -0
  125. package/dist/peerBroker.d.ts.map +1 -0
  126. package/dist/peerBroker.js +70 -0
  127. package/dist/peerBroker.js.map +1 -0
  128. package/dist/peerBrokerSupervisor.d.ts +21 -0
  129. package/dist/peerBrokerSupervisor.d.ts.map +1 -0
  130. package/dist/peerBrokerSupervisor.js +131 -0
  131. package/dist/peerBrokerSupervisor.js.map +1 -0
  132. package/dist/projectPaths.d.ts.map +1 -1
  133. package/dist/projectPaths.js +1 -19
  134. package/dist/projectPaths.js.map +1 -1
  135. package/dist/projectRegistry.d.ts +22 -0
  136. package/dist/projectRegistry.d.ts.map +1 -1
  137. package/dist/projectRegistry.js +69 -2
  138. package/dist/projectRegistry.js.map +1 -1
  139. package/dist/pty/boundary.d.ts +39 -0
  140. package/dist/pty/boundary.d.ts.map +1 -0
  141. package/dist/pty/boundary.js +100 -0
  142. package/dist/pty/boundary.js.map +1 -0
  143. package/dist/pty/errors.d.ts +6 -0
  144. package/dist/pty/errors.d.ts.map +1 -0
  145. package/dist/pty/errors.js +12 -0
  146. package/dist/pty/errors.js.map +1 -0
  147. package/dist/pty/fleet.d.ts +159 -0
  148. package/dist/pty/fleet.d.ts.map +1 -0
  149. package/dist/pty/fleet.js +543 -0
  150. package/dist/pty/fleet.js.map +1 -0
  151. package/dist/pty/launchProfile.d.ts +45 -0
  152. package/dist/pty/launchProfile.d.ts.map +1 -0
  153. package/dist/pty/launchProfile.js +149 -0
  154. package/dist/pty/launchProfile.js.map +1 -0
  155. package/dist/pty/openTerminal.d.ts +48 -0
  156. package/dist/pty/openTerminal.d.ts.map +1 -0
  157. package/dist/pty/openTerminal.js +185 -0
  158. package/dist/pty/openTerminal.js.map +1 -0
  159. package/dist/pty/pairDialog.d.ts +16 -0
  160. package/dist/pty/pairDialog.d.ts.map +1 -0
  161. package/dist/pty/pairDialog.js +121 -0
  162. package/dist/pty/pairDialog.js.map +1 -0
  163. package/dist/pty/pairing.d.ts +86 -0
  164. package/dist/pty/pairing.d.ts.map +1 -0
  165. package/dist/pty/pairing.js +351 -0
  166. package/dist/pty/pairing.js.map +1 -0
  167. package/dist/pty/routes.d.ts +49 -0
  168. package/dist/pty/routes.d.ts.map +1 -0
  169. package/dist/pty/routes.js +624 -0
  170. package/dist/pty/routes.js.map +1 -0
  171. package/dist/pty/sessions.d.ts +167 -0
  172. package/dist/pty/sessions.d.ts.map +1 -0
  173. package/dist/pty/sessions.js +513 -0
  174. package/dist/pty/sessions.js.map +1 -0
  175. package/dist/pty/tmux.d.ts +109 -0
  176. package/dist/pty/tmux.d.ts.map +1 -0
  177. package/dist/pty/tmux.js +271 -0
  178. package/dist/pty/tmux.js.map +1 -0
  179. package/dist/roster.d.ts +37 -0
  180. package/dist/roster.d.ts.map +1 -0
  181. package/dist/roster.js +126 -0
  182. package/dist/roster.js.map +1 -0
  183. package/dist/runtime/circuitBreaker.d.ts +95 -0
  184. package/dist/runtime/circuitBreaker.d.ts.map +1 -0
  185. package/dist/runtime/circuitBreaker.js +178 -0
  186. package/dist/runtime/circuitBreaker.js.map +1 -0
  187. package/dist/runtime/daemonMeta.d.ts +53 -0
  188. package/dist/runtime/daemonMeta.d.ts.map +1 -0
  189. package/dist/runtime/daemonMeta.js +121 -0
  190. package/dist/runtime/daemonMeta.js.map +1 -0
  191. package/dist/runtime/ensureRuntime.d.ts +103 -0
  192. package/dist/runtime/ensureRuntime.d.ts.map +1 -0
  193. package/dist/runtime/ensureRuntime.js +276 -0
  194. package/dist/runtime/ensureRuntime.js.map +1 -0
  195. package/dist/runtime/extensions.d.ts +57 -0
  196. package/dist/runtime/extensions.d.ts.map +1 -0
  197. package/dist/runtime/extensions.js +216 -0
  198. package/dist/runtime/extensions.js.map +1 -0
  199. package/dist/runtime/localHttp.d.ts +24 -0
  200. package/dist/runtime/localHttp.d.ts.map +1 -0
  201. package/dist/runtime/localHttp.js +84 -0
  202. package/dist/runtime/localHttp.js.map +1 -0
  203. package/dist/runtime/projectResolution.d.ts +29 -0
  204. package/dist/runtime/projectResolution.d.ts.map +1 -0
  205. package/dist/runtime/projectResolution.js +67 -0
  206. package/dist/runtime/projectResolution.js.map +1 -0
  207. package/dist/runtime/protocol.d.ts +62 -0
  208. package/dist/runtime/protocol.d.ts.map +1 -0
  209. package/dist/runtime/protocol.js +74 -0
  210. package/dist/runtime/protocol.js.map +1 -0
  211. package/dist/sentinelIssues.d.ts +42 -2
  212. package/dist/sentinelIssues.d.ts.map +1 -1
  213. package/dist/sentinelIssues.js +167 -65
  214. package/dist/sentinelIssues.js.map +1 -1
  215. package/dist/server.js +123 -15
  216. package/dist/server.js.map +1 -1
  217. package/dist/toolConfig.d.ts.map +1 -1
  218. package/dist/toolConfig.js +12 -0
  219. package/dist/toolConfig.js.map +1 -1
  220. package/dist/tools/agentic/agentQueue.d.ts.map +1 -1
  221. package/dist/tools/agentic/agentQueue.js +4 -0
  222. package/dist/tools/agentic/agentQueue.js.map +1 -1
  223. package/dist/tools/agentic/index.d.ts.map +1 -1
  224. package/dist/tools/agentic/index.js +28 -2
  225. package/dist/tools/agentic/index.js.map +1 -1
  226. package/dist/tools/architect/index.d.ts +1 -0
  227. package/dist/tools/architect/index.d.ts.map +1 -1
  228. package/dist/tools/architect/index.js +52 -3
  229. package/dist/tools/architect/index.js.map +1 -1
  230. package/dist/tools/architect.d.ts.map +1 -1
  231. package/dist/tools/architect.js +59 -8
  232. package/dist/tools/architect.js.map +1 -1
  233. package/dist/tools/artifacts.d.ts +5 -0
  234. package/dist/tools/artifacts.d.ts.map +1 -0
  235. package/dist/tools/artifacts.js +89 -0
  236. package/dist/tools/artifacts.js.map +1 -0
  237. package/dist/tools/asks.d.ts +6 -0
  238. package/dist/tools/asks.d.ts.map +1 -0
  239. package/dist/tools/asks.js +104 -0
  240. package/dist/tools/asks.js.map +1 -0
  241. package/dist/tools/context.d.ts +10 -5
  242. package/dist/tools/context.d.ts.map +1 -1
  243. package/dist/tools/context.js +45 -18
  244. package/dist/tools/context.js.map +1 -1
  245. package/dist/tools/coordinator/coordinator.d.ts.map +1 -1
  246. package/dist/tools/coordinator/coordinator.js +48 -19
  247. package/dist/tools/coordinator/coordinator.js.map +1 -1
  248. package/dist/tools/coordinator/index.d.ts +1 -0
  249. package/dist/tools/coordinator/index.d.ts.map +1 -1
  250. package/dist/tools/coordinator/index.js +42 -2
  251. package/dist/tools/coordinator/index.js.map +1 -1
  252. package/dist/tools/crit.d.ts +2 -1
  253. package/dist/tools/crit.d.ts.map +1 -1
  254. package/dist/tools/crit.js +19 -4
  255. package/dist/tools/crit.js.map +1 -1
  256. package/dist/tools/data-inspector.d.ts +1 -1
  257. package/dist/tools/data-inspector.d.ts.map +1 -1
  258. package/dist/tools/data-inspector.js +1 -1
  259. package/dist/tools/data-inspector.js.map +1 -1
  260. package/dist/tools/designer/index.d.ts.map +1 -1
  261. package/dist/tools/designer/index.js +4 -3
  262. package/dist/tools/designer/index.js.map +1 -1
  263. package/dist/tools/designer/lateral-tools.d.ts.map +1 -1
  264. package/dist/tools/designer/lateral-tools.js +4 -2
  265. package/dist/tools/designer/lateral-tools.js.map +1 -1
  266. package/dist/tools/designer.d.ts +2 -1
  267. package/dist/tools/designer.d.ts.map +1 -1
  268. package/dist/tools/designer.js +24 -12
  269. package/dist/tools/designer.js.map +1 -1
  270. package/dist/tools/dojo/index.d.ts.map +1 -1
  271. package/dist/tools/dojo/index.js +52 -3
  272. package/dist/tools/dojo/index.js.map +1 -1
  273. package/dist/tools/dojo.d.ts +65 -3
  274. package/dist/tools/dojo.d.ts.map +1 -1
  275. package/dist/tools/dojo.js +148 -23
  276. package/dist/tools/dojo.js.map +1 -1
  277. package/dist/tools/friction.d.ts.map +1 -1
  278. package/dist/tools/friction.js +16 -6
  279. package/dist/tools/friction.js.map +1 -1
  280. package/dist/tools/guardian.d.ts +7 -0
  281. package/dist/tools/guardian.d.ts.map +1 -1
  282. package/dist/tools/guardian.js +152 -11
  283. package/dist/tools/guardian.js.map +1 -1
  284. package/dist/tools/hygiene/oracle-hygiene.d.ts.map +1 -1
  285. package/dist/tools/hygiene/oracle-hygiene.js +8 -2
  286. package/dist/tools/hygiene/oracle-hygiene.js.map +1 -1
  287. package/dist/tools/index.d.ts.map +1 -1
  288. package/dist/tools/index.js +33 -18
  289. package/dist/tools/index.js.map +1 -1
  290. package/dist/tools/killswitch/index.d.ts +13 -0
  291. package/dist/tools/killswitch/index.d.ts.map +1 -0
  292. package/dist/tools/killswitch/index.js +138 -0
  293. package/dist/tools/killswitch/index.js.map +1 -0
  294. package/dist/tools/learnings.d.ts +2 -1
  295. package/dist/tools/learnings.d.ts.map +1 -1
  296. package/dist/tools/learnings.js +31 -15
  297. package/dist/tools/learnings.js.map +1 -1
  298. package/dist/tools/oracle/index.d.ts.map +1 -1
  299. package/dist/tools/oracle/index.js +8 -0
  300. package/dist/tools/oracle/index.js.map +1 -1
  301. package/dist/tools/oracle.d.ts +7 -0
  302. package/dist/tools/oracle.d.ts.map +1 -1
  303. package/dist/tools/oracle.js +41 -12
  304. package/dist/tools/oracle.js.map +1 -1
  305. package/dist/tools/policy.d.ts +2 -1
  306. package/dist/tools/policy.d.ts.map +1 -1
  307. package/dist/tools/policy.js +28 -9
  308. package/dist/tools/policy.js.map +1 -1
  309. package/dist/tools/postoffice/index.d.ts +10 -0
  310. package/dist/tools/postoffice/index.d.ts.map +1 -0
  311. package/dist/tools/postoffice/index.js +218 -0
  312. package/dist/tools/postoffice/index.js.map +1 -0
  313. package/dist/tools/postoffice.d.ts +78 -0
  314. package/dist/tools/postoffice.d.ts.map +1 -0
  315. package/dist/tools/postoffice.js +199 -0
  316. package/dist/tools/postoffice.js.map +1 -0
  317. package/dist/tools/provenance.d.ts +18 -2
  318. package/dist/tools/provenance.d.ts.map +1 -1
  319. package/dist/tools/provenance.js +68 -40
  320. package/dist/tools/provenance.js.map +1 -1
  321. package/dist/tools/registry/index.d.ts.map +1 -1
  322. package/dist/tools/registry/index.js +24 -0
  323. package/dist/tools/registry/index.js.map +1 -1
  324. package/dist/tools/sentinel/index.d.ts +1 -0
  325. package/dist/tools/sentinel/index.d.ts.map +1 -1
  326. package/dist/tools/sentinel/index.js +142 -6
  327. package/dist/tools/sentinel/index.js.map +1 -1
  328. package/dist/tools/sentinel.d.ts +152 -1
  329. package/dist/tools/sentinel.d.ts.map +1 -1
  330. package/dist/tools/sentinel.js +609 -328
  331. package/dist/tools/sentinel.js.map +1 -1
  332. package/dist/tools/shared/index.d.ts +2 -1
  333. package/dist/tools/shared/index.d.ts.map +1 -1
  334. package/dist/tools/shared/index.js +2 -1
  335. package/dist/tools/shared/index.js.map +1 -1
  336. package/dist/tools/shared/response.d.ts +24 -0
  337. package/dist/tools/shared/response.d.ts.map +1 -1
  338. package/dist/tools/shared/response.js +61 -7
  339. package/dist/tools/shared/response.js.map +1 -1
  340. package/dist/tools/shared/runTracker.d.ts +2 -0
  341. package/dist/tools/shared/runTracker.d.ts.map +1 -1
  342. package/dist/tools/shared/runTracker.js +38 -0
  343. package/dist/tools/shared/runTracker.js.map +1 -1
  344. package/dist/tools/shared/storeRead.d.ts +153 -0
  345. package/dist/tools/shared/storeRead.d.ts.map +1 -0
  346. package/dist/tools/shared/storeRead.js +242 -0
  347. package/dist/tools/shared/storeRead.js.map +1 -0
  348. package/dist/tools/swarm.d.ts +3 -1
  349. package/dist/tools/swarm.d.ts.map +1 -1
  350. package/dist/tools/swarm.js +6 -12
  351. package/dist/tools/swarm.js.map +1 -1
  352. package/dist/tools/testSpec.d.ts +3 -2
  353. package/dist/tools/testSpec.d.ts.map +1 -1
  354. package/dist/tools/testSpec.js +51 -14
  355. package/dist/tools/testSpec.js.map +1 -1
  356. package/dist/tools/vector.d.ts +33 -1
  357. package/dist/tools/vector.d.ts.map +1 -1
  358. package/dist/tools/vector.js +31 -5
  359. package/dist/tools/vector.js.map +1 -1
  360. package/dist/tools/voice/index.d.ts +1 -0
  361. package/dist/tools/voice/index.d.ts.map +1 -1
  362. package/dist/tools/voice/index.js +33 -2
  363. package/dist/tools/voice/index.js.map +1 -1
  364. package/dist/tools/voice.d.ts +48 -1
  365. package/dist/tools/voice.d.ts.map +1 -1
  366. package/dist/tools/voice.js +63 -3
  367. package/dist/tools/voice.js.map +1 -1
  368. package/dist/tools/zoom/index.d.ts +15 -0
  369. package/dist/tools/zoom/index.d.ts.map +1 -0
  370. package/dist/tools/zoom/index.js +174 -0
  371. package/dist/tools/zoom/index.js.map +1 -0
  372. package/dist/tools/zoom.d.ts +234 -0
  373. package/dist/tools/zoom.d.ts.map +1 -0
  374. package/dist/tools/zoom.js +627 -0
  375. package/dist/tools/zoom.js.map +1 -0
  376. package/dist/transports/bridge.d.ts +1 -1
  377. package/dist/transports/bridge.d.ts.map +1 -1
  378. package/dist/transports/bridge.js +4 -1
  379. package/dist/transports/bridge.js.map +1 -1
  380. package/dist/transports/http.d.ts +1 -1
  381. package/dist/transports/http.d.ts.map +1 -1
  382. package/dist/transports/http.js +3 -1
  383. package/dist/transports/http.js.map +1 -1
  384. package/dist/transports/index.d.ts +1 -0
  385. package/dist/transports/index.d.ts.map +1 -1
  386. package/dist/transports/index.js +1 -0
  387. package/dist/transports/index.js.map +1 -1
  388. package/dist/transports/mcp.d.ts +1 -1
  389. package/dist/transports/mcp.d.ts.map +1 -1
  390. package/dist/transports/mcp.js +9 -4
  391. package/dist/transports/mcp.js.map +1 -1
  392. package/dist/transports/stdio.d.ts +1 -1
  393. package/dist/transports/stdio.d.ts.map +1 -1
  394. package/dist/transports/stdio.js +2 -0
  395. package/dist/transports/stdio.js.map +1 -1
  396. package/dist/transports/thinStdio.d.ts +22 -0
  397. package/dist/transports/thinStdio.d.ts.map +1 -0
  398. package/dist/transports/thinStdio.js +218 -0
  399. package/dist/transports/thinStdio.js.map +1 -0
  400. package/dist/transports/types.d.ts +8 -1
  401. package/dist/transports/types.d.ts.map +1 -1
  402. package/dist/types/agent-services.d.ts +7 -0
  403. package/dist/types/agent-services.d.ts.map +1 -1
  404. package/dist/version.d.ts +5 -0
  405. package/dist/version.d.ts.map +1 -0
  406. package/dist/version.js +23 -0
  407. package/dist/version.js.map +1 -0
  408. package/package.json +20 -4
  409. package/templates/AGENT.md +3 -0
  410. package/templates/CLAUDE-decibel.md +11 -0
  411. package/templates/com.decibel.daemon.plist +6 -1
  412. package/dist/tools/deck.d.ts +0 -11
  413. package/dist/tools/deck.d.ts.map +0 -1
  414. package/dist/tools/deck.js +0 -1656
  415. package/dist/tools/deck.js.map +0 -1
  416. package/dist/tools/mother.d.ts +0 -11
  417. package/dist/tools/mother.d.ts.map +0 -1
  418. package/dist/tools/mother.js +0 -503
  419. package/dist/tools/mother.js.map +0 -1
  420. package/dist/tools/registry.d.ts +0 -3
  421. package/dist/tools/registry.d.ts.map +0 -1
  422. package/dist/tools/registry.js +0 -189
  423. package/dist/tools/registry.js.map +0 -1
  424. package/dist/tools/senken.d.ts +0 -11
  425. package/dist/tools/senken.d.ts.map +0 -1
  426. package/dist/tools/senken.js +0 -482
  427. package/dist/tools/senken.js.map +0 -1
  428. package/dist/tools/terminal.d.ts +0 -17
  429. package/dist/tools/terminal.d.ts.map +0 -1
  430. package/dist/tools/terminal.js +0 -968
  431. package/dist/tools/terminal.js.map +0 -1
  432. package/dist/tools/toolsIndex.d.ts +0 -5
  433. package/dist/tools/toolsIndex.d.ts.map +0 -1
  434. package/dist/tools/toolsIndex.js +0 -37
  435. package/dist/tools/toolsIndex.js.map +0 -1
@@ -25,7 +25,13 @@
25
25
  * { "status": "error", "error": "...", "code": "..." }
26
26
  */
27
27
  import { createServer } from 'http';
28
- import { timingSafeEqual, randomBytes } from 'crypto';
28
+ import { randomBytes } from 'crypto';
29
+ import { isLoopbackPeer, isAllowedOrigin, presentedBearer, respondToPnaPreflight, timingSafeTokenCompare, } from './pty/boundary.js';
30
+ import { pairingOf } from './pty/pairing.js';
31
+ import { isSessionWritePath, isSessionAttachPath, handleSessionWrite, handleSessionUpgrade, closeAllAttachSockets, sessionAuthConfig, handleChatWrite, handlePair, isPairPath, applyPairingAuth, } from './pty/routes.js';
32
+ import { readChat } from './chat.js';
33
+ import { listHostedSessions, SessionError } from './pty/sessions.js';
34
+ import { fleet } from './pty/fleet.js';
29
35
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
30
36
  import { readFileSync, existsSync } from 'fs';
31
37
  import { fileURLToPath } from 'url';
@@ -33,12 +39,18 @@ import { dirname, join, resolve } from 'path';
33
39
  import { log } from './config.js';
34
40
  import { writeLocalAgentSession } from './agentPresence.js';
35
41
  import { drainCommands, setSessionToken, getSessionToken, dropSessionToken, ackOwnerOf, clearAckOwner, } from './agentInbox.js';
36
- import { settleCommandFromAck, ORG_ID } from './agentCommands.js';
42
+ import { settleCommandFromAck } from './agentCommands.js';
43
+ import { getOrgId } from './orgId.js';
37
44
  import { isSupabaseConfigured, getSupabaseServiceClient } from './lib/supabase.js';
38
45
  import { shouldQueueForAgent, parseToolCall } from './httpQueueDetection.js';
46
+ import { getExtensionDiagnostics } from './kernel.js';
39
47
  import { getLicenseValidator } from './license.js';
40
48
  import { listProjects } from './projectRegistry.js';
41
- import { setDaemonPort } from './daemon.js';
49
+ import { setDaemonPort, clearDaemonPort } from './daemon.js';
50
+ import { RUNTIME_PROTOCOL_VERSION } from './runtime/protocol.js';
51
+ import { daemonStateDir } from './runtime/daemonMeta.js';
52
+ import { PACKAGE } from './version.js';
53
+ import { wrapSuccess, wrapError, envelopeHttpStatus, } from './lib/envelope.js';
42
54
  import { listEpics, listRepoIssues, isProjectResolutionError, } from './tools/sentinel.js';
43
55
  import { voiceInboxAdd, } from './tools/voice.js';
44
56
  import { generateImage, getImageStatus, meshyGenerate, getMeshyStatus, meshyDownload, tripoGenerate, getTripoStatus, tripoDownload, klingGenerateVideo, klingGenerateTextVideo, klingGenerateAvatar, getKlingStatus, listTasks, } from './tools/studio/index.js';
@@ -82,6 +94,21 @@ class RateLimiter {
82
94
  entry.count++;
83
95
  return entry.count <= limit;
84
96
  }
97
+ /**
98
+ * A named bucket with its own limit, outside the ip:/agent: namespaces so no
99
+ * header can land a request in it.
100
+ */
101
+ checkBucket(bucket, limit) {
102
+ const key = `bucket:${bucket}`;
103
+ const now = Date.now();
104
+ const entry = this.windows.get(key);
105
+ if (!entry || now - entry.start > 60_000) {
106
+ this.windows.set(key, { count: 1, start: now });
107
+ return true;
108
+ }
109
+ entry.count++;
110
+ return entry.count <= limit;
111
+ }
85
112
  /** Set a per-agent RPM override */
86
113
  setAgentLimit(agentId, rpm) {
87
114
  this.agentLimits.set(agentId, rpm);
@@ -101,29 +128,31 @@ class RateLimiter {
101
128
  }
102
129
  }
103
130
  // ============================================================================
104
- // Security: Timing-Safe Token Comparison
131
+ // Security: shared trust boundary (src/pty/boundary.ts)
105
132
  // ============================================================================
106
- function timingSafeTokenCompare(provided, expected) {
107
- const a = Buffer.from(provided, 'utf-8');
108
- const b = Buffer.from(expected, 'utf-8');
109
- if (a.length !== b.length) {
110
- // Still do a comparison to keep timing constant
111
- timingSafeEqual(a, a);
133
+ // The timing-safe compare and the loopback check live in one module so the REST
134
+ // surface and the session/attach surface cannot drift apart.
135
+ //
136
+ // isLocalAgentPeer is the trust boundary for the /agents/* endpoints: ONLY a
137
+ // process on this machine may register/poll/ack a local runtime (the 127.0.0.1
138
+ // bind is the principal — confused-deputy rule).
139
+ const isLocalAgentPeer = isLoopbackPeer;
140
+ /** Request headers any allowed browser origin may send. */
141
+ const GENERIC_ALLOWED_HEADERS = [
142
+ 'Content-Type', 'Authorization', 'Mcp-Session-Id', 'Accept', 'X-Agent-Id', 'X-Run-Id',
143
+ 'X-License-Key', 'X-Allowed-Facades', 'X-Scope', 'X-Request-Id', 'X-Parent-Call-Id',
144
+ 'X-Engagement-Mode', 'X-User-Key', 'X-Org-Key',
145
+ 'X-Pairing-Id', 'X-Pairing-Nonce', 'X-Pairing-Iat', 'X-Pairing-Sig',
146
+ ];
147
+ /** The request carries the daemon auth_token or the sessions token as its bearer. */
148
+ function holdsLocalBearer(req, daemonToken) {
149
+ const presented = presentedBearer(req);
150
+ if (!presented)
112
151
  return false;
113
- }
114
- return timingSafeEqual(a, b);
115
- }
116
- /**
117
- * The connection's trust boundary for the /agents/* endpoints: ONLY a process on
118
- * this machine may register/poll/ack a local runtime (the 127.0.0.1 bind is the
119
- * principal — confused-deputy rule). We check the real socket peer address (not a
120
- * spoofable header). An EMPTY remoteAddress is rejected (was previously allowed) —
121
- * a genuine TCP loopback connection always presents 127.0.0.1/::1, so empty only
122
- * arises from a destroyed/odd socket and must not be treated as local.
123
- */
124
- function isLocalAgentPeer(req) {
125
- const peer = (req.socket.remoteAddress || '').replace('::ffff:', '');
126
- return peer === '127.0.0.1' || peer === '::1';
152
+ if (daemonToken && timingSafeTokenCompare(presented, daemonToken))
153
+ return true;
154
+ const sessionsToken = sessionAuthConfig().token;
155
+ return !!sessionsToken && timingSafeTokenCompare(presented, sessionsToken);
127
156
  }
128
157
  /**
129
158
  * Mint-or-return the per-session capability token (proof-of-possession the SDK must
@@ -158,18 +187,7 @@ function isLocalhostOrigin(origin) {
158
187
  // ============================================================================
159
188
  const __filename = fileURLToPath(import.meta.url);
160
189
  const __dirname = dirname(__filename);
161
- function getVersion() {
162
- try {
163
- // Try to read from package.json (works in both dev and prod)
164
- const pkgPath = join(__dirname, '..', 'package.json');
165
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
166
- return { version: pkg.version || '0.0.0', name: pkg.name || '@decibelsystems/tools' };
167
- }
168
- catch {
169
- return { version: '2.0.0', name: '@decibelsystems/tools' };
170
- }
171
- }
172
- const PKG = getVersion();
190
+ const PKG = PACKAGE;
173
191
  // ============================================================================
174
192
  // Landing Page HTML
175
193
  // ============================================================================
@@ -208,43 +226,6 @@ function formatUptime(ms) {
208
226
  return `${minutes}m ${seconds % 60}s`;
209
227
  return `${seconds}s`;
210
228
  }
211
- /**
212
- * Wrap a successful result in status envelope
213
- */
214
- function wrapSuccess(data) {
215
- return { status: 'executed', ...data };
216
- }
217
- /**
218
- * Wrap an error in status envelope
219
- */
220
- /**
221
- * Sanitize error messages to prevent information disclosure.
222
- * Removes absolute file paths and replaces with generic placeholders.
223
- * Preserves URLs and relative paths.
224
- */
225
- function sanitizeErrorMessage(message) {
226
- let sanitized = message;
227
- // Step 1: Preserve paths containing .decibel (keep the .decibel part, strip prefix)
228
- // /home/user/project/.decibel/foo -> .decibel/foo
229
- sanitized = sanitized.replace(/(?:\/[^\s:'"]+)?\/\.decibel\//g, '.decibel/');
230
- sanitized = sanitized.replace(/(?:[A-Z]:\\[^\s:'"]+)?\\\.decibel\\/gi, '.decibel\\');
231
- // Step 2: Remove absolute Unix paths (must start with / and have at least one more /)
232
- // But exclude URLs (http://, https://, file://)
233
- // Match: /home/user/file.txt, /var/log/app.log
234
- // Don't match: http://example.com, ./relative/path
235
- sanitized = sanitized.replace(/(?<!:)\/(?:home|Users|var|tmp|opt|usr|etc|media|mnt)\/[^\s:'"]+/g, '[path]');
236
- // Step 3: Remove Windows absolute paths (C:\, D:\, etc.)
237
- sanitized = sanitized.replace(/[A-Z]:\\[^\s:'"]+/gi, '[path]');
238
- // Step 4: Sanitize any remaining usernames in paths that weren't caught
239
- sanitized = sanitized.replace(/\/home\/[^\/\s]+\//g, '/home/[user]/');
240
- sanitized = sanitized.replace(/\/Users\/[^\/\s]+\//g, '/Users/[user]/');
241
- sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/gi, 'C:\\Users\\[user]\\');
242
- return sanitized;
243
- }
244
- function wrapError(error, code) {
245
- const sanitized = sanitizeErrorMessage(error);
246
- return { status: 'error', error: sanitized, ...(code && { code }) };
247
- }
248
229
  /**
249
230
  * Send JSON response with status envelope
250
231
  */
@@ -346,20 +327,30 @@ async function executeTool(tool, args, req, tierOverride) {
346
327
  requestId: req.headers['x-request-id'],
347
328
  tier: tierOverride,
348
329
  allowedFacades,
349
- } : tierOverride ? { tier: tierOverride } : undefined;
330
+ transport: 'http',
331
+ } : tierOverride ? { tier: tierOverride, transport: 'http' } : { transport: 'http' };
350
332
  const toolResult = await kernel.dispatch(tool, args, context);
351
333
  const text = toolResult.content[0]?.text;
352
334
  if (toolResult.isError) {
353
335
  return wrapError(text || 'Tool execution failed', 'TOOL_ERROR');
354
336
  }
355
- // Parse JSON result or wrap as message
337
+ // Parse the tool's payload, or carry it as prose when it isn't JSON.
338
+ //
339
+ // The prose branch is legitimate — some tools return markdown — but it used
340
+ // to be indistinguishable from a data response that failed to parse. A
341
+ // caller expecting `events` got an object with no `events` key, `ok: true`,
342
+ // and no indication that anything unusual happened. That is how a
343
+ // concatenated feedback prompt (fixed in tools/shared/response.ts) turned
344
+ // one call in fifteen into a silent empty result for every HTTP consumer.
345
+ // `payload_format` makes the two cases tellable apart.
356
346
  let result;
357
347
  if (text) {
358
348
  try {
359
349
  result = JSON.parse(text);
360
350
  }
361
351
  catch {
362
- result = { message: text };
352
+ log(`HTTP: tool ${tool} returned non-JSON text (${text.length} bytes) — carrying it as prose`);
353
+ result = { message: text, payload_format: 'text' };
363
354
  }
364
355
  }
365
356
  else {
@@ -406,6 +397,7 @@ async function queueForAgent(facade, action, args, agentId, projectId) {
406
397
  }
407
398
  return {
408
399
  status: 'queued',
400
+ ok: true,
409
401
  queue_id: data.id,
410
402
  message: 'Queued for local sync. Use agentic queue_status to check result.',
411
403
  };
@@ -424,7 +416,7 @@ function getAvailableTools() {
424
416
  * Get tools in OpenAI function calling format (facade-based)
425
417
  */
426
418
  function getOpenAITools() {
427
- return kernel.getMcpToolDefinitions('full').map(def => ({
419
+ return kernel.getMcpToolDefinitions('full', { transport: 'http' }).map(def => ({
428
420
  type: 'function',
429
421
  function: {
430
422
  name: def.name,
@@ -563,14 +555,31 @@ export async function startHttpServer(server, kernelInstance, options) {
563
555
  // else: untrusted origin → no ACAO header (browser blocks the read).
564
556
  }
565
557
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
566
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Mcp-Session-Id, Accept, X-Agent-Id, X-Run-Id, X-License-Key, X-Allowed-Facades, X-Scope, X-Request-Id, X-Parent-Call-Id, X-Engagement-Mode, X-User-Key, X-Org-Key');
558
+ res.setHeader('Access-Control-Allow-Headers', GENERIC_ALLOWED_HEADERS.join(', '));
567
559
  res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id');
560
+ // Hosted-session write routes answer their own preflight: they carry the
561
+ // Private Network Access header and a stricter origin allowlist than the
562
+ // generic localhost CORS above (src/pty/routes.ts).
563
+ // A preflight from an allowlisted HQ origin (the dev server, or deployed HQ
564
+ // for paired requests) gets the Private Network Access header and the
565
+ // pairing headers; without PNA an https page cannot reach 127.0.0.1 at all.
566
+ if (isDaemon && req.method === 'OPTIONS' && isAllowedOrigin(req.headers.origin, sessionAuthConfig().allowedOrigins)) {
567
+ respondToPnaPreflight(req, res, sessionAuthConfig().allowedOrigins, GENERIC_ALLOWED_HEADERS);
568
+ return;
569
+ }
568
570
  // (a) Handle preflight OPTIONS requests
569
571
  if (req.method === 'OPTIONS') {
570
572
  res.writeHead(204);
571
573
  res.end();
572
574
  return;
573
575
  }
576
+ // Signed requests from a paired browser (ADR-0012 in this repo). Verified
577
+ // here, once, before rate limiting and auth: a verified request is marked
578
+ // (pairingOf) and stands in for a bearer everywhere below. A request that
579
+ // CLAIMS a pairing and fails is refused outright rather than falling
580
+ // through as anonymous.
581
+ if (isDaemon && !applyPairingAuth(req, res, url))
582
+ return;
574
583
  // HOSTED FAIL-CLOSED, checked EARLY (before the rate limiter) so an
575
584
  // unauthenticated hosted caller can't consume/poison the rate-limit map or any
576
585
  // downstream state before being refused. Public infra routes stay open so
@@ -587,9 +596,20 @@ export async function startHttpServer(server, kernelInstance, options) {
587
596
  return;
588
597
  }
589
598
  // Rate limiting (check before auth to prevent brute force)
599
+ //
600
+ // Every local caller is 127.0.0.1, so by IP they all share one 100-rpm
601
+ // bucket: an HQ tab refreshing its roster used to spend it for the whole
602
+ // machine, and then attach-token and every hook call got RATE_LIMITED.
603
+ // A loopback caller that proves it holds the daemon bearer or the sessions
604
+ // bearer (HQ's dev proxy injects one server-side) gets its own, larger
605
+ // bucket. Anonymous loopback keeps the shared one.
590
606
  const clientIp = (req.socket.remoteAddress || '127.0.0.1').replace('::ffff:', '');
591
607
  const reqAgentId = req.headers['x-agent-id'];
592
- if (!rateLimiter.check(clientIp, reqAgentId)) {
608
+ const trustedLocal = isDaemon && isLoopbackPeer(req) && (!!pairingOf(req) || holdsLocalBearer(req, authToken));
609
+ const withinLimit = trustedLocal
610
+ ? rateLimiter.checkBucket('trusted-local', Math.max(rateLimitRpm * 20, 2000))
611
+ : rateLimiter.check(clientIp, reqAgentId);
612
+ if (!withinLimit) {
593
613
  log(`HTTP: Rate limited ${reqAgentId || clientIp}`);
594
614
  sendJson(res, 429, wrapError('Too many requests (rate limit exceeded)', 'RATE_LIMITED'));
595
615
  return;
@@ -602,6 +622,25 @@ export async function startHttpServer(server, kernelInstance, options) {
602
622
  activeRequests.add(req);
603
623
  res.on('finish', () => activeRequests.delete(req));
604
624
  res.on('close', () => activeRequests.delete(req));
625
+ // Hosted-session writes (attach-token, control, spawn) are gated on the
626
+ // sessions bearer, not the daemon auth_token, so they are routed BEFORE the
627
+ // daemon's own auth gate. See src/pty/routes.ts for why. Daemon mode only:
628
+ // a hosted --http server has no business starting local terminals.
629
+ if (isDaemon && isSessionWritePath(path)) {
630
+ await handleSessionWrite(req, res, path);
631
+ return;
632
+ }
633
+ // Pairing: request, status, challenge, revoke. Pre-auth by nature; none of
634
+ // them approves anything (approval is the CLI, on this machine).
635
+ if (isDaemon && isPairPath(path)) {
636
+ await handlePair(req, res, path);
637
+ return;
638
+ }
639
+ // POST /chat puts text into other sessions: same gate as the session writes.
640
+ if (isDaemon && path === '/chat' && req.method === 'POST') {
641
+ await handleChatWrite(req, res);
642
+ return;
643
+ }
605
644
  // (c) Root health check - GET / returns 200
606
645
  if (path === '/' && req.method === 'GET') {
607
646
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -630,6 +669,10 @@ export async function startHttpServer(server, kernelInstance, options) {
630
669
  status: 'ok',
631
670
  version: PKG.version,
632
671
  api_version: 'v1',
672
+ // Wire-contract version, negotiated by ensureRuntime(). Distinct from
673
+ // `version` — a long-lived daemon can outlive the clients connecting to
674
+ // it, and most releases do not change the contract. See runtime/protocol.ts.
675
+ protocol_version: RUNTIME_PROTOCOL_VERSION,
633
676
  uptime_ms: uptimeMs,
634
677
  uptime_human: formatUptime(uptimeMs),
635
678
  pid: process.pid,
@@ -642,6 +685,17 @@ export async function startHttpServer(server, kernelInstance, options) {
642
685
  pro: licenseTier !== 'core',
643
686
  license_tier: licenseTier,
644
687
  supabase_configured: isSupabaseConfigured(),
688
+ // Facades whose dependency is failing. `{}` is the healthy case — a
689
+ // non-empty object means calls to those facades are being refused fast
690
+ // rather than left to time out. See runtime/circuitBreaker.ts.
691
+ circuits: kernel.circuitSnapshot(),
692
+ // Private facades loaded from the allowlist, and every entry refused.
693
+ // Reported because the alternative is silence: an extension that failed
694
+ // to load looks exactly like one that was never configured, and this
695
+ // daemon has already shipped one bug of that shape — a broken voice
696
+ // inbox that read as "0 messages" for five hours. See
697
+ // runtime/extensions.ts.
698
+ extensions: getExtensionDiagnostics(),
645
699
  }));
646
700
  return;
647
701
  }
@@ -667,14 +721,14 @@ export async function startHttpServer(server, kernelInstance, options) {
667
721
  sendJson(res, 401, wrapError('This endpoint requires authentication. Hosted (--http) mode must be started with DECIBEL_AUTH_TOKEN set.', 'AUTH_NOT_CONFIGURED'));
668
722
  return;
669
723
  }
670
- if (authToken) {
724
+ if (authToken && !pairingOf(req)) {
671
725
  const authHeader = req.headers.authorization;
672
726
  if (!authHeader || !timingSafeTokenCompare(authHeader, `Bearer ${authToken}`)) {
673
727
  sendJson(res, 401, wrapError('Unauthorized', 'UNAUTHORIZED'));
674
728
  return;
675
729
  }
676
730
  }
677
- const dispatchLogPath = join(process.env.HOME || '~', '.decibel', 'logs', 'dispatch.jsonl');
731
+ const dispatchLogPath = join(daemonStateDir(), 'logs', 'dispatch.jsonl');
678
732
  try {
679
733
  const content = readFileSync(dispatchLogPath, 'utf-8');
680
734
  const lines = content.trim().split('\n').filter(Boolean);
@@ -772,8 +826,9 @@ export async function startHttpServer(server, kernelInstance, options) {
772
826
  sendJson(res, 401, wrapError('This endpoint requires authentication. Hosted (--http) mode must be started with DECIBEL_AUTH_TOKEN set.', 'AUTH_NOT_CONFIGURED'));
773
827
  return;
774
828
  }
775
- // Auth check (timing-safe comparison to prevent timing attacks)
776
- if (authToken) {
829
+ // Auth check (timing-safe comparison to prevent timing attacks). A request
830
+ // verified under an approved pairing already proved more than a bearer.
831
+ if (authToken && !pairingOf(req)) {
777
832
  const authHeader = req.headers.authorization;
778
833
  if (!authHeader || !timingSafeTokenCompare(authHeader, `Bearer ${authToken}`)) {
779
834
  log('HTTP: Unauthorized request');
@@ -784,7 +839,62 @@ export async function startHttpServer(server, kernelInstance, options) {
784
839
  // ========================================================================
785
840
  // Simple REST Endpoints (for external AI agents)
786
841
  // ========================================================================
842
+ // GET /sessions[?project_id=<id>] — hosted-session roster for HQ (ADR-0012 /
843
+ // decibel-hq EPIC-0015). Without project_id, every session on the machine.
844
+ // Read-only, so it sits behind the normal daemon gate; the write routes are
845
+ // handled above with their own bearer.
846
+ if (isDaemon && path === '/sessions' && req.method === 'GET') {
847
+ const projectId = url.searchParams.get('project_id') || undefined;
848
+ try {
849
+ const sessions = await listHostedSessions(projectId);
850
+ res.writeHead(200, { 'Content-Type': 'application/json' });
851
+ res.end(JSON.stringify({ sessions }));
852
+ }
853
+ catch (err) {
854
+ if (err instanceof SessionError) {
855
+ sendJson(res, err.status, wrapError(err.message, err.code));
856
+ }
857
+ else {
858
+ sendJson(res, 500, wrapError(err instanceof Error ? err.message : String(err), 'INTERNAL'));
859
+ }
860
+ }
861
+ return;
862
+ }
863
+ // GET /sessions/last-fleet — the sessions to offer for restore (decibel-hq
864
+ // ADR-0018). Read-only, behind the normal daemon gate like GET /sessions.
865
+ if (isDaemon && path === '/sessions/last-fleet' && req.method === 'GET') {
866
+ res.writeHead(200, { 'Content-Type': 'application/json' });
867
+ res.end(JSON.stringify(fleet().lastFleet()));
868
+ return;
869
+ }
870
+ // GET /chat?since=<id>&limit= — the HQ chat log (decibel-hq EPIC-0016).
871
+ // Read-only, behind the normal daemon gate. human_peer_id changes whenever
872
+ // the peer re-registers, so clients read it from here every time.
873
+ if (isDaemon && path === '/chat' && req.method === 'GET') {
874
+ const since = Number(url.searchParams.get('since') ?? 0) || 0;
875
+ const limit = Number(url.searchParams.get('limit') ?? 200) || 200;
876
+ res.writeHead(200, { 'Content-Type': 'application/json' });
877
+ res.end(JSON.stringify(readChat(since, limit)));
878
+ return;
879
+ }
787
880
  // GET /tools - List available tools
881
+ // MCP tool definitions, verbatim — the shape a `tools/list` response needs.
882
+ //
883
+ // This is what lets a stdio client stop building its own kernel. /tools
884
+ // above is a human/OpenAI-shaped summary; this one is the exact array the
885
+ // MCP handler returns, so a thin adapter can serve tools/list by forwarding
886
+ // it rather than loading 195 tool modules to produce the same bytes.
887
+ if (path === '/mcp/tools' && req.method === 'GET') {
888
+ const tierParam = url.searchParams.get('tier');
889
+ const tier = (tierParam === 'compact' || tierParam === 'micro' ? tierParam : 'full');
890
+ sendJson(res, 200, wrapSuccess({
891
+ version: PKG.version,
892
+ protocol_version: RUNTIME_PROTOCOL_VERSION,
893
+ tier,
894
+ tools: kernel.getMcpToolDefinitions(tier, { transport: 'http' }),
895
+ }));
896
+ return;
897
+ }
788
898
  if (path === '/tools' && req.method === 'GET') {
789
899
  sendJson(res, 200, wrapSuccess({
790
900
  version: PKG.version,
@@ -944,7 +1054,13 @@ export async function startHttpServer(server, kernelInstance, options) {
944
1054
  sendJson(res, 403, wrapError('Invalid or missing session token', 'BAD_SESSION_TOKEN'));
945
1055
  return;
946
1056
  }
947
- const commands = drainCommands(sessionKey, ORG_ID);
1057
+ // No org means no dispatcher ran, so nothing can be queued for this session.
1058
+ let org = null;
1059
+ try {
1060
+ org = getOrgId();
1061
+ }
1062
+ catch { /* malformed: the startup gate reports it */ }
1063
+ const commands = org ? drainCommands(sessionKey, org) : [];
948
1064
  sendJson(res, 200, wrapSuccess({ session_key: sessionKey, commands, count: commands.length }));
949
1065
  return;
950
1066
  }
@@ -1151,7 +1267,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1151
1267
  const tier = await resolveTier(req, configLicenseKey);
1152
1268
  log(`HTTP: /call tool=${tool} tier=${tier}`);
1153
1269
  const result = await executeTool(tool, args, req, tier);
1154
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1270
+ sendJson(res, envelopeHttpStatus(result), result);
1155
1271
  }
1156
1272
  catch (error) {
1157
1273
  const message = error instanceof Error ? error.message : String(error);
@@ -1191,10 +1307,30 @@ export async function startHttpServer(server, kernelInstance, options) {
1191
1307
  requestId: req.headers['x-request-id'] || bodyContext.requestId,
1192
1308
  allowedFacades: bodyContext.allowedFacades,
1193
1309
  tier,
1310
+ // Set here, never read from the request: a caller that could name its
1311
+ // own transport could name 'stdio' and walk straight through the
1312
+ // local-only gate.
1313
+ transport: 'http',
1194
1314
  };
1195
1315
  log(`HTTP: /batch — ${calls.length} calls (agent=${context.agentId || 'anonymous'}, tier=${tier})`);
1196
1316
  const results = await kernel.batch(calls, context);
1197
- sendJson(res, 200, { status: 'executed', results });
1317
+ // `ok` reflects STRUCTURAL validity: did every call name something this
1318
+ // runtime actually has. A call that ran and returned isError leaves ok
1319
+ // true — partial failure is a normal batch outcome and callers depend on
1320
+ // that. A call naming a facade that is not registered does not.
1321
+ //
1322
+ // The status stays 200 because the body still carries real results for
1323
+ // the other calls in the batch, and throwing those away behind a 4xx
1324
+ // would trade one silent failure for another.
1325
+ const unknown = results.filter(r => r.code === 'UNKNOWN_FACADE');
1326
+ if (unknown.length > 0) {
1327
+ log(`HTTP: /batch — ${unknown.length} unknown facade(s): ${unknown.map(r => r.facade).join(', ')}`);
1328
+ }
1329
+ sendJson(res, 200, {
1330
+ status: unknown.length > 0 ? 'error' : 'executed',
1331
+ ok: unknown.length === 0,
1332
+ results,
1333
+ });
1198
1334
  }
1199
1335
  catch (error) {
1200
1336
  const message = error instanceof Error ? error.message : String(error);
@@ -1223,7 +1359,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1223
1359
  const body = await parseBody(req);
1224
1360
  log(`HTTP: /api/tools/${toolName}`);
1225
1361
  const result = await executeTool(toolName, body);
1226
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1362
+ sendJson(res, envelopeHttpStatus(result), result);
1227
1363
  }
1228
1364
  catch (error) {
1229
1365
  const message = error instanceof Error ? error.message : String(error);
@@ -1240,7 +1376,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1240
1376
  const body = await parseBody(req);
1241
1377
  log('HTTP: /dojo/wish');
1242
1378
  const result = await executeTool('dojo_add_wish', body);
1243
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1379
+ sendJson(res, envelopeHttpStatus(result), result);
1244
1380
  }
1245
1381
  catch (error) {
1246
1382
  const message = error instanceof Error ? error.message : String(error);
@@ -1254,7 +1390,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1254
1390
  const body = await parseBody(req);
1255
1391
  log('HTTP: /dojo/propose');
1256
1392
  const result = await executeTool('dojo_create_proposal', body);
1257
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1393
+ sendJson(res, envelopeHttpStatus(result), result);
1258
1394
  }
1259
1395
  catch (error) {
1260
1396
  const message = error instanceof Error ? error.message : String(error);
@@ -1268,7 +1404,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1268
1404
  const body = await parseBody(req);
1269
1405
  log('HTTP: /dojo/scaffold');
1270
1406
  const result = await executeTool('dojo_scaffold_experiment', body);
1271
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1407
+ sendJson(res, envelopeHttpStatus(result), result);
1272
1408
  }
1273
1409
  catch (error) {
1274
1410
  const message = error instanceof Error ? error.message : String(error);
@@ -1282,7 +1418,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1282
1418
  const body = await parseBody(req);
1283
1419
  log('HTTP: /dojo/run');
1284
1420
  const result = await executeTool('dojo_run_experiment', body);
1285
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1421
+ sendJson(res, envelopeHttpStatus(result), result);
1286
1422
  }
1287
1423
  catch (error) {
1288
1424
  const message = error instanceof Error ? error.message : String(error);
@@ -1296,7 +1432,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1296
1432
  const body = await parseBody(req);
1297
1433
  log('HTTP: /dojo/results');
1298
1434
  const result = await executeTool('dojo_read_results', body);
1299
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1435
+ sendJson(res, envelopeHttpStatus(result), result);
1300
1436
  }
1301
1437
  catch (error) {
1302
1438
  const message = error instanceof Error ? error.message : String(error);
@@ -1317,7 +1453,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1317
1453
  }
1318
1454
  log('HTTP: /dojo/list');
1319
1455
  const result = await executeTool('dojo_list', body);
1320
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1456
+ sendJson(res, envelopeHttpStatus(result), result);
1321
1457
  }
1322
1458
  catch (error) {
1323
1459
  const message = error instanceof Error ? error.message : String(error);
@@ -1337,7 +1473,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1337
1473
  }
1338
1474
  log('HTTP: /dojo/wishes');
1339
1475
  const result = await executeTool('dojo_list_wishes', body);
1340
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1476
+ sendJson(res, envelopeHttpStatus(result), result);
1341
1477
  }
1342
1478
  catch (error) {
1343
1479
  const message = error instanceof Error ? error.message : String(error);
@@ -1351,7 +1487,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1351
1487
  const body = await parseBody(req);
1352
1488
  log('HTTP: /dojo/can-graduate');
1353
1489
  const result = await executeTool('dojo_can_graduate', body);
1354
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1490
+ sendJson(res, envelopeHttpStatus(result), result);
1355
1491
  }
1356
1492
  catch (error) {
1357
1493
  const message = error instanceof Error ? error.message : String(error);
@@ -1365,7 +1501,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1365
1501
  const body = await parseBody(req);
1366
1502
  log('HTTP: /dojo/artifact');
1367
1503
  const result = await executeTool('dojo_read_artifact', body);
1368
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1504
+ sendJson(res, envelopeHttpStatus(result), result);
1369
1505
  }
1370
1506
  catch (error) {
1371
1507
  const message = error instanceof Error ? error.message : String(error);
@@ -1379,7 +1515,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1379
1515
  const body = await parseBody(req);
1380
1516
  log('HTTP: /dojo/bench');
1381
1517
  const result = await executeTool('dojo_bench', body);
1382
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1518
+ sendJson(res, envelopeHttpStatus(result), result);
1383
1519
  }
1384
1520
  catch (error) {
1385
1521
  const message = error instanceof Error ? error.message : String(error);
@@ -1396,7 +1532,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1396
1532
  const body = await parseBody(req);
1397
1533
  log('HTTP: /bench/run');
1398
1534
  const result = await executeTool('decibel_bench', body);
1399
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1535
+ sendJson(res, envelopeHttpStatus(result), result);
1400
1536
  }
1401
1537
  catch (error) {
1402
1538
  const message = error instanceof Error ? error.message : String(error);
@@ -1410,7 +1546,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1410
1546
  const body = await parseBody(req);
1411
1547
  log('HTTP: /bench/compare');
1412
1548
  const result = await executeTool('decibel_bench_compare', body);
1413
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1549
+ sendJson(res, envelopeHttpStatus(result), result);
1414
1550
  }
1415
1551
  catch (error) {
1416
1552
  const message = error instanceof Error ? error.message : String(error);
@@ -1427,7 +1563,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1427
1563
  const body = await parseBody(req);
1428
1564
  log('HTTP: /context/refresh');
1429
1565
  const result = await executeTool('decibel_context_refresh', body);
1430
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1566
+ sendJson(res, envelopeHttpStatus(result), result);
1431
1567
  }
1432
1568
  catch (error) {
1433
1569
  const message = error instanceof Error ? error.message : String(error);
@@ -1441,7 +1577,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1441
1577
  const body = await parseBody(req);
1442
1578
  log('HTTP: /context/pin');
1443
1579
  const result = await executeTool('decibel_context_pin', body);
1444
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1580
+ sendJson(res, envelopeHttpStatus(result), result);
1445
1581
  }
1446
1582
  catch (error) {
1447
1583
  const message = error instanceof Error ? error.message : String(error);
@@ -1455,7 +1591,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1455
1591
  const body = await parseBody(req);
1456
1592
  log('HTTP: /context/unpin');
1457
1593
  const result = await executeTool('decibel_context_unpin', body);
1458
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1594
+ sendJson(res, envelopeHttpStatus(result), result);
1459
1595
  }
1460
1596
  catch (error) {
1461
1597
  const message = error instanceof Error ? error.message : String(error);
@@ -1475,7 +1611,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1475
1611
  }
1476
1612
  log('HTTP: /context/list');
1477
1613
  const result = await executeTool('decibel_context_list', body);
1478
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1614
+ sendJson(res, envelopeHttpStatus(result), result);
1479
1615
  }
1480
1616
  catch (error) {
1481
1617
  const message = error instanceof Error ? error.message : String(error);
@@ -1489,7 +1625,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1489
1625
  const body = await parseBody(req);
1490
1626
  log('HTTP: /event/append');
1491
1627
  const result = await executeTool('decibel_event_append', body);
1492
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1628
+ sendJson(res, envelopeHttpStatus(result), result);
1493
1629
  }
1494
1630
  catch (error) {
1495
1631
  const message = error instanceof Error ? error.message : String(error);
@@ -1517,7 +1653,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1517
1653
  }
1518
1654
  log('HTTP: /event/search');
1519
1655
  const result = await executeTool('decibel_event_search', body);
1520
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1656
+ sendJson(res, envelopeHttpStatus(result), result);
1521
1657
  }
1522
1658
  catch (error) {
1523
1659
  const message = error instanceof Error ? error.message : String(error);
@@ -1531,7 +1667,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1531
1667
  const body = await parseBody(req);
1532
1668
  log('HTTP: /artifact/list');
1533
1669
  const result = await executeTool('decibel_artifact_list', body);
1534
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1670
+ sendJson(res, envelopeHttpStatus(result), result);
1535
1671
  }
1536
1672
  catch (error) {
1537
1673
  const message = error instanceof Error ? error.message : String(error);
@@ -1545,7 +1681,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1545
1681
  const body = await parseBody(req);
1546
1682
  log('HTTP: /artifact/read');
1547
1683
  const result = await executeTool('decibel_artifact_read', body);
1548
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1684
+ sendJson(res, envelopeHttpStatus(result), result);
1549
1685
  }
1550
1686
  catch (error) {
1551
1687
  const message = error instanceof Error ? error.message : String(error);
@@ -1769,6 +1905,16 @@ export async function startHttpServer(server, kernelInstance, options) {
1769
1905
  // ========================================================================
1770
1906
  // Studio API Endpoints (frontend_v0.2 compatible)
1771
1907
  // ========================================================================
1908
+ // Tier guard: these REST shortcuts call studio (pro-tier) tools directly,
1909
+ // bypassing kernel dispatch where tier gating normally lives. Enforce it
1910
+ // here once for all /api/generate-* routes. (crucible re-run 2026-06-07)
1911
+ if (path.startsWith('/api/generate-') && req.method === 'POST') {
1912
+ const tier = await resolveTier(req, configLicenseKey);
1913
+ if (tier === 'core') {
1914
+ sendJson(res, 403, wrapError('Studio generation requires a pro license', 'TIER_REQUIRED'));
1915
+ return;
1916
+ }
1917
+ }
1772
1918
  // POST /api/generate-flux-kontext-image - Start image generation
1773
1919
  if (path === '/api/generate-flux-kontext-image' && req.method === 'POST') {
1774
1920
  try {
@@ -2122,17 +2268,46 @@ export async function startHttpServer(server, kernelInstance, options) {
2122
2268
  res.writeHead(404, { 'Content-Type': 'application/json' });
2123
2269
  res.end(JSON.stringify({ error: 'Not found' }));
2124
2270
  });
2271
+ // WebSocket upgrades. The only upgrade this server accepts is a hosted-session
2272
+ // attach (daemon mode); everything else is refused rather than left hanging,
2273
+ // since Node leaves an unhandled upgrade socket open until it times out.
2274
+ httpServer.on('upgrade', (req, socket, head) => {
2275
+ const upgradeUrl = new URL(req.url || '/', 'http://127.0.0.1');
2276
+ if (isDaemon && isSessionAttachPath(upgradeUrl.pathname)) {
2277
+ handleSessionUpgrade(req, socket, head, upgradeUrl).catch((err) => {
2278
+ log(`HTTP: attach upgrade failed: ${err instanceof Error ? err.message : String(err)}`);
2279
+ try {
2280
+ socket.destroy();
2281
+ }
2282
+ catch { /* ignore */ }
2283
+ });
2284
+ return;
2285
+ }
2286
+ socket.write('HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n');
2287
+ socket.destroy();
2288
+ });
2125
2289
  // Configure HTTP server timeouts to prevent premature connection drops
2126
2290
  httpServer.keepAliveTimeout = timeoutMs;
2127
2291
  httpServer.headersTimeout = timeoutMs + 1000; // Slightly longer than keepAliveTimeout
2128
2292
  httpServer.listen(port, host, () => {
2129
2293
  log(`HTTP Server listening on http://${host}:${port}`);
2130
- // Advertise port + pid in daemon.meta so clients can discover the daemon.
2131
- try {
2132
- setDaemonPort(port);
2294
+ // Advertise port + pid in daemon.meta so clients can discover the daemon —
2295
+ // but ONLY as the daemon. daemon.meta is machine-global: the session-init
2296
+ // hook, the close hook, the HQ client and ensureRuntime all follow it. A
2297
+ // plain `--http` run is a process someone started for a reason of their own
2298
+ // (a test, a second checkout, CI, the hosted deployment), and when it
2299
+ // advertised, every hook on the machine followed it to a port that died
2300
+ // with it (ISS-0179).
2301
+ if (isDaemon) {
2302
+ try {
2303
+ setDaemonPort(port);
2304
+ }
2305
+ catch (err) {
2306
+ log(`Daemon: failed to write port to meta: ${err instanceof Error ? err.message : String(err)}`);
2307
+ }
2133
2308
  }
2134
- catch (err) {
2135
- log(`Daemon: failed to write port to meta: ${err instanceof Error ? err.message : String(err)}`);
2309
+ else {
2310
+ log(`HTTP Server: not advertising in daemon.meta (not --daemon); clients keep their existing discovery`);
2136
2311
  }
2137
2312
  console.log(`
2138
2313
  ╔══════════════════════════════════════════════════════════════╗
@@ -2183,6 +2358,8 @@ ${authToken ? '║ Auth: Bearer token required
2183
2358
  log(`HTTP: ${activeRequests.size} request(s) still active after 10s drain timeout`);
2184
2359
  }
2185
2360
  }
2361
+ // Close attach sockets (their tmux sessions live on)
2362
+ closeAllAttachSockets();
2186
2363
  // Close SSE connections
2187
2364
  for (const conn of activeSseConnections) {
2188
2365
  try {
@@ -2199,40 +2376,17 @@ ${authToken ? '║ Auth: Bearer token required
2199
2376
  // If already closed, resolve immediately
2200
2377
  setTimeout(resolve, 100);
2201
2378
  });
2379
+ // Withdraw discovery if we owned it, so a clean shutdown does not leave
2380
+ // every client on the machine dialling a port nothing is serving
2381
+ // (ISS-0179). clearDaemonPort only clears an entry that is ours.
2382
+ if (isDaemon)
2383
+ clearDaemonPort();
2202
2384
  log('HTTP server stopped');
2203
2385
  },
2204
2386
  };
2205
2387
  }
2206
- /**
2207
- * Parse command line arguments for HTTP mode
2208
- */
2209
- export function parseHttpArgs(args) {
2210
- const httpMode = args.includes('--http');
2211
- const portIndex = args.indexOf('--port');
2212
- // --port flag wins; else honor PORT env (Render sets it); else leave undefined
2213
- // so daemonConfig's default (4888) applies downstream instead of being
2214
- // short-circuited by a baked-in default here. See server.ts port/host resolution.
2215
- const port = portIndex !== -1
2216
- ? parseInt(args[portIndex + 1], 10)
2217
- : process.env.PORT
2218
- ? parseInt(process.env.PORT, 10)
2219
- : undefined;
2220
- // SECURITY: Prefer env var for auth token (CLI args visible in ps/history)
2221
- // Fall back to --auth-token for backwards compatibility
2222
- const authIndex = args.indexOf('--auth-token');
2223
- const authToken = process.env.DECIBEL_AUTH_TOKEN ||
2224
- (authIndex !== -1 ? args[authIndex + 1] : undefined);
2225
- const hostIndex = args.indexOf('--host');
2226
- // --host flag wins; else leave undefined so daemonConfig host (127.0.0.1, daemon
2227
- // mode) or the transport default applies instead of being short-circuited here.
2228
- const host = hostIndex !== -1 ? args[hostIndex + 1] : undefined;
2229
- // SSE/Connection tuning arguments
2230
- const keepaliveIndex = args.indexOf('--sse-keepalive');
2231
- const sseKeepaliveMs = keepaliveIndex !== -1 ? parseInt(args[keepaliveIndex + 1], 10) : undefined;
2232
- const timeoutIndex = args.indexOf('--timeout');
2233
- const timeoutMs = timeoutIndex !== -1 ? parseInt(args[timeoutIndex + 1], 10) : undefined;
2234
- const retryIndex = args.indexOf('--sse-retry');
2235
- const retryIntervalMs = retryIndex !== -1 ? parseInt(args[retryIndex + 1], 10) : undefined;
2236
- return { httpMode, port, authToken, host, sseKeepaliveMs, timeoutMs, retryIntervalMs };
2237
- }
2388
+ // parseHttpArgs moved to ./httpArgs.js so that argv parsing carries no
2389
+ // dependencies. Re-exported here because it has always been part of this
2390
+ // module's surface.
2391
+ export { parseHttpArgs } from './httpArgs.js';
2238
2392
  //# sourceMappingURL=httpServer.js.map