@decibelsystems/tools 2.2.0-beta.0 → 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 (273) hide show
  1. package/CHANGELOG.md +160 -0
  2. package/dist/agentPresence.d.ts +80 -0
  3. package/dist/agentPresence.d.ts.map +1 -1
  4. package/dist/agentPresence.js +177 -0
  5. package/dist/agentPresence.js.map +1 -1
  6. package/dist/agentic/index.d.ts +18 -8
  7. package/dist/agentic/index.d.ts.map +1 -1
  8. package/dist/agentic/index.js +18 -144
  9. package/dist/agentic/index.js.map +1 -1
  10. package/dist/agentic/types.d.ts +106 -0
  11. package/dist/agentic/types.d.ts.map +1 -1
  12. package/dist/agentic/types.js +77 -1
  13. package/dist/agentic/types.js.map +1 -1
  14. package/dist/architectAdrs.d.ts +14 -2
  15. package/dist/architectAdrs.d.ts.map +1 -1
  16. package/dist/architectAdrs.js +9 -3
  17. package/dist/architectAdrs.js.map +1 -1
  18. package/dist/client/transports.d.ts.map +1 -1
  19. package/dist/client/transports.js +13 -3
  20. package/dist/client/transports.js.map +1 -1
  21. package/dist/daemon.d.ts +19 -0
  22. package/dist/daemon.d.ts.map +1 -1
  23. package/dist/daemon.js +181 -38
  24. package/dist/daemon.js.map +1 -1
  25. package/dist/daemonConfig.d.ts +24 -0
  26. package/dist/daemonConfig.d.ts.map +1 -1
  27. package/dist/daemonConfig.js +46 -12
  28. package/dist/daemonConfig.js.map +1 -1
  29. package/dist/decibelPaths.d.ts.map +1 -1
  30. package/dist/decibelPaths.js +39 -6
  31. package/dist/decibelPaths.js.map +1 -1
  32. package/dist/domain/issue.d.ts +77 -0
  33. package/dist/domain/issue.d.ts.map +1 -0
  34. package/dist/domain/issue.js +134 -0
  35. package/dist/domain/issue.js.map +1 -0
  36. package/dist/domain/issueCodec.d.ts +39 -0
  37. package/dist/domain/issueCodec.d.ts.map +1 -0
  38. package/dist/domain/issueCodec.js +298 -0
  39. package/dist/domain/issueCodec.js.map +1 -0
  40. package/dist/domain/issueRepository.d.ts +91 -0
  41. package/dist/domain/issueRepository.d.ts.map +1 -0
  42. package/dist/domain/issueRepository.js +257 -0
  43. package/dist/domain/issueRepository.js.map +1 -0
  44. package/dist/facades/definitions.d.ts +0 -1
  45. package/dist/facades/definitions.d.ts.map +1 -1
  46. package/dist/facades/definitions.js +94 -113
  47. package/dist/facades/definitions.js.map +1 -1
  48. package/dist/facades/index.d.ts +2 -1
  49. package/dist/facades/index.d.ts.map +1 -1
  50. package/dist/facades/index.js +1 -1
  51. package/dist/facades/index.js.map +1 -1
  52. package/dist/facades/types.d.ts +17 -0
  53. package/dist/facades/types.d.ts.map +1 -1
  54. package/dist/httpArgs.d.ts +11 -0
  55. package/dist/httpArgs.d.ts.map +1 -0
  56. package/dist/httpArgs.js +39 -0
  57. package/dist/httpArgs.js.map +1 -0
  58. package/dist/httpServer.d.ts +1 -12
  59. package/dist/httpServer.d.ts.map +1 -1
  60. package/dist/httpServer.js +97 -96
  61. package/dist/httpServer.js.map +1 -1
  62. package/dist/kernel.d.ts +46 -1
  63. package/dist/kernel.d.ts.map +1 -1
  64. package/dist/kernel.js +311 -26
  65. package/dist/kernel.js.map +1 -1
  66. package/dist/lib/agent-services/assumptions.d.ts.map +1 -1
  67. package/dist/lib/agent-services/assumptions.js +8 -2
  68. package/dist/lib/agent-services/assumptions.js.map +1 -1
  69. package/dist/lib/atomicWrite.d.ts +26 -0
  70. package/dist/lib/atomicWrite.d.ts.map +1 -0
  71. package/dist/lib/atomicWrite.js +73 -0
  72. package/dist/lib/atomicWrite.js.map +1 -0
  73. package/dist/lib/envelope.d.ts +31 -0
  74. package/dist/lib/envelope.d.ts.map +1 -0
  75. package/dist/lib/envelope.js +80 -0
  76. package/dist/lib/envelope.js.map +1 -0
  77. package/dist/lib/fileLock.d.ts +38 -0
  78. package/dist/lib/fileLock.d.ts.map +1 -0
  79. package/dist/lib/fileLock.js +124 -0
  80. package/dist/lib/fileLock.js.map +1 -0
  81. package/dist/lib/recordIdAllocator.d.ts +86 -0
  82. package/dist/lib/recordIdAllocator.d.ts.map +1 -0
  83. package/dist/lib/recordIdAllocator.js +161 -0
  84. package/dist/lib/recordIdAllocator.js.map +1 -0
  85. package/dist/projectRegistry.d.ts.map +1 -1
  86. package/dist/projectRegistry.js +11 -0
  87. package/dist/projectRegistry.js.map +1 -1
  88. package/dist/runtime/circuitBreaker.d.ts +95 -0
  89. package/dist/runtime/circuitBreaker.d.ts.map +1 -0
  90. package/dist/runtime/circuitBreaker.js +178 -0
  91. package/dist/runtime/circuitBreaker.js.map +1 -0
  92. package/dist/runtime/ensureRuntime.d.ts +103 -0
  93. package/dist/runtime/ensureRuntime.d.ts.map +1 -0
  94. package/dist/runtime/ensureRuntime.js +281 -0
  95. package/dist/runtime/ensureRuntime.js.map +1 -0
  96. package/dist/runtime/extensions.d.ts +57 -0
  97. package/dist/runtime/extensions.d.ts.map +1 -0
  98. package/dist/runtime/extensions.js +216 -0
  99. package/dist/runtime/extensions.js.map +1 -0
  100. package/dist/runtime/localHttp.d.ts +24 -0
  101. package/dist/runtime/localHttp.d.ts.map +1 -0
  102. package/dist/runtime/localHttp.js +84 -0
  103. package/dist/runtime/localHttp.js.map +1 -0
  104. package/dist/runtime/projectResolution.d.ts +29 -0
  105. package/dist/runtime/projectResolution.d.ts.map +1 -0
  106. package/dist/runtime/projectResolution.js +67 -0
  107. package/dist/runtime/projectResolution.js.map +1 -0
  108. package/dist/runtime/protocol.d.ts +62 -0
  109. package/dist/runtime/protocol.d.ts.map +1 -0
  110. package/dist/runtime/protocol.js +74 -0
  111. package/dist/runtime/protocol.js.map +1 -0
  112. package/dist/sentinelIssues.d.ts +26 -2
  113. package/dist/sentinelIssues.d.ts.map +1 -1
  114. package/dist/sentinelIssues.js +125 -64
  115. package/dist/sentinelIssues.js.map +1 -1
  116. package/dist/server.js +59 -12
  117. package/dist/server.js.map +1 -1
  118. package/dist/tools/agentic/index.d.ts.map +1 -1
  119. package/dist/tools/agentic/index.js +28 -2
  120. package/dist/tools/agentic/index.js.map +1 -1
  121. package/dist/tools/architect/index.d.ts.map +1 -1
  122. package/dist/tools/architect/index.js +7 -2
  123. package/dist/tools/architect/index.js.map +1 -1
  124. package/dist/tools/context.d.ts +10 -5
  125. package/dist/tools/context.d.ts.map +1 -1
  126. package/dist/tools/context.js +45 -18
  127. package/dist/tools/context.js.map +1 -1
  128. package/dist/tools/coordinator/coordinator.d.ts.map +1 -1
  129. package/dist/tools/coordinator/coordinator.js +48 -19
  130. package/dist/tools/coordinator/coordinator.js.map +1 -1
  131. package/dist/tools/crit.d.ts +2 -1
  132. package/dist/tools/crit.d.ts.map +1 -1
  133. package/dist/tools/crit.js +19 -4
  134. package/dist/tools/crit.js.map +1 -1
  135. package/dist/tools/designer/index.d.ts.map +1 -1
  136. package/dist/tools/designer/index.js +4 -3
  137. package/dist/tools/designer/index.js.map +1 -1
  138. package/dist/tools/designer/lateral-tools.d.ts.map +1 -1
  139. package/dist/tools/designer/lateral-tools.js +4 -2
  140. package/dist/tools/designer/lateral-tools.js.map +1 -1
  141. package/dist/tools/designer.d.ts +2 -1
  142. package/dist/tools/designer.d.ts.map +1 -1
  143. package/dist/tools/designer.js +24 -12
  144. package/dist/tools/designer.js.map +1 -1
  145. package/dist/tools/dojo.d.ts +3 -2
  146. package/dist/tools/dojo.d.ts.map +1 -1
  147. package/dist/tools/dojo.js +64 -22
  148. package/dist/tools/dojo.js.map +1 -1
  149. package/dist/tools/friction.d.ts.map +1 -1
  150. package/dist/tools/friction.js +16 -6
  151. package/dist/tools/friction.js.map +1 -1
  152. package/dist/tools/guardian.d.ts.map +1 -1
  153. package/dist/tools/guardian.js +6 -2
  154. package/dist/tools/guardian.js.map +1 -1
  155. package/dist/tools/hygiene/oracle-hygiene.d.ts.map +1 -1
  156. package/dist/tools/hygiene/oracle-hygiene.js +8 -2
  157. package/dist/tools/hygiene/oracle-hygiene.js.map +1 -1
  158. package/dist/tools/index.d.ts.map +1 -1
  159. package/dist/tools/index.js +27 -18
  160. package/dist/tools/index.js.map +1 -1
  161. package/dist/tools/learnings.d.ts +2 -1
  162. package/dist/tools/learnings.d.ts.map +1 -1
  163. package/dist/tools/learnings.js +31 -15
  164. package/dist/tools/learnings.js.map +1 -1
  165. package/dist/tools/oracle.d.ts.map +1 -1
  166. package/dist/tools/oracle.js +19 -6
  167. package/dist/tools/oracle.js.map +1 -1
  168. package/dist/tools/policy.d.ts +2 -1
  169. package/dist/tools/policy.d.ts.map +1 -1
  170. package/dist/tools/policy.js +28 -9
  171. package/dist/tools/policy.js.map +1 -1
  172. package/dist/tools/postoffice/index.d.ts +10 -0
  173. package/dist/tools/postoffice/index.d.ts.map +1 -0
  174. package/dist/tools/postoffice/index.js +218 -0
  175. package/dist/tools/postoffice/index.js.map +1 -0
  176. package/dist/tools/postoffice.d.ts +78 -0
  177. package/dist/tools/postoffice.d.ts.map +1 -0
  178. package/dist/tools/postoffice.js +199 -0
  179. package/dist/tools/postoffice.js.map +1 -0
  180. package/dist/tools/provenance.d.ts +7 -0
  181. package/dist/tools/provenance.d.ts.map +1 -1
  182. package/dist/tools/provenance.js +42 -16
  183. package/dist/tools/provenance.js.map +1 -1
  184. package/dist/tools/sentinel/index.d.ts +1 -0
  185. package/dist/tools/sentinel/index.d.ts.map +1 -1
  186. package/dist/tools/sentinel/index.js +88 -4
  187. package/dist/tools/sentinel/index.js.map +1 -1
  188. package/dist/tools/sentinel.d.ts +102 -1
  189. package/dist/tools/sentinel.d.ts.map +1 -1
  190. package/dist/tools/sentinel.js +544 -328
  191. package/dist/tools/sentinel.js.map +1 -1
  192. package/dist/tools/shared/index.d.ts +2 -1
  193. package/dist/tools/shared/index.d.ts.map +1 -1
  194. package/dist/tools/shared/index.js +2 -1
  195. package/dist/tools/shared/index.js.map +1 -1
  196. package/dist/tools/shared/response.d.ts +24 -0
  197. package/dist/tools/shared/response.d.ts.map +1 -1
  198. package/dist/tools/shared/response.js +61 -7
  199. package/dist/tools/shared/response.js.map +1 -1
  200. package/dist/tools/shared/storeRead.d.ts +153 -0
  201. package/dist/tools/shared/storeRead.d.ts.map +1 -0
  202. package/dist/tools/shared/storeRead.js +242 -0
  203. package/dist/tools/shared/storeRead.js.map +1 -0
  204. package/dist/tools/testSpec.d.ts +3 -2
  205. package/dist/tools/testSpec.d.ts.map +1 -1
  206. package/dist/tools/testSpec.js +51 -14
  207. package/dist/tools/testSpec.js.map +1 -1
  208. package/dist/tools/vector.d.ts +33 -1
  209. package/dist/tools/vector.d.ts.map +1 -1
  210. package/dist/tools/vector.js +31 -5
  211. package/dist/tools/vector.js.map +1 -1
  212. package/dist/tools/zoom/index.d.ts +15 -0
  213. package/dist/tools/zoom/index.d.ts.map +1 -0
  214. package/dist/tools/zoom/index.js +174 -0
  215. package/dist/tools/zoom/index.js.map +1 -0
  216. package/dist/tools/zoom.d.ts +234 -0
  217. package/dist/tools/zoom.d.ts.map +1 -0
  218. package/dist/tools/zoom.js +627 -0
  219. package/dist/tools/zoom.js.map +1 -0
  220. package/dist/transports/bridge.d.ts +1 -1
  221. package/dist/transports/bridge.d.ts.map +1 -1
  222. package/dist/transports/bridge.js +2 -0
  223. package/dist/transports/bridge.js.map +1 -1
  224. package/dist/transports/http.d.ts +1 -1
  225. package/dist/transports/http.d.ts.map +1 -1
  226. package/dist/transports/http.js +3 -1
  227. package/dist/transports/http.js.map +1 -1
  228. package/dist/transports/index.d.ts +1 -0
  229. package/dist/transports/index.d.ts.map +1 -1
  230. package/dist/transports/index.js +1 -0
  231. package/dist/transports/index.js.map +1 -1
  232. package/dist/transports/mcp.d.ts +1 -1
  233. package/dist/transports/mcp.d.ts.map +1 -1
  234. package/dist/transports/mcp.js +7 -3
  235. package/dist/transports/mcp.js.map +1 -1
  236. package/dist/transports/stdio.d.ts +1 -1
  237. package/dist/transports/stdio.d.ts.map +1 -1
  238. package/dist/transports/stdio.js +2 -0
  239. package/dist/transports/stdio.js.map +1 -1
  240. package/dist/transports/thinStdio.d.ts +22 -0
  241. package/dist/transports/thinStdio.d.ts.map +1 -0
  242. package/dist/transports/thinStdio.js +218 -0
  243. package/dist/transports/thinStdio.js.map +1 -0
  244. package/dist/transports/types.d.ts +8 -1
  245. package/dist/transports/types.d.ts.map +1 -1
  246. package/dist/types/agent-services.d.ts +7 -0
  247. package/dist/types/agent-services.d.ts.map +1 -1
  248. package/package.json +9 -4
  249. package/templates/com.decibel.daemon.plist +6 -1
  250. package/dist/tools/deck.d.ts +0 -11
  251. package/dist/tools/deck.d.ts.map +0 -1
  252. package/dist/tools/deck.js +0 -1656
  253. package/dist/tools/deck.js.map +0 -1
  254. package/dist/tools/mother.d.ts +0 -11
  255. package/dist/tools/mother.d.ts.map +0 -1
  256. package/dist/tools/mother.js +0 -503
  257. package/dist/tools/mother.js.map +0 -1
  258. package/dist/tools/registry.d.ts +0 -3
  259. package/dist/tools/registry.d.ts.map +0 -1
  260. package/dist/tools/registry.js +0 -189
  261. package/dist/tools/registry.js.map +0 -1
  262. package/dist/tools/senken.d.ts +0 -11
  263. package/dist/tools/senken.d.ts.map +0 -1
  264. package/dist/tools/senken.js +0 -482
  265. package/dist/tools/senken.js.map +0 -1
  266. package/dist/tools/terminal.d.ts +0 -17
  267. package/dist/tools/terminal.d.ts.map +0 -1
  268. package/dist/tools/terminal.js +0 -968
  269. package/dist/tools/terminal.js.map +0 -1
  270. package/dist/tools/toolsIndex.d.ts +0 -5
  271. package/dist/tools/toolsIndex.d.ts.map +0 -1
  272. package/dist/tools/toolsIndex.js +0 -37
  273. package/dist/tools/toolsIndex.js.map +0 -1
@@ -36,9 +36,12 @@ import { drainCommands, setSessionToken, getSessionToken, dropSessionToken, ackO
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,
@@ -1151,7 +1160,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1151
1160
  const tier = await resolveTier(req, configLicenseKey);
1152
1161
  log(`HTTP: /call tool=${tool} tier=${tier}`);
1153
1162
  const result = await executeTool(tool, args, req, tier);
1154
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1163
+ sendJson(res, envelopeHttpStatus(result), result);
1155
1164
  }
1156
1165
  catch (error) {
1157
1166
  const message = error instanceof Error ? error.message : String(error);
@@ -1191,10 +1200,30 @@ export async function startHttpServer(server, kernelInstance, options) {
1191
1200
  requestId: req.headers['x-request-id'] || bodyContext.requestId,
1192
1201
  allowedFacades: bodyContext.allowedFacades,
1193
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',
1194
1207
  };
1195
1208
  log(`HTTP: /batch — ${calls.length} calls (agent=${context.agentId || 'anonymous'}, tier=${tier})`);
1196
1209
  const results = await kernel.batch(calls, context);
1197
- 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
+ });
1198
1227
  }
1199
1228
  catch (error) {
1200
1229
  const message = error instanceof Error ? error.message : String(error);
@@ -1223,7 +1252,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1223
1252
  const body = await parseBody(req);
1224
1253
  log(`HTTP: /api/tools/${toolName}`);
1225
1254
  const result = await executeTool(toolName, body);
1226
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1255
+ sendJson(res, envelopeHttpStatus(result), result);
1227
1256
  }
1228
1257
  catch (error) {
1229
1258
  const message = error instanceof Error ? error.message : String(error);
@@ -1240,7 +1269,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1240
1269
  const body = await parseBody(req);
1241
1270
  log('HTTP: /dojo/wish');
1242
1271
  const result = await executeTool('dojo_add_wish', body);
1243
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1272
+ sendJson(res, envelopeHttpStatus(result), result);
1244
1273
  }
1245
1274
  catch (error) {
1246
1275
  const message = error instanceof Error ? error.message : String(error);
@@ -1254,7 +1283,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1254
1283
  const body = await parseBody(req);
1255
1284
  log('HTTP: /dojo/propose');
1256
1285
  const result = await executeTool('dojo_create_proposal', body);
1257
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1286
+ sendJson(res, envelopeHttpStatus(result), result);
1258
1287
  }
1259
1288
  catch (error) {
1260
1289
  const message = error instanceof Error ? error.message : String(error);
@@ -1268,7 +1297,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1268
1297
  const body = await parseBody(req);
1269
1298
  log('HTTP: /dojo/scaffold');
1270
1299
  const result = await executeTool('dojo_scaffold_experiment', body);
1271
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1300
+ sendJson(res, envelopeHttpStatus(result), result);
1272
1301
  }
1273
1302
  catch (error) {
1274
1303
  const message = error instanceof Error ? error.message : String(error);
@@ -1282,7 +1311,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1282
1311
  const body = await parseBody(req);
1283
1312
  log('HTTP: /dojo/run');
1284
1313
  const result = await executeTool('dojo_run_experiment', body);
1285
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1314
+ sendJson(res, envelopeHttpStatus(result), result);
1286
1315
  }
1287
1316
  catch (error) {
1288
1317
  const message = error instanceof Error ? error.message : String(error);
@@ -1296,7 +1325,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1296
1325
  const body = await parseBody(req);
1297
1326
  log('HTTP: /dojo/results');
1298
1327
  const result = await executeTool('dojo_read_results', body);
1299
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1328
+ sendJson(res, envelopeHttpStatus(result), result);
1300
1329
  }
1301
1330
  catch (error) {
1302
1331
  const message = error instanceof Error ? error.message : String(error);
@@ -1317,7 +1346,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1317
1346
  }
1318
1347
  log('HTTP: /dojo/list');
1319
1348
  const result = await executeTool('dojo_list', body);
1320
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1349
+ sendJson(res, envelopeHttpStatus(result), result);
1321
1350
  }
1322
1351
  catch (error) {
1323
1352
  const message = error instanceof Error ? error.message : String(error);
@@ -1337,7 +1366,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1337
1366
  }
1338
1367
  log('HTTP: /dojo/wishes');
1339
1368
  const result = await executeTool('dojo_list_wishes', body);
1340
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1369
+ sendJson(res, envelopeHttpStatus(result), result);
1341
1370
  }
1342
1371
  catch (error) {
1343
1372
  const message = error instanceof Error ? error.message : String(error);
@@ -1351,7 +1380,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1351
1380
  const body = await parseBody(req);
1352
1381
  log('HTTP: /dojo/can-graduate');
1353
1382
  const result = await executeTool('dojo_can_graduate', body);
1354
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1383
+ sendJson(res, envelopeHttpStatus(result), result);
1355
1384
  }
1356
1385
  catch (error) {
1357
1386
  const message = error instanceof Error ? error.message : String(error);
@@ -1365,7 +1394,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1365
1394
  const body = await parseBody(req);
1366
1395
  log('HTTP: /dojo/artifact');
1367
1396
  const result = await executeTool('dojo_read_artifact', body);
1368
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1397
+ sendJson(res, envelopeHttpStatus(result), result);
1369
1398
  }
1370
1399
  catch (error) {
1371
1400
  const message = error instanceof Error ? error.message : String(error);
@@ -1379,7 +1408,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1379
1408
  const body = await parseBody(req);
1380
1409
  log('HTTP: /dojo/bench');
1381
1410
  const result = await executeTool('dojo_bench', body);
1382
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1411
+ sendJson(res, envelopeHttpStatus(result), result);
1383
1412
  }
1384
1413
  catch (error) {
1385
1414
  const message = error instanceof Error ? error.message : String(error);
@@ -1396,7 +1425,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1396
1425
  const body = await parseBody(req);
1397
1426
  log('HTTP: /bench/run');
1398
1427
  const result = await executeTool('decibel_bench', body);
1399
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1428
+ sendJson(res, envelopeHttpStatus(result), result);
1400
1429
  }
1401
1430
  catch (error) {
1402
1431
  const message = error instanceof Error ? error.message : String(error);
@@ -1410,7 +1439,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1410
1439
  const body = await parseBody(req);
1411
1440
  log('HTTP: /bench/compare');
1412
1441
  const result = await executeTool('decibel_bench_compare', body);
1413
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1442
+ sendJson(res, envelopeHttpStatus(result), result);
1414
1443
  }
1415
1444
  catch (error) {
1416
1445
  const message = error instanceof Error ? error.message : String(error);
@@ -1427,7 +1456,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1427
1456
  const body = await parseBody(req);
1428
1457
  log('HTTP: /context/refresh');
1429
1458
  const result = await executeTool('decibel_context_refresh', body);
1430
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1459
+ sendJson(res, envelopeHttpStatus(result), result);
1431
1460
  }
1432
1461
  catch (error) {
1433
1462
  const message = error instanceof Error ? error.message : String(error);
@@ -1441,7 +1470,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1441
1470
  const body = await parseBody(req);
1442
1471
  log('HTTP: /context/pin');
1443
1472
  const result = await executeTool('decibel_context_pin', body);
1444
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1473
+ sendJson(res, envelopeHttpStatus(result), result);
1445
1474
  }
1446
1475
  catch (error) {
1447
1476
  const message = error instanceof Error ? error.message : String(error);
@@ -1455,7 +1484,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1455
1484
  const body = await parseBody(req);
1456
1485
  log('HTTP: /context/unpin');
1457
1486
  const result = await executeTool('decibel_context_unpin', body);
1458
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1487
+ sendJson(res, envelopeHttpStatus(result), result);
1459
1488
  }
1460
1489
  catch (error) {
1461
1490
  const message = error instanceof Error ? error.message : String(error);
@@ -1475,7 +1504,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1475
1504
  }
1476
1505
  log('HTTP: /context/list');
1477
1506
  const result = await executeTool('decibel_context_list', body);
1478
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1507
+ sendJson(res, envelopeHttpStatus(result), result);
1479
1508
  }
1480
1509
  catch (error) {
1481
1510
  const message = error instanceof Error ? error.message : String(error);
@@ -1489,7 +1518,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1489
1518
  const body = await parseBody(req);
1490
1519
  log('HTTP: /event/append');
1491
1520
  const result = await executeTool('decibel_event_append', body);
1492
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1521
+ sendJson(res, envelopeHttpStatus(result), result);
1493
1522
  }
1494
1523
  catch (error) {
1495
1524
  const message = error instanceof Error ? error.message : String(error);
@@ -1517,7 +1546,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1517
1546
  }
1518
1547
  log('HTTP: /event/search');
1519
1548
  const result = await executeTool('decibel_event_search', body);
1520
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1549
+ sendJson(res, envelopeHttpStatus(result), result);
1521
1550
  }
1522
1551
  catch (error) {
1523
1552
  const message = error instanceof Error ? error.message : String(error);
@@ -1531,7 +1560,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1531
1560
  const body = await parseBody(req);
1532
1561
  log('HTTP: /artifact/list');
1533
1562
  const result = await executeTool('decibel_artifact_list', body);
1534
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1563
+ sendJson(res, envelopeHttpStatus(result), result);
1535
1564
  }
1536
1565
  catch (error) {
1537
1566
  const message = error instanceof Error ? error.message : String(error);
@@ -1545,7 +1574,7 @@ export async function startHttpServer(server, kernelInstance, options) {
1545
1574
  const body = await parseBody(req);
1546
1575
  log('HTTP: /artifact/read');
1547
1576
  const result = await executeTool('decibel_artifact_read', body);
1548
- sendJson(res, result.status === 'error' ? 400 : 200, result);
1577
+ sendJson(res, envelopeHttpStatus(result), result);
1549
1578
  }
1550
1579
  catch (error) {
1551
1580
  const message = error instanceof Error ? error.message : String(error);
@@ -2203,36 +2232,8 @@ ${authToken ? '║ Auth: Bearer token required
2203
2232
  },
2204
2233
  };
2205
2234
  }
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
- }
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';
2238
2239
  //# sourceMappingURL=httpServer.js.map