@namzu/sdk 1.0.0 → 1.2.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 (330) hide show
  1. package/CHANGELOG.md +253 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +5 -0
  4. package/dist/agents/ReactiveAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts +22 -1
  6. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  7. package/dist/agents/SupervisorAgent.js +47 -14
  8. package/dist/agents/SupervisorAgent.js.map +1 -1
  9. package/dist/agents/__tests__/supervisor-ledger.test.d.ts +2 -0
  10. package/dist/agents/__tests__/supervisor-ledger.test.d.ts.map +1 -0
  11. package/dist/agents/__tests__/supervisor-ledger.test.js +131 -0
  12. package/dist/agents/__tests__/supervisor-ledger.test.js.map +1 -0
  13. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  14. package/dist/bridge/a2a/mapper.js +2 -0
  15. package/dist/bridge/a2a/mapper.js.map +1 -1
  16. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  17. package/dist/bridge/sse/mapper.js +4 -0
  18. package/dist/bridge/sse/mapper.js.map +1 -1
  19. package/dist/config/runtime.d.ts +24 -0
  20. package/dist/config/runtime.d.ts.map +1 -1
  21. package/dist/config/runtime.js +9 -0
  22. package/dist/config/runtime.js.map +1 -1
  23. package/dist/connector/builtins/webhook.test.js +17 -0
  24. package/dist/connector/builtins/webhook.test.js.map +1 -1
  25. package/dist/connector/index.d.ts +1 -0
  26. package/dist/connector/index.d.ts.map +1 -1
  27. package/dist/connector/index.js +1 -0
  28. package/dist/connector/index.js.map +1 -1
  29. package/dist/connector/mcp/client.d.ts.map +1 -1
  30. package/dist/connector/mcp/client.js +4 -0
  31. package/dist/connector/mcp/client.js.map +1 -1
  32. package/dist/connector/mcp/index.d.ts +1 -0
  33. package/dist/connector/mcp/index.d.ts.map +1 -1
  34. package/dist/connector/mcp/index.js +1 -0
  35. package/dist/connector/mcp/index.js.map +1 -1
  36. package/dist/connector/mcp/streamable-http.d.ts +22 -0
  37. package/dist/connector/mcp/streamable-http.d.ts.map +1 -0
  38. package/dist/connector/mcp/streamable-http.js +128 -0
  39. package/dist/connector/mcp/streamable-http.js.map +1 -0
  40. package/dist/connector/mcp/streamable-http.test.d.ts +2 -0
  41. package/dist/connector/mcp/streamable-http.test.d.ts.map +1 -0
  42. package/dist/connector/mcp/streamable-http.test.js +132 -0
  43. package/dist/connector/mcp/streamable-http.test.js.map +1 -0
  44. package/dist/{runtime/query → constants}/continuation.d.ts +5 -0
  45. package/dist/constants/continuation.d.ts.map +1 -0
  46. package/dist/{runtime/query → constants}/continuation.js +5 -0
  47. package/dist/constants/continuation.js.map +1 -0
  48. package/dist/constants/index.d.ts +1 -0
  49. package/dist/constants/index.d.ts.map +1 -1
  50. package/dist/constants/index.js +1 -0
  51. package/dist/constants/index.js.map +1 -1
  52. package/dist/manager/run/persistence.d.ts +11 -0
  53. package/dist/manager/run/persistence.d.ts.map +1 -1
  54. package/dist/manager/run/persistence.js +30 -1
  55. package/dist/manager/run/persistence.js.map +1 -1
  56. package/dist/persona/assembler.d.ts.map +1 -1
  57. package/dist/persona/assembler.js +18 -9
  58. package/dist/persona/assembler.js.map +1 -1
  59. package/dist/provider/capabilities.d.ts +27 -0
  60. package/dist/provider/capabilities.d.ts.map +1 -0
  61. package/dist/provider/capabilities.js +29 -0
  62. package/dist/provider/capabilities.js.map +1 -0
  63. package/dist/provider/collect.d.ts.map +1 -1
  64. package/dist/provider/collect.js +4 -1
  65. package/dist/provider/collect.js.map +1 -1
  66. package/dist/provider/index.d.ts +2 -0
  67. package/dist/provider/index.d.ts.map +1 -1
  68. package/dist/provider/index.js +1 -0
  69. package/dist/provider/index.js.map +1 -1
  70. package/dist/public-runtime.d.ts +4 -3
  71. package/dist/public-runtime.d.ts.map +1 -1
  72. package/dist/public-runtime.js +3 -3
  73. package/dist/public-runtime.js.map +1 -1
  74. package/dist/registry/tool/execute.d.ts +27 -0
  75. package/dist/registry/tool/execute.d.ts.map +1 -1
  76. package/dist/registry/tool/execute.js +124 -21
  77. package/dist/registry/tool/execute.js.map +1 -1
  78. package/dist/registry/tool/execute.test.d.ts +9 -6
  79. package/dist/registry/tool/execute.test.d.ts.map +1 -1
  80. package/dist/registry/tool/execute.test.js +72 -10
  81. package/dist/registry/tool/execute.test.js.map +1 -1
  82. package/dist/registry/toolset/catalog.test.js +62 -0
  83. package/dist/registry/toolset/catalog.test.js.map +1 -1
  84. package/dist/run/reporter.d.ts.map +1 -1
  85. package/dist/run/reporter.js +8 -0
  86. package/dist/run/reporter.js.map +1 -1
  87. package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts +2 -0
  88. package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts.map +1 -0
  89. package/dist/runtime/query/__tests__/capability-negotiation.test.js +184 -0
  90. package/dist/runtime/query/__tests__/capability-negotiation.test.js.map +1 -0
  91. package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts +2 -0
  92. package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts.map +1 -0
  93. package/dist/runtime/query/__tests__/checkpoint-store.test.js +259 -0
  94. package/dist/runtime/query/__tests__/checkpoint-store.test.js.map +1 -0
  95. package/dist/runtime/query/__tests__/checkpoint.test.js +9 -3
  96. package/dist/runtime/query/__tests__/checkpoint.test.js.map +1 -1
  97. package/dist/runtime/query/__tests__/deferred-tools.test.js +43 -5
  98. package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -1
  99. package/dist/runtime/query/__tests__/long-document-flow.test.d.ts +2 -0
  100. package/dist/runtime/query/__tests__/long-document-flow.test.d.ts.map +1 -0
  101. package/dist/runtime/query/__tests__/long-document-flow.test.js +154 -0
  102. package/dist/runtime/query/__tests__/long-document-flow.test.js.map +1 -0
  103. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts +2 -0
  104. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts.map +1 -0
  105. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js +110 -0
  106. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js.map +1 -0
  107. package/dist/runtime/query/__tests__/prompt.test.js +15 -11
  108. package/dist/runtime/query/__tests__/prompt.test.js.map +1 -1
  109. package/dist/runtime/query/checkpoint.d.ts +16 -2
  110. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  111. package/dist/runtime/query/checkpoint.js +21 -7
  112. package/dist/runtime/query/checkpoint.js.map +1 -1
  113. package/dist/runtime/query/context.d.ts +7 -0
  114. package/dist/runtime/query/context.d.ts.map +1 -1
  115. package/dist/runtime/query/context.js +1 -0
  116. package/dist/runtime/query/context.js.map +1 -1
  117. package/dist/runtime/query/index.d.ts +29 -0
  118. package/dist/runtime/query/index.d.ts.map +1 -1
  119. package/dist/runtime/query/index.js +98 -8
  120. package/dist/runtime/query/index.js.map +1 -1
  121. package/dist/runtime/query/iteration/index.d.ts +3 -55
  122. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  123. package/dist/runtime/query/iteration/index.js +260 -656
  124. package/dist/runtime/query/iteration/index.js.map +1 -1
  125. package/dist/runtime/query/iteration/phases/checkpoint.d.ts.map +1 -1
  126. package/dist/runtime/query/iteration/phases/checkpoint.js +24 -2
  127. package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
  128. package/dist/runtime/query/iteration/phases/compaction.d.ts +8 -0
  129. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  130. package/dist/runtime/query/iteration/phases/compaction.js +84 -3
  131. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  132. package/dist/runtime/query/iteration/phases/context.d.ts +13 -2
  133. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  134. package/dist/runtime/query/iteration/phases/context.js +53 -0
  135. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  136. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts +2 -0
  137. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts.map +1 -0
  138. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js +73 -0
  139. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js.map +1 -0
  140. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts +2 -0
  141. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts.map +1 -0
  142. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js +120 -0
  143. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js.map +1 -0
  144. package/dist/runtime/query/iteration/phases/tool-review.d.ts +1 -1
  145. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  146. package/dist/runtime/query/iteration/phases/tool-review.js +7 -2
  147. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  148. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts +19 -0
  149. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts.map +1 -0
  150. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js +177 -0
  151. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js.map +1 -0
  152. package/dist/runtime/query/iteration/phases/working-memory.d.ts +31 -0
  153. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -0
  154. package/dist/runtime/query/iteration/phases/working-memory.js +81 -0
  155. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -0
  156. package/dist/runtime/query/iteration/stream-turn.d.ts +35 -0
  157. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -0
  158. package/dist/runtime/query/iteration/stream-turn.js +341 -0
  159. package/dist/runtime/query/iteration/stream-turn.js.map +1 -0
  160. package/dist/runtime/query/replay/list.d.ts +17 -1
  161. package/dist/runtime/query/replay/list.d.ts.map +1 -1
  162. package/dist/runtime/query/replay/list.js +21 -4
  163. package/dist/runtime/query/replay/list.js.map +1 -1
  164. package/dist/runtime/query/replay/prepare.d.ts +10 -0
  165. package/dist/runtime/query/replay/prepare.d.ts.map +1 -1
  166. package/dist/runtime/query/replay/prepare.js +20 -4
  167. package/dist/runtime/query/replay/prepare.js.map +1 -1
  168. package/dist/session/workspace/__tests__/shared-run.test.js +1 -1
  169. package/dist/session/workspace/__tests__/shared-run.test.js.map +1 -1
  170. package/dist/session/workspace/shared-run.js +1 -0
  171. package/dist/session/workspace/shared-run.js.map +1 -1
  172. package/dist/skills/loader.test.js +8 -8
  173. package/dist/skills/loader.test.js.map +1 -1
  174. package/dist/store/index.d.ts +1 -0
  175. package/dist/store/index.d.ts.map +1 -1
  176. package/dist/store/index.js +1 -0
  177. package/dist/store/index.js.map +1 -1
  178. package/dist/store/run/checkpoint-disk.d.ts +28 -0
  179. package/dist/store/run/checkpoint-disk.d.ts.map +1 -0
  180. package/dist/store/run/checkpoint-disk.js +55 -0
  181. package/dist/store/run/checkpoint-disk.js.map +1 -0
  182. package/dist/streaming/coalesce.test.js.map +1 -1
  183. package/dist/tools/builtins/__tests__/payload-budget.test.js +48 -0
  184. package/dist/tools/builtins/__tests__/payload-budget.test.js.map +1 -1
  185. package/dist/tools/builtins/__tests__/read-file.test.js +9 -0
  186. package/dist/tools/builtins/__tests__/read-file.test.js.map +1 -1
  187. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  188. package/dist/tools/builtins/read-file.js +50 -2
  189. package/dist/tools/builtins/read-file.js.map +1 -1
  190. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  191. package/dist/tools/builtins/search-tools.js +32 -7
  192. package/dist/tools/builtins/search-tools.js.map +1 -1
  193. package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts +2 -0
  194. package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts.map +1 -0
  195. package/dist/tools/coordinator/__tests__/approve-plan.test.js +114 -0
  196. package/dist/tools/coordinator/__tests__/approve-plan.test.js.map +1 -0
  197. package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts +2 -0
  198. package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts.map +1 -0
  199. package/dist/tools/coordinator/__tests__/ask-user-question.test.js +309 -0
  200. package/dist/tools/coordinator/__tests__/ask-user-question.test.js.map +1 -0
  201. package/dist/tools/coordinator/index.d.ts +8 -0
  202. package/dist/tools/coordinator/index.d.ts.map +1 -1
  203. package/dist/tools/coordinator/index.js +195 -15
  204. package/dist/tools/coordinator/index.js.map +1 -1
  205. package/dist/types/agent/index.d.ts +1 -0
  206. package/dist/types/agent/index.d.ts.map +1 -1
  207. package/dist/types/agent/index.js +1 -0
  208. package/dist/types/agent/index.js.map +1 -1
  209. package/dist/types/agent/reactive.d.ts +15 -0
  210. package/dist/types/agent/reactive.d.ts.map +1 -1
  211. package/dist/types/agent/supervisor.d.ts +18 -0
  212. package/dist/types/agent/supervisor.d.ts.map +1 -1
  213. package/dist/types/agent/working-memory.d.ts +29 -0
  214. package/dist/types/agent/working-memory.d.ts.map +1 -0
  215. package/dist/types/agent/working-memory.js +2 -0
  216. package/dist/types/agent/working-memory.js.map +1 -0
  217. package/dist/types/common/index.d.ts +10 -0
  218. package/dist/types/common/index.d.ts.map +1 -1
  219. package/dist/types/common/index.js +18 -0
  220. package/dist/types/common/index.js.map +1 -1
  221. package/dist/types/common/usage-merge.test.d.ts +2 -0
  222. package/dist/types/common/usage-merge.test.d.ts.map +1 -0
  223. package/dist/types/common/usage-merge.test.js +48 -0
  224. package/dist/types/common/usage-merge.test.js.map +1 -0
  225. package/dist/types/connector/mcp.d.ts +9 -2
  226. package/dist/types/connector/mcp.d.ts.map +1 -1
  227. package/dist/types/hitl/index.d.ts +42 -0
  228. package/dist/types/hitl/index.d.ts.map +1 -1
  229. package/dist/types/hitl/index.js +11 -0
  230. package/dist/types/hitl/index.js.map +1 -1
  231. package/dist/types/provider/chat.d.ts +9 -0
  232. package/dist/types/provider/chat.d.ts.map +1 -1
  233. package/dist/types/provider/config.d.ts +18 -0
  234. package/dist/types/provider/config.d.ts.map +1 -1
  235. package/dist/types/provider/interface.d.ts +10 -0
  236. package/dist/types/provider/interface.d.ts.map +1 -1
  237. package/dist/types/run/checkpoint-store.d.ts +64 -0
  238. package/dist/types/run/checkpoint-store.d.ts.map +1 -0
  239. package/dist/types/run/checkpoint-store.js +12 -0
  240. package/dist/types/run/checkpoint-store.js.map +1 -0
  241. package/dist/types/run/config.d.ts +27 -0
  242. package/dist/types/run/config.d.ts.map +1 -1
  243. package/dist/types/run/events.d.ts +6 -0
  244. package/dist/types/run/events.d.ts.map +1 -1
  245. package/dist/types/run/events.js.map +1 -1
  246. package/dist/types/run/index.d.ts +1 -0
  247. package/dist/types/run/index.d.ts.map +1 -1
  248. package/dist/types/run/index.js +1 -0
  249. package/dist/types/run/index.js.map +1 -1
  250. package/dist/version.d.ts.map +1 -1
  251. package/dist/version.js +25 -4
  252. package/dist/version.js.map +1 -1
  253. package/package.json +1 -1
  254. package/src/agents/ReactiveAgent.ts +5 -0
  255. package/src/agents/SupervisorAgent.ts +56 -16
  256. package/src/agents/__tests__/supervisor-ledger.test.ts +185 -0
  257. package/src/bridge/a2a/mapper.ts +3 -0
  258. package/src/bridge/sse/mapper.ts +5 -0
  259. package/src/config/runtime.ts +9 -0
  260. package/src/connector/builtins/webhook.test.ts +21 -0
  261. package/src/connector/index.ts +1 -0
  262. package/src/connector/mcp/client.ts +4 -0
  263. package/src/connector/mcp/index.ts +1 -0
  264. package/src/connector/mcp/streamable-http.test.ts +169 -0
  265. package/src/connector/mcp/streamable-http.ts +152 -0
  266. package/src/{runtime/query → constants}/continuation.ts +5 -0
  267. package/src/constants/index.ts +1 -0
  268. package/src/manager/run/persistence.ts +34 -1
  269. package/src/persona/assembler.ts +18 -6
  270. package/src/provider/capabilities.ts +47 -0
  271. package/src/provider/collect.ts +4 -1
  272. package/src/provider/index.ts +5 -0
  273. package/src/public-runtime.ts +5 -0
  274. package/src/registry/tool/execute.test.ts +85 -10
  275. package/src/registry/tool/execute.ts +124 -21
  276. package/src/registry/toolset/catalog.test.ts +68 -0
  277. package/src/run/reporter.ts +9 -0
  278. package/src/runtime/query/__tests__/capability-negotiation.test.ts +248 -0
  279. package/src/runtime/query/__tests__/checkpoint-store.test.ts +322 -0
  280. package/src/runtime/query/__tests__/checkpoint.test.ts +15 -6
  281. package/src/runtime/query/__tests__/deferred-tools.test.ts +52 -5
  282. package/src/runtime/query/__tests__/long-document-flow.test.ts +180 -0
  283. package/src/runtime/query/__tests__/orphan-task-turn-end.test.ts +127 -0
  284. package/src/runtime/query/__tests__/prompt.test.ts +15 -11
  285. package/src/runtime/query/checkpoint.ts +23 -9
  286. package/src/runtime/query/context.ts +9 -0
  287. package/src/runtime/query/index.ts +148 -29
  288. package/src/runtime/query/iteration/index.ts +316 -831
  289. package/src/runtime/query/iteration/phases/checkpoint.ts +32 -2
  290. package/src/runtime/query/iteration/phases/compaction.ts +89 -3
  291. package/src/runtime/query/iteration/phases/context.ts +59 -3
  292. package/src/runtime/query/iteration/phases/hitl-abort-race.test.ts +86 -0
  293. package/src/runtime/query/iteration/phases/hitl-answer-question.test.ts +139 -0
  294. package/src/runtime/query/iteration/phases/tool-review.ts +7 -3
  295. package/src/runtime/query/iteration/phases/working-memory-compaction.test.ts +215 -0
  296. package/src/runtime/query/iteration/phases/working-memory.ts +86 -0
  297. package/src/runtime/query/iteration/stream-turn.ts +396 -0
  298. package/src/runtime/query/replay/list.ts +38 -4
  299. package/src/runtime/query/replay/prepare.ts +37 -4
  300. package/src/session/workspace/__tests__/shared-run.test.ts +1 -1
  301. package/src/session/workspace/shared-run.ts +1 -0
  302. package/src/skills/loader.test.ts +8 -8
  303. package/src/store/index.ts +1 -0
  304. package/src/store/run/checkpoint-disk.ts +67 -0
  305. package/src/streaming/coalesce.test.ts +3 -3
  306. package/src/tools/builtins/__tests__/payload-budget.test.ts +63 -0
  307. package/src/tools/builtins/__tests__/read-file.test.ts +12 -0
  308. package/src/tools/builtins/read-file.ts +50 -2
  309. package/src/tools/builtins/search-tools.ts +38 -7
  310. package/src/tools/coordinator/__tests__/approve-plan.test.ts +139 -0
  311. package/src/tools/coordinator/__tests__/ask-user-question.test.ts +370 -0
  312. package/src/tools/coordinator/index.ts +227 -17
  313. package/src/types/agent/index.ts +1 -0
  314. package/src/types/agent/reactive.ts +17 -0
  315. package/src/types/agent/supervisor.ts +20 -0
  316. package/src/types/agent/working-memory.ts +29 -0
  317. package/src/types/common/index.ts +19 -0
  318. package/src/types/common/usage-merge.test.ts +53 -0
  319. package/src/types/connector/mcp.ts +14 -2
  320. package/src/types/hitl/index.ts +52 -1
  321. package/src/types/provider/chat.ts +10 -0
  322. package/src/types/provider/config.ts +18 -0
  323. package/src/types/provider/interface.ts +11 -0
  324. package/src/types/run/checkpoint-store.ts +72 -0
  325. package/src/types/run/config.ts +30 -0
  326. package/src/types/run/events.ts +13 -0
  327. package/src/types/run/index.ts +1 -0
  328. package/src/version.ts +26 -4
  329. package/dist/runtime/query/continuation.d.ts.map +0 -1
  330. package/dist/runtime/query/continuation.js.map +0 -1
@@ -19,7 +19,35 @@ export type { ToolExecutionResult }
19
19
  // Tokens too generic to identify a tool by name — ignored when matching a
20
20
  // batched `search_tools` query so they can't activate the whole catalog
21
21
  // (every bridged tool name shares the `clawtool` prefix, for instance).
22
- const SEARCH_STOP_TOKENS = new Set(['clawtool', 'tool', 'tools', 'mcp', 'the', 'and', 'for', 'use'])
22
+ // Generic CRUD verbs are stopped too: a query like "list deals" must rank
23
+ // by "deals", not token-match every `list_*` tool in the deferred catalog.
24
+ const SEARCH_STOP_TOKENS = new Set([
25
+ 'clawtool',
26
+ 'tool',
27
+ 'tools',
28
+ 'mcp',
29
+ 'the',
30
+ 'and',
31
+ 'for',
32
+ 'use',
33
+ 'list',
34
+ 'read',
35
+ 'create',
36
+ 'update',
37
+ 'get',
38
+ 'find',
39
+ 'delete',
40
+ 'search',
41
+ ])
42
+
43
+ // Weighted-scoring weights mirroring ToolCatalog.searchTools (the richer,
44
+ // otherwise-unused catalog scorer): exact name 12, name substring 8,
45
+ // description 5 — extended here with argument-name indexing (3), following
46
+ // Anthropic's tool-search practice of searching argument names too.
47
+ const SEARCH_WEIGHT_NAME_EXACT = 12
48
+ const SEARCH_WEIGHT_NAME_PARTIAL = 8
49
+ const SEARCH_WEIGHT_DESCRIPTION = 5
50
+ const SEARCH_WEIGHT_ARGUMENT = 3
23
51
 
24
52
  export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
25
53
  private availability: Map<string, ToolAvailability> = new Map()
@@ -118,28 +146,58 @@ export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
118
146
  return this.availability.get(name) ?? 'active'
119
147
  }
120
148
 
149
+ /**
150
+ * Ranked lexical search over DEFERRED tools, score-descending (ties broken
151
+ * by name) so callers can cap activation at a top-k. Each meaningful query
152
+ * term (≥3 chars, not a stop token) is scored against the tool name
153
+ * (exact/substring), description, and argument names; only tools with a
154
+ * positive score are returned. Description matching is safe here precisely
155
+ * because the result is RANKED — the `search_tools` builtin activates only
156
+ * the top slice, so a shared word can no longer drag in the whole catalog.
157
+ *
158
+ * PARKED (phase 5 of the tool-loading plan): an embedding-backed semantic
159
+ * upgrade was evaluated and deliberately NOT built — at ≤~35 deferred
160
+ * in-house tools with distinct names, weighted lexical scoring sits inside
161
+ * the literature's safe zone, and a weak retriever underperforms no
162
+ * retriever at all. Revisit only when (a) the deferred catalog grows past
163
+ * ~75-100 tools (realistic driver: connector-MCP growth), or (b) telemetry
164
+ * shows a search_tools miss-rate above ~10%. Sticky activation is also
165
+ * deliberate: activating inserts the schema into the tools array at its
166
+ * registry position (a one-time prompt-cache prefix bust); re-defer/TTL
167
+ * would churn that prefix repeatedly and is rejected.
168
+ */
121
169
  searchDeferred(query: string): ToolDefinition[] {
122
170
  const q = query.toLowerCase().trim()
123
171
  if (q.length === 0) return []
124
- // Per-token matching exists only so a batched query naming several tools
125
- // at once ("A2aCard PeerRegister PeerList") activates each. Restrict it
126
- // to the tool NAME and drop short/generic tokens — matching tokens
127
- // against descriptions (or letting a shared word like "clawtool"/"list"
128
- // through) would activate the whole catalog and defeat deferral.
129
- const tokens = q.split(/\s+/).filter((tok) => tok.length >= 3 && !SEARCH_STOP_TOKENS.has(tok))
130
- // A bare generic token ("clawtool") identifies nothing specific — skip the
131
- // broad whole-query match for it so it can't activate the whole catalog.
132
- const wholeQueryUseful = q.length >= 3 && !SEARCH_STOP_TOKENS.has(q)
133
- return this.getByAvailability(['deferred']).filter((t) => {
134
- const name = t.name.toLowerCase()
135
- // Whole-query match (single-term capability search) against name or
136
- // description the deliberate, narrow behaviour.
137
- if (wholeQueryUseful && (name.includes(q) || t.description.toLowerCase().includes(q))) {
138
- return true
172
+ const terms = q.split(/\s+/).filter((tok) => tok.length >= 3 && !SEARCH_STOP_TOKENS.has(tok))
173
+ if (terms.length === 0) return []
174
+
175
+ const scored: Array<{ tool: ToolDefinition; score: number }> = []
176
+ for (const tool of this.getByAvailability(['deferred'])) {
177
+ const name = tool.name.toLowerCase()
178
+ const description = tool.description.toLowerCase()
179
+ const argumentNames = listArgumentNames(tool)
180
+ let score = 0
181
+ for (const term of terms) {
182
+ if (name === term) {
183
+ score += SEARCH_WEIGHT_NAME_EXACT
184
+ } else if (name.includes(term)) {
185
+ score += SEARCH_WEIGHT_NAME_PARTIAL
186
+ }
187
+ if (description.includes(term)) {
188
+ score += SEARCH_WEIGHT_DESCRIPTION
189
+ }
190
+ if (argumentNames.some((arg) => arg.includes(term))) {
191
+ score += SEARCH_WEIGHT_ARGUMENT
192
+ }
139
193
  }
140
- // Batched multi-name query: any meaningful token, name only.
141
- return tokens.some((tok) => name.includes(tok))
142
- })
194
+ if (score > 0) {
195
+ scored.push({ tool, score })
196
+ }
197
+ }
198
+
199
+ scored.sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name))
200
+ return scored.map((entry) => entry.tool)
143
201
  }
144
202
 
145
203
  assignTiers(mapping: Record<string, string>): void {
@@ -176,12 +234,24 @@ Executable tool names, descriptions, and JSON input schemas are attached through
176
234
  </tool_runtime_contract>`
177
235
 
178
236
  if (active.length > 0) {
179
- const entries = active.map((t) => `- ${t.name}: ${t.description}`).join('\n')
237
+ // Name-only: every active tool's full description + JSON schema
238
+ // already rides the runtime tools parameter on each request —
239
+ // repeating descriptions here double-bills the same tokens.
240
+ const entries = active.map((t) => `- ${t.name}`).join('\n')
180
241
  parts.push(`<available_tools>\n${entries}\n</available_tools>`)
181
242
  }
182
243
 
183
244
  if (deferred.length > 0) {
184
- const entries = deferred.map((t) => `- ${t.name}`).join('\n')
245
+ // Name + one-line hint: deferred schemas stay off the wire, so the
246
+ // hint is the model's only signal of what a deferred tool does. A
247
+ // bare name list caused a real discovery failure in production
248
+ // (the agent never found read_document behind search_tools).
249
+ const entries = deferred
250
+ .map((t) => {
251
+ const hint = toolDiscoveryHint(t.description)
252
+ return hint.length > 0 ? `- ${t.name}: ${hint}` : `- ${t.name}`
253
+ })
254
+ .join('\n')
185
255
  const deferredIntro =
186
256
  this.has('search_tools') && this.getAvailability('search_tools') === 'active'
187
257
  ? 'Use search_tools to load these before use:'
@@ -372,6 +442,39 @@ Executable tool names, descriptions, and JSON input schemas are attached through
372
442
  }
373
443
  }
374
444
 
445
+ /**
446
+ * One-line discoverability hint for a deferred tool: the first sentence of
447
+ * its description, capped at ~100 chars. Used for the `<deferred_tools>`
448
+ * prompt listing and for `search_tools` near-miss suggestions, where the
449
+ * full description would re-import the token weight deferral avoids.
450
+ */
451
+ export function toolDiscoveryHint(description: string, maxLength = 100): string {
452
+ const normalized = description.trim().replace(/\s+/g, ' ')
453
+ if (normalized.length === 0) return ''
454
+ const sentenceMatch = normalized.match(/^.*?[.!?](?=\s|$)/)
455
+ const sentence = sentenceMatch ? sentenceMatch[0] : normalized
456
+ if (sentence.length <= maxLength) return sentence
457
+ return `${sentence.slice(0, maxLength - 1).trimEnd()}…`
458
+ }
459
+
460
+ /**
461
+ * Lower-cased argument (property) names of a tool's input schema, used by
462
+ * `searchDeferred` ranking. Walks the JSON-Schema rendering (already a
463
+ * registration dependency) instead of Zod internals; opaque schemas simply
464
+ * contribute no argument matches.
465
+ */
466
+ function listArgumentNames(tool: ToolDefinition): string[] {
467
+ try {
468
+ const json = zodToJsonSchema(tool.inputSchema, {
469
+ target: 'jsonSchema7',
470
+ $refStrategy: 'none',
471
+ }) as { properties?: Record<string, unknown> }
472
+ return Object.keys(json.properties ?? {}).map((key) => key.toLowerCase())
473
+ } catch {
474
+ return []
475
+ }
476
+ }
477
+
375
478
  /**
376
479
  * Build a one-sentence "Required: <field>: <type>, <field>: <type>"
377
480
  * hint from a Zod schema, used to enrich tool-input validation
@@ -81,6 +81,74 @@ describe('ToolCatalog', () => {
81
81
  expect(catalog.toLLMTools().map((t) => t.function.name)).toEqual(['bash'])
82
82
  })
83
83
 
84
+ it('scores and reports the matched facets: partial name, source text, toolset text', () => {
85
+ const catalog = new ToolCatalog()
86
+ catalog.registerSource({
87
+ id: 'host',
88
+ kind: 'host_tool',
89
+ name: 'GitHub',
90
+ description: 'repository automation',
91
+ })
92
+ catalog.registerToolset({
93
+ id: 'issues',
94
+ sourceId: 'host',
95
+ name: 'Issue toolset',
96
+ defaultPolicy: { enabled: true, loading: 'eager' },
97
+ })
98
+ catalog.registerTool({
99
+ name: 'create_issue',
100
+ description: 'open a ticket',
101
+ sourceId: 'host',
102
+ toolsetId: 'issues',
103
+ policy: { enabled: true, loading: 'eager' },
104
+ })
105
+
106
+ // 'issue' hits the tool NAME (create_issue, partial) AND the TOOLSET name.
107
+ const byIssue = catalog.searchTools('issue')
108
+ expect(byIssue).toHaveLength(1)
109
+ expect([...(byIssue[0]?.matched ?? [])].sort()).toEqual(['name', 'toolset'])
110
+
111
+ // 'github' hits only the SOURCE text.
112
+ const bySource = catalog.searchTools('github')
113
+ expect(bySource[0]?.matched).toEqual(['source'])
114
+
115
+ // An exact name match outscores a partial one.
116
+ const exact = catalog.searchTools('create_issue')
117
+ expect(exact[0]?.matched).toContain('name')
118
+ expect(exact[0]?.score ?? 0).toBeGreaterThan(byIssue[0]?.score ?? 0)
119
+ })
120
+
121
+ it('adds the preferred boost so a preferred tool outranks an equal match', () => {
122
+ const catalog = new ToolCatalog()
123
+ catalog.registerSource({ id: 'host', kind: 'host_tool', name: 'Host' })
124
+ catalog.registerToolset({
125
+ id: 'ts',
126
+ sourceId: 'host',
127
+ name: 'Tools',
128
+ defaultPolicy: { enabled: true, loading: 'eager' },
129
+ })
130
+ catalog.registerTool({
131
+ name: 'search_plain',
132
+ description: 'search things',
133
+ sourceId: 'host',
134
+ toolsetId: 'ts',
135
+ policy: { enabled: true, loading: 'eager' },
136
+ })
137
+ catalog.registerTool({
138
+ name: 'search_pref',
139
+ description: 'search things',
140
+ sourceId: 'host',
141
+ toolsetId: 'ts',
142
+ policy: { enabled: true, loading: 'eager', preferred: true },
143
+ })
144
+
145
+ const results = catalog.searchTools('search')
146
+ expect(results.map((r) => r.tool.name)).toEqual(['search_pref', 'search_plain'])
147
+ const pref = results.find((r) => r.tool.name === 'search_pref')?.score ?? 0
148
+ const plain = results.find((r) => r.tool.name === 'search_plain')?.score ?? 0
149
+ expect(pref - plain).toBe(1)
150
+ })
151
+
84
152
  it('preserves registry availability as catalog loading policy', () => {
85
153
  const registry = new ToolRegistry()
86
154
  registry.register(makeTool('read_file'))
@@ -225,6 +225,15 @@ export function createRunReporter(parentLogger?: Logger): RunReporter {
225
225
  })
226
226
  break
227
227
 
228
+ case 'capability_warning':
229
+ log.warn('Provider capability mismatch', {
230
+ runId: event.runId,
231
+ capability: event.capability,
232
+ providerId: event.providerId,
233
+ message: event.message,
234
+ })
235
+ break
236
+
228
237
  default: {
229
238
  const _exhaustive: never = event
230
239
  throw new Error(`Unhandled run event type: ${(_exhaustive as RunEvent).type}`)
@@ -0,0 +1,248 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises'
2
+ import { tmpdir } from 'node:os'
3
+ import { join } from 'node:path'
4
+ import { afterEach, describe, expect, it } from 'vitest'
5
+ import { z } from 'zod'
6
+
7
+ import {
8
+ PERMISSIVE_PROVIDER_CAPABILITIES,
9
+ resolveProviderCapabilities,
10
+ } from '../../../provider/capabilities.js'
11
+ import { ToolRegistry } from '../../../registry/tool/execute.js'
12
+ import type { SessionId, TenantId } from '../../../types/ids/index.js'
13
+ import { createUserMessage } from '../../../types/message/index.js'
14
+ import type {
15
+ ChatCompletionParams,
16
+ LLMProvider,
17
+ ProviderCapabilities,
18
+ StreamChunk,
19
+ } from '../../../types/provider/index.js'
20
+ import type { RunEvent } from '../../../types/run/index.js'
21
+ import type { ProjectId, ThreadId } from '../../../types/session/ids.js'
22
+ import { drainQuery } from '../index.js'
23
+
24
+ const ZERO_USAGE = {
25
+ promptTokens: 0,
26
+ completionTokens: 0,
27
+ totalTokens: 0,
28
+ cachedTokens: 0,
29
+ cacheWriteTokens: 0,
30
+ }
31
+
32
+ class CapturingProvider implements LLMProvider {
33
+ readonly id = 'capturing'
34
+ readonly name = 'Capturing Provider'
35
+ readonly capabilities?: ProviderCapabilities
36
+ lastParams?: ChatCompletionParams
37
+
38
+ constructor(capabilities?: ProviderCapabilities) {
39
+ this.capabilities = capabilities
40
+ }
41
+
42
+ async *chatStream(params: ChatCompletionParams): AsyncIterable<StreamChunk> {
43
+ this.lastParams = params
44
+ yield { id: 'msg_1', delta: { content: 'done' } }
45
+ yield { id: 'msg_1', delta: {}, finishReason: 'stop', usage: ZERO_USAGE }
46
+ }
47
+ }
48
+
49
+ const NO_TOOLS_CAPABILITIES: ProviderCapabilities = {
50
+ supportsTools: false,
51
+ supportsStreaming: true,
52
+ supportsFunctionCalling: false,
53
+ supportsVision: true,
54
+ }
55
+
56
+ const NO_VISION_CAPABILITIES: ProviderCapabilities = {
57
+ supportsTools: true,
58
+ supportsStreaming: true,
59
+ supportsFunctionCalling: true,
60
+ supportsVision: false,
61
+ }
62
+
63
+ function registerEchoTool(tools: ToolRegistry): void {
64
+ tools.register({
65
+ name: 'echo',
66
+ description: 'Echo the text back.',
67
+ inputSchema: z.object({ text: z.string() }),
68
+ execute: async () => ({ success: true, output: 'ok' }),
69
+ })
70
+ }
71
+
72
+ function baseParams(provider: LLMProvider, tools: ToolRegistry, workingDirectory: string) {
73
+ return {
74
+ provider,
75
+ tools,
76
+ runConfig: {
77
+ model: 'mock-model',
78
+ timeoutMs: 5_000,
79
+ tokenBudget: 100_000,
80
+ maxIterations: 1,
81
+ maxResponseTokens: 256,
82
+ },
83
+ agentId: 'agent_test',
84
+ agentName: 'Test Agent',
85
+ workingDirectory,
86
+ sessionId: 'ses_capability' as SessionId,
87
+ threadId: 'thd_capability' as ThreadId,
88
+ projectId: 'prj_capability' as ProjectId,
89
+ tenantId: 'tnt_capability' as TenantId,
90
+ }
91
+ }
92
+
93
+ describe('resolveProviderCapabilities', () => {
94
+ it('resolves an undeclared provider to the permissive default', () => {
95
+ expect(resolveProviderCapabilities(new CapturingProvider())).toEqual(
96
+ PERMISSIVE_PROVIDER_CAPABILITIES,
97
+ )
98
+ })
99
+
100
+ it('fills a partial declaration (missing supportsVision) permissively per field', () => {
101
+ const resolved = resolveProviderCapabilities({
102
+ capabilities: {
103
+ supportsTools: false,
104
+ supportsStreaming: true,
105
+ supportsFunctionCalling: false,
106
+ },
107
+ })
108
+ expect(resolved.supportsTools).toBe(false)
109
+ expect(resolved.supportsVision).toBe(true)
110
+ })
111
+ })
112
+
113
+ describe('query() capability negotiation', () => {
114
+ let workdirs: string[] = []
115
+
116
+ afterEach(async () => {
117
+ await Promise.all(workdirs.map((dir) => rm(dir, { recursive: true, force: true })))
118
+ workdirs = []
119
+ })
120
+
121
+ async function mkWorkdir(): Promise<string> {
122
+ const dir = await mkdtemp(join(tmpdir(), 'namzu-capability-'))
123
+ workdirs.push(dir)
124
+ return dir
125
+ }
126
+
127
+ it('strips tool surfaces and emits a capability_warning for a no-tools provider', async () => {
128
+ const provider = new CapturingProvider(NO_TOOLS_CAPABILITIES)
129
+ const tools = new ToolRegistry()
130
+ registerEchoTool(tools)
131
+ const events: RunEvent[] = []
132
+
133
+ const run = await drainQuery(
134
+ {
135
+ ...baseParams(provider, tools, await mkWorkdir()),
136
+ messages: [createUserMessage('hello')],
137
+ },
138
+ (event) => {
139
+ events.push(event)
140
+ },
141
+ )
142
+
143
+ expect(run.status).toBe('completed')
144
+
145
+ // The model was never told about tools: no tools param on the wire…
146
+ expect(provider.lastParams?.tools).toBeUndefined()
147
+ // …and no tool section in the system prompt.
148
+ const systemPrompt = (provider.lastParams?.messages ?? [])
149
+ .filter((m) => m.role === 'system')
150
+ .map((m) => m.content)
151
+ .join('\n')
152
+ expect(systemPrompt).not.toContain('<available_tools>')
153
+ expect(systemPrompt).not.toContain('echo')
154
+
155
+ // The host got the machine-readable warning.
156
+ const warning = events.find(
157
+ (e): e is Extract<RunEvent, { type: 'capability_warning' }> =>
158
+ e.type === 'capability_warning',
159
+ )
160
+ expect(warning?.capability).toBe('tools')
161
+ expect(warning?.providerId).toBe('capturing')
162
+ expect(warning?.message).toContain('stripped')
163
+ })
164
+
165
+ it('keeps tool surfaces intact for an undeclared (permissive-default) provider', async () => {
166
+ const provider = new CapturingProvider()
167
+ const tools = new ToolRegistry()
168
+ registerEchoTool(tools)
169
+
170
+ const run = await drainQuery({
171
+ ...baseParams(provider, tools, await mkWorkdir()),
172
+ messages: [createUserMessage('hello')],
173
+ })
174
+
175
+ expect(run.status).toBe('completed')
176
+ expect(provider.lastParams?.tools?.map((t) => t.function.name)).toContain('echo')
177
+ })
178
+
179
+ it('emits a vision capability_warning when attachments hit a no-vision provider', async () => {
180
+ const provider = new CapturingProvider(NO_VISION_CAPABILITIES)
181
+ const events: RunEvent[] = []
182
+
183
+ const run = await drainQuery(
184
+ {
185
+ ...baseParams(provider, new ToolRegistry(), await mkWorkdir()),
186
+ messages: [
187
+ createUserMessage('what is in this image?', [
188
+ { data: 'aGVsbG8=', mediaType: 'image/png' },
189
+ ]),
190
+ ],
191
+ },
192
+ (event) => {
193
+ events.push(event)
194
+ },
195
+ )
196
+
197
+ expect(run.status).toBe('completed')
198
+ const warning = events.find(
199
+ (e): e is Extract<RunEvent, { type: 'capability_warning' }> =>
200
+ e.type === 'capability_warning',
201
+ )
202
+ expect(warning?.capability).toBe('vision')
203
+ expect(warning?.providerId).toBe('capturing')
204
+ })
205
+
206
+ it('does not warn when no attachments are present on a no-vision provider', async () => {
207
+ const provider = new CapturingProvider(NO_VISION_CAPABILITIES)
208
+ const events: RunEvent[] = []
209
+
210
+ await drainQuery(
211
+ {
212
+ ...baseParams(provider, new ToolRegistry(), await mkWorkdir()),
213
+ messages: [createUserMessage('plain text')],
214
+ },
215
+ (event) => {
216
+ events.push(event)
217
+ },
218
+ )
219
+
220
+ expect(events.some((e) => e.type === 'capability_warning')).toBe(false)
221
+ })
222
+
223
+ it('strictCapabilities: true throws on a tools mismatch instead of degrading', async () => {
224
+ const provider = new CapturingProvider(NO_TOOLS_CAPABILITIES)
225
+ const tools = new ToolRegistry()
226
+ registerEchoTool(tools)
227
+
228
+ await expect(
229
+ drainQuery({
230
+ ...baseParams(provider, tools, await mkWorkdir()),
231
+ messages: [createUserMessage('hello')],
232
+ strictCapabilities: true,
233
+ }),
234
+ ).rejects.toThrow(/supportsTools: false/)
235
+ })
236
+
237
+ it('strictCapabilities: true throws on a vision mismatch instead of degrading', async () => {
238
+ const provider = new CapturingProvider(NO_VISION_CAPABILITIES)
239
+
240
+ await expect(
241
+ drainQuery({
242
+ ...baseParams(provider, new ToolRegistry(), await mkWorkdir()),
243
+ messages: [createUserMessage('describe', [{ data: 'aGVsbG8=', mediaType: 'image/png' }])],
244
+ strictCapabilities: true,
245
+ }),
246
+ ).rejects.toThrow(/supportsVision: false/)
247
+ })
248
+ })