@decibelsystems/tools 2.1.4 → 3.0.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 (295) hide show
  1. package/CHANGELOG.md +160 -0
  2. package/README.md +1 -1
  3. package/dist/agentPresence.d.ts +80 -0
  4. package/dist/agentPresence.d.ts.map +1 -1
  5. package/dist/agentPresence.js +177 -0
  6. package/dist/agentPresence.js.map +1 -1
  7. package/dist/agentic/index.d.ts +18 -8
  8. package/dist/agentic/index.d.ts.map +1 -1
  9. package/dist/agentic/index.js +18 -144
  10. package/dist/agentic/index.js.map +1 -1
  11. package/dist/agentic/types.d.ts +106 -0
  12. package/dist/agentic/types.d.ts.map +1 -1
  13. package/dist/agentic/types.js +77 -1
  14. package/dist/agentic/types.js.map +1 -1
  15. package/dist/architectAdrs.d.ts +14 -2
  16. package/dist/architectAdrs.d.ts.map +1 -1
  17. package/dist/architectAdrs.js +9 -3
  18. package/dist/architectAdrs.js.map +1 -1
  19. package/dist/client/transports.d.ts.map +1 -1
  20. package/dist/client/transports.js +13 -3
  21. package/dist/client/transports.js.map +1 -1
  22. package/dist/daemon.d.ts +19 -0
  23. package/dist/daemon.d.ts.map +1 -1
  24. package/dist/daemon.js +181 -38
  25. package/dist/daemon.js.map +1 -1
  26. package/dist/daemonConfig.d.ts +30 -0
  27. package/dist/daemonConfig.d.ts.map +1 -1
  28. package/dist/daemonConfig.js +56 -9
  29. package/dist/daemonConfig.js.map +1 -1
  30. package/dist/decibelPaths.d.ts.map +1 -1
  31. package/dist/decibelPaths.js +39 -6
  32. package/dist/decibelPaths.js.map +1 -1
  33. package/dist/domain/issue.d.ts +77 -0
  34. package/dist/domain/issue.d.ts.map +1 -0
  35. package/dist/domain/issue.js +134 -0
  36. package/dist/domain/issue.js.map +1 -0
  37. package/dist/domain/issueCodec.d.ts +39 -0
  38. package/dist/domain/issueCodec.d.ts.map +1 -0
  39. package/dist/domain/issueCodec.js +298 -0
  40. package/dist/domain/issueCodec.js.map +1 -0
  41. package/dist/domain/issueRepository.d.ts +91 -0
  42. package/dist/domain/issueRepository.d.ts.map +1 -0
  43. package/dist/domain/issueRepository.js +257 -0
  44. package/dist/domain/issueRepository.js.map +1 -0
  45. package/dist/facades/definitions.d.ts +0 -1
  46. package/dist/facades/definitions.d.ts.map +1 -1
  47. package/dist/facades/definitions.js +94 -98
  48. package/dist/facades/definitions.js.map +1 -1
  49. package/dist/facades/index.d.ts +2 -1
  50. package/dist/facades/index.d.ts.map +1 -1
  51. package/dist/facades/index.js +1 -1
  52. package/dist/facades/index.js.map +1 -1
  53. package/dist/facades/types.d.ts +17 -0
  54. package/dist/facades/types.d.ts.map +1 -1
  55. package/dist/httpArgs.d.ts +11 -0
  56. package/dist/httpArgs.d.ts.map +1 -0
  57. package/dist/httpArgs.js +39 -0
  58. package/dist/httpArgs.js.map +1 -0
  59. package/dist/httpServer.d.ts +1 -12
  60. package/dist/httpServer.d.ts.map +1 -1
  61. package/dist/httpServer.js +173 -98
  62. package/dist/httpServer.js.map +1 -1
  63. package/dist/kernel.d.ts +55 -1
  64. package/dist/kernel.d.ts.map +1 -1
  65. package/dist/kernel.js +366 -31
  66. package/dist/kernel.js.map +1 -1
  67. package/dist/lib/agent-services/assumptions.d.ts.map +1 -1
  68. package/dist/lib/agent-services/assumptions.js +8 -2
  69. package/dist/lib/agent-services/assumptions.js.map +1 -1
  70. package/dist/lib/atomicWrite.d.ts +26 -0
  71. package/dist/lib/atomicWrite.d.ts.map +1 -0
  72. package/dist/lib/atomicWrite.js +73 -0
  73. package/dist/lib/atomicWrite.js.map +1 -0
  74. package/dist/lib/envelope.d.ts +31 -0
  75. package/dist/lib/envelope.d.ts.map +1 -0
  76. package/dist/lib/envelope.js +80 -0
  77. package/dist/lib/envelope.js.map +1 -0
  78. package/dist/lib/fileLock.d.ts +38 -0
  79. package/dist/lib/fileLock.d.ts.map +1 -0
  80. package/dist/lib/fileLock.js +124 -0
  81. package/dist/lib/fileLock.js.map +1 -0
  82. package/dist/lib/recordIdAllocator.d.ts +86 -0
  83. package/dist/lib/recordIdAllocator.d.ts.map +1 -0
  84. package/dist/lib/recordIdAllocator.js +161 -0
  85. package/dist/lib/recordIdAllocator.js.map +1 -0
  86. package/dist/license.js +3 -3
  87. package/dist/license.js.map +1 -1
  88. package/dist/projectRegistry.d.ts +65 -3
  89. package/dist/projectRegistry.d.ts.map +1 -1
  90. package/dist/projectRegistry.js +233 -33
  91. package/dist/projectRegistry.js.map +1 -1
  92. package/dist/runtime/circuitBreaker.d.ts +95 -0
  93. package/dist/runtime/circuitBreaker.d.ts.map +1 -0
  94. package/dist/runtime/circuitBreaker.js +178 -0
  95. package/dist/runtime/circuitBreaker.js.map +1 -0
  96. package/dist/runtime/ensureRuntime.d.ts +103 -0
  97. package/dist/runtime/ensureRuntime.d.ts.map +1 -0
  98. package/dist/runtime/ensureRuntime.js +281 -0
  99. package/dist/runtime/ensureRuntime.js.map +1 -0
  100. package/dist/runtime/extensions.d.ts +57 -0
  101. package/dist/runtime/extensions.d.ts.map +1 -0
  102. package/dist/runtime/extensions.js +216 -0
  103. package/dist/runtime/extensions.js.map +1 -0
  104. package/dist/runtime/localHttp.d.ts +24 -0
  105. package/dist/runtime/localHttp.d.ts.map +1 -0
  106. package/dist/runtime/localHttp.js +84 -0
  107. package/dist/runtime/localHttp.js.map +1 -0
  108. package/dist/runtime/projectResolution.d.ts +29 -0
  109. package/dist/runtime/projectResolution.d.ts.map +1 -0
  110. package/dist/runtime/projectResolution.js +67 -0
  111. package/dist/runtime/projectResolution.js.map +1 -0
  112. package/dist/runtime/protocol.d.ts +62 -0
  113. package/dist/runtime/protocol.d.ts.map +1 -0
  114. package/dist/runtime/protocol.js +74 -0
  115. package/dist/runtime/protocol.js.map +1 -0
  116. package/dist/sentinelIssues.d.ts +26 -2
  117. package/dist/sentinelIssues.d.ts.map +1 -1
  118. package/dist/sentinelIssues.js +163 -76
  119. package/dist/sentinelIssues.js.map +1 -1
  120. package/dist/server.js +65 -12
  121. package/dist/server.js.map +1 -1
  122. package/dist/setup.d.ts +83 -0
  123. package/dist/setup.d.ts.map +1 -0
  124. package/dist/setup.js +445 -0
  125. package/dist/setup.js.map +1 -0
  126. package/dist/store/markdown.d.ts +14 -1
  127. package/dist/store/markdown.d.ts.map +1 -1
  128. package/dist/store/markdown.js +36 -3
  129. package/dist/store/markdown.js.map +1 -1
  130. package/dist/tools/agentic/index.d.ts.map +1 -1
  131. package/dist/tools/agentic/index.js +28 -2
  132. package/dist/tools/agentic/index.js.map +1 -1
  133. package/dist/tools/architect/index.d.ts.map +1 -1
  134. package/dist/tools/architect/index.js +7 -2
  135. package/dist/tools/architect/index.js.map +1 -1
  136. package/dist/tools/conductor/index.d.ts +9 -0
  137. package/dist/tools/conductor/index.d.ts.map +1 -0
  138. package/dist/tools/conductor/index.js +242 -0
  139. package/dist/tools/conductor/index.js.map +1 -0
  140. package/dist/tools/context.d.ts +10 -5
  141. package/dist/tools/context.d.ts.map +1 -1
  142. package/dist/tools/context.js +45 -18
  143. package/dist/tools/context.js.map +1 -1
  144. package/dist/tools/coordinator/coordinator.d.ts.map +1 -1
  145. package/dist/tools/coordinator/coordinator.js +48 -19
  146. package/dist/tools/coordinator/coordinator.js.map +1 -1
  147. package/dist/tools/crit.d.ts +2 -1
  148. package/dist/tools/crit.d.ts.map +1 -1
  149. package/dist/tools/crit.js +19 -4
  150. package/dist/tools/crit.js.map +1 -1
  151. package/dist/tools/designer/index.d.ts.map +1 -1
  152. package/dist/tools/designer/index.js +4 -3
  153. package/dist/tools/designer/index.js.map +1 -1
  154. package/dist/tools/designer/lateral-tools.d.ts.map +1 -1
  155. package/dist/tools/designer/lateral-tools.js +4 -2
  156. package/dist/tools/designer/lateral-tools.js.map +1 -1
  157. package/dist/tools/designer.d.ts +2 -1
  158. package/dist/tools/designer.d.ts.map +1 -1
  159. package/dist/tools/designer.js +24 -12
  160. package/dist/tools/designer.js.map +1 -1
  161. package/dist/tools/dojo.d.ts +3 -2
  162. package/dist/tools/dojo.d.ts.map +1 -1
  163. package/dist/tools/dojo.js +64 -22
  164. package/dist/tools/dojo.js.map +1 -1
  165. package/dist/tools/dojoPolicy.js +1 -1
  166. package/dist/tools/dojoPolicy.js.map +1 -1
  167. package/dist/tools/friction.d.ts.map +1 -1
  168. package/dist/tools/friction.js +16 -6
  169. package/dist/tools/friction.js.map +1 -1
  170. package/dist/tools/guardian.d.ts +29 -7
  171. package/dist/tools/guardian.d.ts.map +1 -1
  172. package/dist/tools/guardian.js +90 -75
  173. package/dist/tools/guardian.js.map +1 -1
  174. package/dist/tools/hygiene/oracle-hygiene.d.ts.map +1 -1
  175. package/dist/tools/hygiene/oracle-hygiene.js +8 -2
  176. package/dist/tools/hygiene/oracle-hygiene.js.map +1 -1
  177. package/dist/tools/index.d.ts.map +1 -1
  178. package/dist/tools/index.js +29 -18
  179. package/dist/tools/index.js.map +1 -1
  180. package/dist/tools/learnings.d.ts +2 -1
  181. package/dist/tools/learnings.d.ts.map +1 -1
  182. package/dist/tools/learnings.js +31 -15
  183. package/dist/tools/learnings.js.map +1 -1
  184. package/dist/tools/oracle.d.ts.map +1 -1
  185. package/dist/tools/oracle.js +19 -6
  186. package/dist/tools/oracle.js.map +1 -1
  187. package/dist/tools/policy.d.ts +2 -1
  188. package/dist/tools/policy.d.ts.map +1 -1
  189. package/dist/tools/policy.js +28 -9
  190. package/dist/tools/policy.js.map +1 -1
  191. package/dist/tools/postoffice/index.d.ts +10 -0
  192. package/dist/tools/postoffice/index.d.ts.map +1 -0
  193. package/dist/tools/postoffice/index.js +218 -0
  194. package/dist/tools/postoffice/index.js.map +1 -0
  195. package/dist/tools/postoffice.d.ts +78 -0
  196. package/dist/tools/postoffice.d.ts.map +1 -0
  197. package/dist/tools/postoffice.js +199 -0
  198. package/dist/tools/postoffice.js.map +1 -0
  199. package/dist/tools/provenance.d.ts +7 -0
  200. package/dist/tools/provenance.d.ts.map +1 -1
  201. package/dist/tools/provenance.js +42 -16
  202. package/dist/tools/provenance.js.map +1 -1
  203. package/dist/tools/registry/index.d.ts.map +1 -1
  204. package/dist/tools/registry/index.js +41 -12
  205. package/dist/tools/registry/index.js.map +1 -1
  206. package/dist/tools/sentinel/index.d.ts +1 -0
  207. package/dist/tools/sentinel/index.d.ts.map +1 -1
  208. package/dist/tools/sentinel/index.js +88 -4
  209. package/dist/tools/sentinel/index.js.map +1 -1
  210. package/dist/tools/sentinel.d.ts +117 -1
  211. package/dist/tools/sentinel.d.ts.map +1 -1
  212. package/dist/tools/sentinel.js +761 -339
  213. package/dist/tools/sentinel.js.map +1 -1
  214. package/dist/tools/shared/index.d.ts +2 -1
  215. package/dist/tools/shared/index.d.ts.map +1 -1
  216. package/dist/tools/shared/index.js +2 -1
  217. package/dist/tools/shared/index.js.map +1 -1
  218. package/dist/tools/shared/response.d.ts +24 -0
  219. package/dist/tools/shared/response.d.ts.map +1 -1
  220. package/dist/tools/shared/response.js +61 -7
  221. package/dist/tools/shared/response.js.map +1 -1
  222. package/dist/tools/shared/storeRead.d.ts +153 -0
  223. package/dist/tools/shared/storeRead.d.ts.map +1 -0
  224. package/dist/tools/shared/storeRead.js +242 -0
  225. package/dist/tools/shared/storeRead.js.map +1 -0
  226. package/dist/tools/testSpec.d.ts +3 -2
  227. package/dist/tools/testSpec.d.ts.map +1 -1
  228. package/dist/tools/testSpec.js +51 -14
  229. package/dist/tools/testSpec.js.map +1 -1
  230. package/dist/tools/vector.d.ts +33 -1
  231. package/dist/tools/vector.d.ts.map +1 -1
  232. package/dist/tools/vector.js +31 -5
  233. package/dist/tools/vector.js.map +1 -1
  234. package/dist/tools/zoom/index.d.ts +15 -0
  235. package/dist/tools/zoom/index.d.ts.map +1 -0
  236. package/dist/tools/zoom/index.js +174 -0
  237. package/dist/tools/zoom/index.js.map +1 -0
  238. package/dist/tools/zoom.d.ts +234 -0
  239. package/dist/tools/zoom.d.ts.map +1 -0
  240. package/dist/tools/zoom.js +627 -0
  241. package/dist/tools/zoom.js.map +1 -0
  242. package/dist/transports/bridge.d.ts +1 -1
  243. package/dist/transports/bridge.d.ts.map +1 -1
  244. package/dist/transports/bridge.js +2 -0
  245. package/dist/transports/bridge.js.map +1 -1
  246. package/dist/transports/http.d.ts +1 -1
  247. package/dist/transports/http.d.ts.map +1 -1
  248. package/dist/transports/http.js +3 -1
  249. package/dist/transports/http.js.map +1 -1
  250. package/dist/transports/index.d.ts +1 -0
  251. package/dist/transports/index.d.ts.map +1 -1
  252. package/dist/transports/index.js +1 -0
  253. package/dist/transports/index.js.map +1 -1
  254. package/dist/transports/mcp.d.ts +1 -1
  255. package/dist/transports/mcp.d.ts.map +1 -1
  256. package/dist/transports/mcp.js +7 -3
  257. package/dist/transports/mcp.js.map +1 -1
  258. package/dist/transports/stdio.d.ts +1 -1
  259. package/dist/transports/stdio.d.ts.map +1 -1
  260. package/dist/transports/stdio.js +2 -0
  261. package/dist/transports/stdio.js.map +1 -1
  262. package/dist/transports/thinStdio.d.ts +22 -0
  263. package/dist/transports/thinStdio.d.ts.map +1 -0
  264. package/dist/transports/thinStdio.js +218 -0
  265. package/dist/transports/thinStdio.js.map +1 -0
  266. package/dist/transports/types.d.ts +8 -1
  267. package/dist/transports/types.d.ts.map +1 -1
  268. package/dist/types/agent-services.d.ts +7 -0
  269. package/dist/types/agent-services.d.ts.map +1 -1
  270. package/package.json +9 -4
  271. package/templates/com.decibel.daemon.plist +6 -1
  272. package/dist/tools/deck.d.ts +0 -11
  273. package/dist/tools/deck.d.ts.map +0 -1
  274. package/dist/tools/deck.js +0 -1656
  275. package/dist/tools/deck.js.map +0 -1
  276. package/dist/tools/mother.d.ts +0 -11
  277. package/dist/tools/mother.d.ts.map +0 -1
  278. package/dist/tools/mother.js +0 -503
  279. package/dist/tools/mother.js.map +0 -1
  280. package/dist/tools/registry.d.ts +0 -3
  281. package/dist/tools/registry.d.ts.map +0 -1
  282. package/dist/tools/registry.js +0 -189
  283. package/dist/tools/registry.js.map +0 -1
  284. package/dist/tools/senken.d.ts +0 -11
  285. package/dist/tools/senken.d.ts.map +0 -1
  286. package/dist/tools/senken.js +0 -482
  287. package/dist/tools/senken.js.map +0 -1
  288. package/dist/tools/terminal.d.ts +0 -17
  289. package/dist/tools/terminal.d.ts.map +0 -1
  290. package/dist/tools/terminal.js +0 -968
  291. package/dist/tools/terminal.js.map +0 -1
  292. package/dist/tools/toolsIndex.d.ts +0 -5
  293. package/dist/tools/toolsIndex.d.ts.map +0 -1
  294. package/dist/tools/toolsIndex.js +0 -37
  295. package/dist/tools/toolsIndex.js.map +0 -1
@@ -27,18 +27,21 @@
27
27
  import { createServer } from 'http';
28
28
  import { timingSafeEqual, randomBytes } from 'crypto';
29
29
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
30
- import { readFileSync } from 'fs';
30
+ import { readFileSync, existsSync } from 'fs';
31
31
  import { fileURLToPath } from 'url';
32
- import { dirname, join } from 'path';
32
+ import { dirname, join, resolve } from 'path';
33
33
  import { log } from './config.js';
34
34
  import { writeLocalAgentSession } from './agentPresence.js';
35
35
  import { drainCommands, setSessionToken, getSessionToken, dropSessionToken, ackOwnerOf, clearAckOwner, } from './agentInbox.js';
36
36
  import { settleCommandFromAck, ORG_ID } from './agentCommands.js';
37
37
  import { isSupabaseConfigured, getSupabaseServiceClient } from './lib/supabase.js';
38
38
  import { shouldQueueForAgent, parseToolCall } from './httpQueueDetection.js';
39
+ import { getExtensionDiagnostics } from './kernel.js';
39
40
  import { getLicenseValidator } from './license.js';
40
41
  import { listProjects } from './projectRegistry.js';
41
42
  import { setDaemonPort } from './daemon.js';
43
+ import { RUNTIME_PROTOCOL_VERSION } from './runtime/protocol.js';
44
+ import { wrapSuccess, wrapError, envelopeHttpStatus, } from './lib/envelope.js';
42
45
  import { listEpics, listRepoIssues, isProjectResolutionError, } from './tools/sentinel.js';
43
46
  import { voiceInboxAdd, } from './tools/voice.js';
44
47
  import { generateImage, getImageStatus, meshyGenerate, getMeshyStatus, meshyDownload, tripoGenerate, getTripoStatus, tripoDownload, klingGenerateVideo, klingGenerateTextVideo, klingGenerateAvatar, getKlingStatus, listTasks, } from './tools/studio/index.js';
@@ -208,43 +211,6 @@ function formatUptime(ms) {
208
211
  return `${minutes}m ${seconds % 60}s`;
209
212
  return `${seconds}s`;
210
213
  }
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
214
  /**
249
215
  * Send JSON response with status envelope
250
216
  */
@@ -346,20 +312,30 @@ async function executeTool(tool, args, req, tierOverride) {
346
312
  requestId: req.headers['x-request-id'],
347
313
  tier: tierOverride,
348
314
  allowedFacades,
349
- } : tierOverride ? { tier: tierOverride } : undefined;
315
+ transport: 'http',
316
+ } : tierOverride ? { tier: tierOverride, transport: 'http' } : { transport: 'http' };
350
317
  const toolResult = await kernel.dispatch(tool, args, context);
351
318
  const text = toolResult.content[0]?.text;
352
319
  if (toolResult.isError) {
353
320
  return wrapError(text || 'Tool execution failed', 'TOOL_ERROR');
354
321
  }
355
- // Parse JSON result or wrap as message
322
+ // Parse the tool's payload, or carry it as prose when it isn't JSON.
323
+ //
324
+ // The prose branch is legitimate — some tools return markdown — but it used
325
+ // to be indistinguishable from a data response that failed to parse. A
326
+ // caller expecting `events` got an object with no `events` key, `ok: true`,
327
+ // and no indication that anything unusual happened. That is how a
328
+ // concatenated feedback prompt (fixed in tools/shared/response.ts) turned
329
+ // one call in fifteen into a silent empty result for every HTTP consumer.
330
+ // `payload_format` makes the two cases tellable apart.
356
331
  let result;
357
332
  if (text) {
358
333
  try {
359
334
  result = JSON.parse(text);
360
335
  }
361
336
  catch {
362
- result = { message: text };
337
+ log(`HTTP: tool ${tool} returned non-JSON text (${text.length} bytes) — carrying it as prose`);
338
+ result = { message: text, payload_format: 'text' };
363
339
  }
364
340
  }
365
341
  else {
@@ -406,6 +382,7 @@ async function queueForAgent(facade, action, args, agentId, projectId) {
406
382
  }
407
383
  return {
408
384
  status: 'queued',
385
+ ok: true,
409
386
  queue_id: data.id,
410
387
  message: 'Queued for local sync. Use agentic queue_status to check result.',
411
388
  };
@@ -424,7 +401,7 @@ function getAvailableTools() {
424
401
  * Get tools in OpenAI function calling format (facade-based)
425
402
  */
426
403
  function getOpenAITools() {
427
- return kernel.getMcpToolDefinitions('full').map(def => ({
404
+ return kernel.getMcpToolDefinitions('full', { transport: 'http' }).map(def => ({
428
405
  type: 'function',
429
406
  function: {
430
407
  name: def.name,
@@ -630,6 +607,10 @@ export async function startHttpServer(server, kernelInstance, options) {
630
607
  status: 'ok',
631
608
  version: PKG.version,
632
609
  api_version: 'v1',
610
+ // Wire-contract version, negotiated by ensureRuntime(). Distinct from
611
+ // `version` — a long-lived daemon can outlive the clients connecting to
612
+ // it, and most releases do not change the contract. See runtime/protocol.ts.
613
+ protocol_version: RUNTIME_PROTOCOL_VERSION,
633
614
  uptime_ms: uptimeMs,
634
615
  uptime_human: formatUptime(uptimeMs),
635
616
  pid: process.pid,
@@ -642,6 +623,17 @@ export async function startHttpServer(server, kernelInstance, options) {
642
623
  pro: licenseTier !== 'core',
643
624
  license_tier: licenseTier,
644
625
  supabase_configured: isSupabaseConfigured(),
626
+ // Facades whose dependency is failing. `{}` is the healthy case — a
627
+ // non-empty object means calls to those facades are being refused fast
628
+ // rather than left to time out. See runtime/circuitBreaker.ts.
629
+ circuits: kernel.circuitSnapshot(),
630
+ // Private facades loaded from the allowlist, and every entry refused.
631
+ // Reported because the alternative is silence: an extension that failed
632
+ // to load looks exactly like one that was never configured, and this
633
+ // daemon has already shipped one bug of that shape — a broken voice
634
+ // inbox that read as "0 messages" for five hours. See
635
+ // runtime/extensions.ts.
636
+ extensions: getExtensionDiagnostics(),
645
637
  }));
646
638
  return;
647
639
  }
@@ -785,6 +777,23 @@ export async function startHttpServer(server, kernelInstance, options) {
785
777
  // Simple REST Endpoints (for external AI agents)
786
778
  // ========================================================================
787
779
  // GET /tools - List available tools
780
+ // MCP tool definitions, verbatim — the shape a `tools/list` response needs.
781
+ //
782
+ // This is what lets a stdio client stop building its own kernel. /tools
783
+ // above is a human/OpenAI-shaped summary; this one is the exact array the
784
+ // MCP handler returns, so a thin adapter can serve tools/list by forwarding
785
+ // it rather than loading 195 tool modules to produce the same bytes.
786
+ if (path === '/mcp/tools' && req.method === 'GET') {
787
+ const tierParam = url.searchParams.get('tier');
788
+ const tier = (tierParam === 'compact' || tierParam === 'micro' ? tierParam : 'full');
789
+ sendJson(res, 200, wrapSuccess({
790
+ version: PKG.version,
791
+ protocol_version: RUNTIME_PROTOCOL_VERSION,
792
+ tier,
793
+ tools: kernel.getMcpToolDefinitions(tier, { transport: 'http' }),
794
+ }));
795
+ return;
796
+ }
788
797
  if (path === '/tools' && req.method === 'GET') {
789
798
  sendJson(res, 200, wrapSuccess({
790
799
  version: PKG.version,
@@ -1046,6 +1055,80 @@ export async function startHttpServer(server, kernelInstance, options) {
1046
1055
  res.on('error', cleanup);
1047
1056
  return;
1048
1057
  }
1058
+ // GET /conductor/stream — SSE live-tail of the conductor trace ledger (C3).
1059
+ // Pure live-from-now: history is conductor.trace's job. Optional ?session_id / ?project_id filter.
1060
+ if (path === '/conductor/stream' && req.method === 'GET') {
1061
+ const filterSession = url.searchParams.get('session_id');
1062
+ const filterProject = url.searchParams.get('project_id');
1063
+ // mirrors conductor/index.ts ledgerPath() — same file the runs append to
1064
+ const lp = process.env.CONDUCTOR_LEDGER
1065
+ || join(process.env.CONDUCTOR_DIR || resolve(process.cwd(), '../decibel-orchestrator'), '.decibel/conductor/trace.jsonl');
1066
+ res.writeHead(200, {
1067
+ 'Content-Type': 'text/event-stream',
1068
+ 'Cache-Control': 'no-cache',
1069
+ 'Connection': 'keep-alive',
1070
+ });
1071
+ res.write(': connected\n\n');
1072
+ let offset = 0;
1073
+ try {
1074
+ offset = existsSync(lp) ? readFileSync(lp, 'utf8').length : 0;
1075
+ }
1076
+ catch {
1077
+ offset = 0;
1078
+ }
1079
+ let buf = '';
1080
+ // ponytail: re-reads the whole JSONL per poll — fine at trace sizes; switch to
1081
+ // fd offset reads if the ledger grows large.
1082
+ const poll = setInterval(() => {
1083
+ if (res.writableEnded)
1084
+ return;
1085
+ let content = '';
1086
+ try {
1087
+ content = existsSync(lp) ? readFileSync(lp, 'utf8') : '';
1088
+ }
1089
+ catch {
1090
+ return;
1091
+ }
1092
+ if (content.length < offset) {
1093
+ offset = 0;
1094
+ buf = '';
1095
+ } // rotated/truncated
1096
+ if (content.length <= offset)
1097
+ return;
1098
+ buf += content.slice(offset);
1099
+ offset = content.length;
1100
+ let nl;
1101
+ while ((nl = buf.indexOf('\n')) >= 0) {
1102
+ const line = buf.slice(0, nl);
1103
+ buf = buf.slice(nl + 1);
1104
+ if (!line.trim())
1105
+ continue;
1106
+ let evt;
1107
+ try {
1108
+ evt = JSON.parse(line);
1109
+ }
1110
+ catch {
1111
+ continue;
1112
+ }
1113
+ if (filterSession && evt.request_id !== filterSession)
1114
+ continue;
1115
+ if (filterProject && evt.project_id !== filterProject)
1116
+ continue;
1117
+ if (!res.writableEnded)
1118
+ res.write(`data: ${JSON.stringify(evt)}\n\n`);
1119
+ }
1120
+ }, 300);
1121
+ activeSseConnections.add(res);
1122
+ log(`HTTP: /conductor/stream opened (${activeSseConnections.size} SSE connections)`);
1123
+ const cleanup = () => {
1124
+ clearInterval(poll);
1125
+ activeSseConnections.delete(res);
1126
+ log(`HTTP: /conductor/stream closed (${activeSseConnections.size} SSE connections)`);
1127
+ };
1128
+ res.on('close', cleanup);
1129
+ res.on('error', cleanup);
1130
+ return;
1131
+ }
1049
1132
  // GET /facades - Facade registry for agent bootstrap
1050
1133
  if (path === '/facades' && req.method === 'GET') {
1051
1134
  const tier = (url.searchParams.get('tier') || 'full');
@@ -1077,7 +1160,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1077
1160
  const tier = await resolveTier(req, configLicenseKey);
1078
1161
  log(`HTTP: /call tool=${tool} tier=${tier}`);
1079
1162
  const result = await executeTool(tool, args, req, tier);
1080
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1163
+ sendJson(res, envelopeHttpStatus(result), result);
1081
1164
  }
1082
1165
  catch (error) {
1083
1166
  const message = error instanceof Error ? error.message : String(error);
@@ -1117,10 +1200,30 @@ export async function startHttpServer(server, kernelInstance, options) {
1117
1200
  requestId: req.headers['x-request-id'] || bodyContext.requestId,
1118
1201
  allowedFacades: bodyContext.allowedFacades,
1119
1202
  tier,
1203
+ // Set here, never read from the request: a caller that could name its
1204
+ // own transport could name 'stdio' and walk straight through the
1205
+ // local-only gate.
1206
+ transport: 'http',
1120
1207
  };
1121
1208
  log(`HTTP: /batch — ${calls.length} calls (agent=${context.agentId || 'anonymous'}, tier=${tier})`);
1122
1209
  const results = await kernel.batch(calls, context);
1123
- sendJson(res, 200, { status: 'executed', results });
1210
+ // `ok` reflects STRUCTURAL validity: did every call name something this
1211
+ // runtime actually has. A call that ran and returned isError leaves ok
1212
+ // true — partial failure is a normal batch outcome and callers depend on
1213
+ // that. A call naming a facade that is not registered does not.
1214
+ //
1215
+ // The status stays 200 because the body still carries real results for
1216
+ // the other calls in the batch, and throwing those away behind a 4xx
1217
+ // would trade one silent failure for another.
1218
+ const unknown = results.filter(r => r.code === 'UNKNOWN_FACADE');
1219
+ if (unknown.length > 0) {
1220
+ log(`HTTP: /batch — ${unknown.length} unknown facade(s): ${unknown.map(r => r.facade).join(', ')}`);
1221
+ }
1222
+ sendJson(res, 200, {
1223
+ status: unknown.length > 0 ? 'error' : 'executed',
1224
+ ok: unknown.length === 0,
1225
+ results,
1226
+ });
1124
1227
  }
1125
1228
  catch (error) {
1126
1229
  const message = error instanceof Error ? error.message : String(error);
@@ -1149,7 +1252,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1149
1252
  const body = await parseBody(req);
1150
1253
  log(`HTTP: /api/tools/${toolName}`);
1151
1254
  const result = await executeTool(toolName, body);
1152
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1255
+ sendJson(res, envelopeHttpStatus(result), result);
1153
1256
  }
1154
1257
  catch (error) {
1155
1258
  const message = error instanceof Error ? error.message : String(error);
@@ -1166,7 +1269,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1166
1269
  const body = await parseBody(req);
1167
1270
  log('HTTP: /dojo/wish');
1168
1271
  const result = await executeTool('dojo_add_wish', body);
1169
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1272
+ sendJson(res, envelopeHttpStatus(result), result);
1170
1273
  }
1171
1274
  catch (error) {
1172
1275
  const message = error instanceof Error ? error.message : String(error);
@@ -1180,7 +1283,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1180
1283
  const body = await parseBody(req);
1181
1284
  log('HTTP: /dojo/propose');
1182
1285
  const result = await executeTool('dojo_create_proposal', body);
1183
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1286
+ sendJson(res, envelopeHttpStatus(result), result);
1184
1287
  }
1185
1288
  catch (error) {
1186
1289
  const message = error instanceof Error ? error.message : String(error);
@@ -1194,7 +1297,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1194
1297
  const body = await parseBody(req);
1195
1298
  log('HTTP: /dojo/scaffold');
1196
1299
  const result = await executeTool('dojo_scaffold_experiment', body);
1197
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1300
+ sendJson(res, envelopeHttpStatus(result), result);
1198
1301
  }
1199
1302
  catch (error) {
1200
1303
  const message = error instanceof Error ? error.message : String(error);
@@ -1208,7 +1311,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1208
1311
  const body = await parseBody(req);
1209
1312
  log('HTTP: /dojo/run');
1210
1313
  const result = await executeTool('dojo_run_experiment', body);
1211
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1314
+ sendJson(res, envelopeHttpStatus(result), result);
1212
1315
  }
1213
1316
  catch (error) {
1214
1317
  const message = error instanceof Error ? error.message : String(error);
@@ -1222,7 +1325,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1222
1325
  const body = await parseBody(req);
1223
1326
  log('HTTP: /dojo/results');
1224
1327
  const result = await executeTool('dojo_read_results', body);
1225
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1328
+ sendJson(res, envelopeHttpStatus(result), result);
1226
1329
  }
1227
1330
  catch (error) {
1228
1331
  const message = error instanceof Error ? error.message : String(error);
@@ -1243,7 +1346,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1243
1346
  }
1244
1347
  log('HTTP: /dojo/list');
1245
1348
  const result = await executeTool('dojo_list', body);
1246
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1349
+ sendJson(res, envelopeHttpStatus(result), result);
1247
1350
  }
1248
1351
  catch (error) {
1249
1352
  const message = error instanceof Error ? error.message : String(error);
@@ -1263,7 +1366,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1263
1366
  }
1264
1367
  log('HTTP: /dojo/wishes');
1265
1368
  const result = await executeTool('dojo_list_wishes', body);
1266
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1369
+ sendJson(res, envelopeHttpStatus(result), result);
1267
1370
  }
1268
1371
  catch (error) {
1269
1372
  const message = error instanceof Error ? error.message : String(error);
@@ -1277,7 +1380,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1277
1380
  const body = await parseBody(req);
1278
1381
  log('HTTP: /dojo/can-graduate');
1279
1382
  const result = await executeTool('dojo_can_graduate', body);
1280
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1383
+ sendJson(res, envelopeHttpStatus(result), result);
1281
1384
  }
1282
1385
  catch (error) {
1283
1386
  const message = error instanceof Error ? error.message : String(error);
@@ -1291,7 +1394,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1291
1394
  const body = await parseBody(req);
1292
1395
  log('HTTP: /dojo/artifact');
1293
1396
  const result = await executeTool('dojo_read_artifact', body);
1294
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1397
+ sendJson(res, envelopeHttpStatus(result), result);
1295
1398
  }
1296
1399
  catch (error) {
1297
1400
  const message = error instanceof Error ? error.message : String(error);
@@ -1305,7 +1408,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1305
1408
  const body = await parseBody(req);
1306
1409
  log('HTTP: /dojo/bench');
1307
1410
  const result = await executeTool('dojo_bench', body);
1308
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1411
+ sendJson(res, envelopeHttpStatus(result), result);
1309
1412
  }
1310
1413
  catch (error) {
1311
1414
  const message = error instanceof Error ? error.message : String(error);
@@ -1322,7 +1425,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1322
1425
  const body = await parseBody(req);
1323
1426
  log('HTTP: /bench/run');
1324
1427
  const result = await executeTool('decibel_bench', body);
1325
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1428
+ sendJson(res, envelopeHttpStatus(result), result);
1326
1429
  }
1327
1430
  catch (error) {
1328
1431
  const message = error instanceof Error ? error.message : String(error);
@@ -1336,7 +1439,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1336
1439
  const body = await parseBody(req);
1337
1440
  log('HTTP: /bench/compare');
1338
1441
  const result = await executeTool('decibel_bench_compare', body);
1339
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1442
+ sendJson(res, envelopeHttpStatus(result), result);
1340
1443
  }
1341
1444
  catch (error) {
1342
1445
  const message = error instanceof Error ? error.message : String(error);
@@ -1353,7 +1456,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1353
1456
  const body = await parseBody(req);
1354
1457
  log('HTTP: /context/refresh');
1355
1458
  const result = await executeTool('decibel_context_refresh', body);
1356
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1459
+ sendJson(res, envelopeHttpStatus(result), result);
1357
1460
  }
1358
1461
  catch (error) {
1359
1462
  const message = error instanceof Error ? error.message : String(error);
@@ -1367,7 +1470,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1367
1470
  const body = await parseBody(req);
1368
1471
  log('HTTP: /context/pin');
1369
1472
  const result = await executeTool('decibel_context_pin', body);
1370
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1473
+ sendJson(res, envelopeHttpStatus(result), result);
1371
1474
  }
1372
1475
  catch (error) {
1373
1476
  const message = error instanceof Error ? error.message : String(error);
@@ -1381,7 +1484,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1381
1484
  const body = await parseBody(req);
1382
1485
  log('HTTP: /context/unpin');
1383
1486
  const result = await executeTool('decibel_context_unpin', body);
1384
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1487
+ sendJson(res, envelopeHttpStatus(result), result);
1385
1488
  }
1386
1489
  catch (error) {
1387
1490
  const message = error instanceof Error ? error.message : String(error);
@@ -1401,7 +1504,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1401
1504
  }
1402
1505
  log('HTTP: /context/list');
1403
1506
  const result = await executeTool('decibel_context_list', body);
1404
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1507
+ sendJson(res, envelopeHttpStatus(result), result);
1405
1508
  }
1406
1509
  catch (error) {
1407
1510
  const message = error instanceof Error ? error.message : String(error);
@@ -1415,7 +1518,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1415
1518
  const body = await parseBody(req);
1416
1519
  log('HTTP: /event/append');
1417
1520
  const result = await executeTool('decibel_event_append', body);
1418
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1521
+ sendJson(res, envelopeHttpStatus(result), result);
1419
1522
  }
1420
1523
  catch (error) {
1421
1524
  const message = error instanceof Error ? error.message : String(error);
@@ -1443,7 +1546,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1443
1546
  }
1444
1547
  log('HTTP: /event/search');
1445
1548
  const result = await executeTool('decibel_event_search', body);
1446
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1549
+ sendJson(res, envelopeHttpStatus(result), result);
1447
1550
  }
1448
1551
  catch (error) {
1449
1552
  const message = error instanceof Error ? error.message : String(error);
@@ -1457,7 +1560,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1457
1560
  const body = await parseBody(req);
1458
1561
  log('HTTP: /artifact/list');
1459
1562
  const result = await executeTool('decibel_artifact_list', body);
1460
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1563
+ sendJson(res, envelopeHttpStatus(result), result);
1461
1564
  }
1462
1565
  catch (error) {
1463
1566
  const message = error instanceof Error ? error.message : String(error);
@@ -1471,7 +1574,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1471
1574
  const body = await parseBody(req);
1472
1575
  log('HTTP: /artifact/read');
1473
1576
  const result = await executeTool('decibel_artifact_read', body);
1474
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1577
+ sendJson(res, envelopeHttpStatus(result), result);
1475
1578
  }
1476
1579
  catch (error) {
1477
1580
  const message = error instanceof Error ? error.message : String(error);
@@ -2129,36 +2232,8 @@ ${authToken ? '║ Auth: Bearer token required
2129
2232
  },
2130
2233
  };
2131
2234
  }
2132
- /**
2133
- * Parse command line arguments for HTTP mode
2134
- */
2135
- export function parseHttpArgs(args) {
2136
- const httpMode = args.includes('--http');
2137
- const portIndex = args.indexOf('--port');
2138
- // --port flag wins; else honor PORT env (Render sets it); else leave undefined
2139
- // so daemonConfig's default (4888) applies downstream instead of being
2140
- // short-circuited by a baked-in default here. See server.ts port/host resolution.
2141
- const port = portIndex !== -1
2142
- ? parseInt(args[portIndex + 1], 10)
2143
- : process.env.PORT
2144
- ? parseInt(process.env.PORT, 10)
2145
- : undefined;
2146
- // SECURITY: Prefer env var for auth token (CLI args visible in ps/history)
2147
- // Fall back to --auth-token for backwards compatibility
2148
- const authIndex = args.indexOf('--auth-token');
2149
- const authToken = process.env.DECIBEL_AUTH_TOKEN ||
2150
- (authIndex !== -1 ? args[authIndex + 1] : undefined);
2151
- const hostIndex = args.indexOf('--host');
2152
- // --host flag wins; else leave undefined so daemonConfig host (127.0.0.1, daemon
2153
- // mode) or the transport default applies instead of being short-circuited here.
2154
- const host = hostIndex !== -1 ? args[hostIndex + 1] : undefined;
2155
- // SSE/Connection tuning arguments
2156
- const keepaliveIndex = args.indexOf('--sse-keepalive');
2157
- const sseKeepaliveMs = keepaliveIndex !== -1 ? parseInt(args[keepaliveIndex + 1], 10) : undefined;
2158
- const timeoutIndex = args.indexOf('--timeout');
2159
- const timeoutMs = timeoutIndex !== -1 ? parseInt(args[timeoutIndex + 1], 10) : undefined;
2160
- const retryIndex = args.indexOf('--sse-retry');
2161
- const retryIntervalMs = retryIndex !== -1 ? parseInt(args[retryIndex + 1], 10) : undefined;
2162
- return { httpMode, port, authToken, host, sseKeepaliveMs, timeoutMs, retryIntervalMs };
2163
- }
2235
+ // parseHttpArgs moved to ./httpArgs.js so that argv parsing carries no
2236
+ // dependencies. Re-exported here because it has always been part of this
2237
+ // module's surface.
2238
+ export { parseHttpArgs } from './httpArgs.js';
2164
2239
  //# sourceMappingURL=httpServer.js.map