agentfootprint 9.41.0 → 9.44.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 (367) hide show
  1. package/AGENTS.md +30 -1
  2. package/CLAUDE.md +3 -1
  3. package/ai-instructions/claude-code/SKILL.md +31 -1
  4. package/dist/adapters/hosting/agentcore.js +163 -23
  5. package/dist/adapters/hosting/agentcore.js.map +1 -1
  6. package/dist/adapters/hosting/firestoreSessions.js +184 -14
  7. package/dist/adapters/hosting/firestoreSessions.js.map +1 -1
  8. package/dist/adapters/hosting/googleAgentEngine.js +29 -0
  9. package/dist/adapters/hosting/googleAgentEngine.js.map +1 -1
  10. package/dist/artifacts/conformance/cases.js +20 -0
  11. package/dist/artifacts/conformance/cases.js.map +1 -1
  12. package/dist/artifacts/scopePath.js +41 -1
  13. package/dist/artifacts/scopePath.js.map +1 -1
  14. package/dist/core/Agent.js +17 -1
  15. package/dist/core/Agent.js.map +1 -1
  16. package/dist/core/agent/AgentBuilder.js +56 -1
  17. package/dist/core/agent/AgentBuilder.js.map +1 -1
  18. package/dist/core/agent/buildAgentChart.js +5 -1
  19. package/dist/core/agent/buildAgentChart.js.map +1 -1
  20. package/dist/core/agent/buildDynamicAgentChart.js +5 -1
  21. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  22. package/dist/core/agent/coverage/absent.js +163 -0
  23. package/dist/core/agent/coverage/absent.js.map +1 -0
  24. package/dist/core/agent/coverage/answer.js +90 -0
  25. package/dist/core/agent/coverage/answer.js.map +1 -0
  26. package/dist/core/agent/coverage/evidence.js +90 -0
  27. package/dist/core/agent/coverage/evidence.js.map +1 -0
  28. package/dist/core/agent/coverage/index.js +37 -0
  29. package/dist/core/agent/coverage/index.js.map +1 -0
  30. package/dist/core/agent/coverage/items.js +91 -0
  31. package/dist/core/agent/coverage/items.js.map +1 -0
  32. package/dist/core/agent/coverage/ledger.js +131 -0
  33. package/dist/core/agent/coverage/ledger.js.map +1 -0
  34. package/dist/core/agent/coverage/read.js +56 -0
  35. package/dist/core/agent/coverage/read.js.map +1 -0
  36. package/dist/core/agent/coverage/types.js +15 -0
  37. package/dist/core/agent/coverage/types.js.map +1 -0
  38. package/dist/core/agent/evidence/evidenceIndex.js +18 -1
  39. package/dist/core/agent/evidence/evidenceIndex.js.map +1 -1
  40. package/dist/core/agent/stages/prepareFinal.js +44 -3
  41. package/dist/core/agent/stages/prepareFinal.js.map +1 -1
  42. package/dist/core/agent/stages/toolCalls.js +109 -3
  43. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  44. package/dist/core/agent/toolEffects.js +1 -1
  45. package/dist/core/outputFallback.js.map +1 -1
  46. package/dist/debug.js +49 -25
  47. package/dist/debug.js.map +1 -1
  48. package/dist/esm/adapters/hosting/agentcore.d.ts +8 -0
  49. package/dist/esm/adapters/hosting/agentcore.js +163 -23
  50. package/dist/esm/adapters/hosting/agentcore.js.map +1 -1
  51. package/dist/esm/adapters/hosting/firestoreSessions.d.ts +162 -14
  52. package/dist/esm/adapters/hosting/firestoreSessions.js +182 -13
  53. package/dist/esm/adapters/hosting/firestoreSessions.js.map +1 -1
  54. package/dist/esm/adapters/hosting/googleAgentEngine.d.ts +16 -1
  55. package/dist/esm/adapters/hosting/googleAgentEngine.js +29 -0
  56. package/dist/esm/adapters/hosting/googleAgentEngine.js.map +1 -1
  57. package/dist/esm/artifacts/conformance/cases.js +20 -0
  58. package/dist/esm/artifacts/conformance/cases.js.map +1 -1
  59. package/dist/esm/artifacts/scopePath.d.ts +31 -0
  60. package/dist/esm/artifacts/scopePath.js +41 -1
  61. package/dist/esm/artifacts/scopePath.js.map +1 -1
  62. package/dist/esm/core/Agent.d.ts +8 -1
  63. package/dist/esm/core/Agent.js +18 -2
  64. package/dist/esm/core/Agent.js.map +1 -1
  65. package/dist/esm/core/agent/AgentBuilder.d.ts +43 -0
  66. package/dist/esm/core/agent/AgentBuilder.js +56 -1
  67. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  68. package/dist/esm/core/agent/buildAgentChart.d.ts +9 -0
  69. package/dist/esm/core/agent/buildAgentChart.js +6 -2
  70. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  71. package/dist/esm/core/agent/buildDynamicAgentChart.js +6 -2
  72. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  73. package/dist/esm/core/agent/coverage/absent.d.ts +104 -0
  74. package/dist/esm/core/agent/coverage/absent.js +157 -0
  75. package/dist/esm/core/agent/coverage/absent.js.map +1 -0
  76. package/dist/esm/core/agent/coverage/answer.d.ts +46 -0
  77. package/dist/esm/core/agent/coverage/answer.js +86 -0
  78. package/dist/esm/core/agent/coverage/answer.js.map +1 -0
  79. package/dist/esm/core/agent/coverage/evidence.d.ts +68 -0
  80. package/dist/esm/core/agent/coverage/evidence.js +86 -0
  81. package/dist/esm/core/agent/coverage/evidence.js.map +1 -0
  82. package/dist/esm/core/agent/coverage/index.d.ts +17 -0
  83. package/dist/esm/core/agent/coverage/index.js +17 -0
  84. package/dist/esm/core/agent/coverage/index.js.map +1 -0
  85. package/dist/esm/core/agent/coverage/items.d.ts +35 -0
  86. package/dist/esm/core/agent/coverage/items.js +85 -0
  87. package/dist/esm/core/agent/coverage/items.js.map +1 -0
  88. package/dist/esm/core/agent/coverage/ledger.d.ts +80 -0
  89. package/dist/esm/core/agent/coverage/ledger.js +125 -0
  90. package/dist/esm/core/agent/coverage/ledger.js.map +1 -0
  91. package/dist/esm/core/agent/coverage/read.d.ts +51 -0
  92. package/dist/esm/core/agent/coverage/read.js +52 -0
  93. package/dist/esm/core/agent/coverage/read.js.map +1 -0
  94. package/dist/esm/core/agent/coverage/types.d.ts +142 -0
  95. package/dist/esm/core/agent/coverage/types.js +14 -0
  96. package/dist/esm/core/agent/coverage/types.js.map +1 -0
  97. package/dist/esm/core/agent/evidence/evidenceIndex.d.ts +7 -0
  98. package/dist/esm/core/agent/evidence/evidenceIndex.js +18 -1
  99. package/dist/esm/core/agent/evidence/evidenceIndex.js.map +1 -1
  100. package/dist/esm/core/agent/stages/prepareFinal.d.ts +18 -0
  101. package/dist/esm/core/agent/stages/prepareFinal.js +42 -2
  102. package/dist/esm/core/agent/stages/prepareFinal.js.map +1 -1
  103. package/dist/esm/core/agent/stages/toolCalls.js +109 -3
  104. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  105. package/dist/esm/core/agent/toolEffects.d.ts +2 -2
  106. package/dist/esm/core/agent/toolEffects.js +1 -1
  107. package/dist/esm/core/agent/types.d.ts +14 -0
  108. package/dist/esm/core/outputFallback.d.ts +15 -1
  109. package/dist/esm/core/outputFallback.js.map +1 -1
  110. package/dist/esm/debug.d.ts +1 -0
  111. package/dist/esm/debug.js +7 -0
  112. package/dist/esm/debug.js.map +1 -1
  113. package/dist/esm/events/dispatcher.d.ts +1 -1
  114. package/dist/esm/events/dispatcher.js.map +1 -1
  115. package/dist/esm/events/payloads.d.ts +82 -0
  116. package/dist/esm/events/registry.d.ts +11 -1
  117. package/dist/esm/events/registry.js +10 -0
  118. package/dist/esm/events/registry.js.map +1 -1
  119. package/dist/esm/hosting/conformance/cases.d.ts +2 -1
  120. package/dist/esm/hosting/conformance/cases.js +229 -22
  121. package/dist/esm/hosting/conformance/cases.js.map +1 -1
  122. package/dist/esm/hosting/conformance/types.d.ts +7 -7
  123. package/dist/esm/hosting/errors.d.ts +28 -0
  124. package/dist/esm/hosting/errors.js +40 -0
  125. package/dist/esm/hosting/errors.js.map +1 -1
  126. package/dist/esm/hosting/index.d.ts +11 -3
  127. package/dist/esm/hosting/index.js +15 -2
  128. package/dist/esm/hosting/index.js.map +1 -1
  129. package/dist/esm/hosting/memorySessions.js +39 -0
  130. package/dist/esm/hosting/memorySessions.js.map +1 -1
  131. package/dist/esm/hosting/sessionRetention.d.ts +56 -0
  132. package/dist/esm/hosting/sessionRetention.js +69 -0
  133. package/dist/esm/hosting/sessionRetention.js.map +1 -0
  134. package/dist/esm/hosting/sqliteSessions.d.ts +11 -1
  135. package/dist/esm/hosting/sqliteSessions.js +51 -0
  136. package/dist/esm/hosting/sqliteSessions.js.map +1 -1
  137. package/dist/esm/hosting/types.d.ts +184 -0
  138. package/dist/esm/hosting/types.js +9 -0
  139. package/dist/esm/hosting/types.js.map +1 -1
  140. package/dist/esm/hosting-providers.d.ts +15 -11
  141. package/dist/esm/hosting-providers.js +21 -11
  142. package/dist/esm/hosting-providers.js.map +1 -1
  143. package/dist/esm/index.d.ts +1 -0
  144. package/dist/esm/index.js +18 -0
  145. package/dist/esm/index.js.map +1 -1
  146. package/dist/esm/lib/context-bisect/arms/apply.d.ts +54 -0
  147. package/dist/esm/lib/context-bisect/arms/apply.js +41 -0
  148. package/dist/esm/lib/context-bisect/arms/apply.js.map +1 -0
  149. package/dist/esm/lib/context-bisect/arms/compare.d.ts +27 -0
  150. package/dist/esm/lib/context-bisect/arms/compare.js +112 -0
  151. package/dist/esm/lib/context-bisect/arms/compare.js.map +1 -0
  152. package/dist/esm/lib/context-bisect/arms/index.d.ts +16 -0
  153. package/dist/esm/lib/context-bisect/arms/index.js +16 -0
  154. package/dist/esm/lib/context-bisect/arms/index.js.map +1 -0
  155. package/dist/esm/lib/context-bisect/arms/manifest.d.ts +79 -0
  156. package/dist/esm/lib/context-bisect/arms/manifest.js +199 -0
  157. package/dist/esm/lib/context-bisect/arms/manifest.js.map +1 -0
  158. package/dist/esm/lib/context-bisect/arms/probe.d.ts +54 -0
  159. package/dist/esm/lib/context-bisect/arms/probe.js +125 -0
  160. package/dist/esm/lib/context-bisect/arms/probe.js.map +1 -0
  161. package/dist/esm/lib/context-bisect/arms/types.d.ts +319 -0
  162. package/dist/esm/lib/context-bisect/arms/types.js +67 -0
  163. package/dist/esm/lib/context-bisect/arms/types.js.map +1 -0
  164. package/dist/esm/lib/context-bisect/arms/validate.d.ts +26 -0
  165. package/dist/esm/lib/context-bisect/arms/validate.js +122 -0
  166. package/dist/esm/lib/context-bisect/arms/validate.js.map +1 -0
  167. package/dist/esm/lib/context-bisect/arms/verdict.d.ts +53 -0
  168. package/dist/esm/lib/context-bisect/arms/verdict.js +103 -0
  169. package/dist/esm/lib/context-bisect/arms/verdict.js.map +1 -0
  170. package/dist/esm/lib/context-bisect/index.d.ts +1 -0
  171. package/dist/esm/lib/context-bisect/index.js +5 -0
  172. package/dist/esm/lib/context-bisect/index.js.map +1 -1
  173. package/dist/esm/lib/injection-engine/index.d.ts +1 -1
  174. package/dist/esm/lib/injection-engine/index.js +3 -1
  175. package/dist/esm/lib/injection-engine/index.js.map +1 -1
  176. package/dist/esm/lib/injection-engine/skillExamples.d.ts +16 -13
  177. package/dist/esm/lib/injection-engine/skillExamples.js +16 -60
  178. package/dist/esm/lib/injection-engine/skillExamples.js.map +1 -1
  179. package/dist/esm/lib/injection-engine/skillGraph.d.ts +50 -0
  180. package/dist/esm/lib/injection-engine/skillGraph.js +52 -1
  181. package/dist/esm/lib/injection-engine/skillGraph.js.map +1 -1
  182. package/dist/esm/lib/injection-engine/skillGraphCheckup.d.ts +6 -5
  183. package/dist/esm/lib/injection-engine/skillGraphCheckup.js.map +1 -1
  184. package/dist/esm/lib/injection-engine/skillNeverRoutes.d.ts +131 -0
  185. package/dist/esm/lib/injection-engine/skillNeverRoutes.js +228 -0
  186. package/dist/esm/lib/injection-engine/skillNeverRoutes.js.map +1 -0
  187. package/dist/esm/lib/injection-engine/skillPartition.d.ts +69 -0
  188. package/dist/esm/lib/injection-engine/skillPartition.js +273 -0
  189. package/dist/esm/lib/injection-engine/skillPartition.js.map +1 -0
  190. package/dist/esm/lib/injection-engine/skillsFromDir.d.ts +83 -3
  191. package/dist/esm/lib/injection-engine/skillsFromDir.js +118 -7
  192. package/dist/esm/lib/injection-engine/skillsFromDir.js.map +1 -1
  193. package/dist/esm/lib/injection-engine/skillsFromDirRoutes.d.ts +105 -0
  194. package/dist/esm/lib/injection-engine/skillsFromDirRoutes.js +169 -0
  195. package/dist/esm/lib/injection-engine/skillsFromDirRoutes.js.map +1 -0
  196. package/dist/esm/lib/injection-engine/startRuleClaim.d.ts +91 -0
  197. package/dist/esm/lib/injection-engine/startRuleClaim.js +78 -0
  198. package/dist/esm/lib/injection-engine/startRuleClaim.js.map +1 -0
  199. package/dist/esm/lib/injection-engine/toolOutcome.d.ts +12 -3
  200. package/dist/esm/lib/injection-engine/toolOutcome.js +2 -1
  201. package/dist/esm/lib/injection-engine/toolOutcome.js.map +1 -1
  202. package/dist/events/dispatcher.js.map +1 -1
  203. package/dist/events/registry.js +10 -0
  204. package/dist/events/registry.js.map +1 -1
  205. package/dist/hosting/conformance/cases.js +229 -22
  206. package/dist/hosting/conformance/cases.js.map +1 -1
  207. package/dist/hosting/errors.js +42 -1
  208. package/dist/hosting/errors.js.map +1 -1
  209. package/dist/hosting/index.js +18 -2
  210. package/dist/hosting/index.js.map +1 -1
  211. package/dist/hosting/memorySessions.js +39 -0
  212. package/dist/hosting/memorySessions.js.map +1 -1
  213. package/dist/hosting/sessionRetention.js +73 -0
  214. package/dist/hosting/sessionRetention.js.map +1 -0
  215. package/dist/hosting/sqliteSessions.js +51 -0
  216. package/dist/hosting/sqliteSessions.js.map +1 -1
  217. package/dist/hosting/types.js +10 -1
  218. package/dist/hosting/types.js.map +1 -1
  219. package/dist/hosting-providers.js +21 -11
  220. package/dist/hosting-providers.js.map +1 -1
  221. package/dist/index.js +72 -43
  222. package/dist/index.js.map +1 -1
  223. package/dist/lib/context-bisect/arms/apply.js +45 -0
  224. package/dist/lib/context-bisect/arms/apply.js.map +1 -0
  225. package/dist/lib/context-bisect/arms/compare.js +116 -0
  226. package/dist/lib/context-bisect/arms/compare.js.map +1 -0
  227. package/dist/lib/context-bisect/arms/index.js +35 -0
  228. package/dist/lib/context-bisect/arms/index.js.map +1 -0
  229. package/dist/lib/context-bisect/arms/manifest.js +207 -0
  230. package/dist/lib/context-bisect/arms/manifest.js.map +1 -0
  231. package/dist/lib/context-bisect/arms/probe.js +132 -0
  232. package/dist/lib/context-bisect/arms/probe.js.map +1 -0
  233. package/dist/lib/context-bisect/arms/types.js +68 -0
  234. package/dist/lib/context-bisect/arms/types.js.map +1 -0
  235. package/dist/lib/context-bisect/arms/validate.js +128 -0
  236. package/dist/lib/context-bisect/arms/validate.js.map +1 -0
  237. package/dist/lib/context-bisect/arms/verdict.js +107 -0
  238. package/dist/lib/context-bisect/arms/verdict.js.map +1 -0
  239. package/dist/lib/context-bisect/index.js +23 -1
  240. package/dist/lib/context-bisect/index.js.map +1 -1
  241. package/dist/lib/injection-engine/index.js +4 -1
  242. package/dist/lib/injection-engine/index.js.map +1 -1
  243. package/dist/lib/injection-engine/skillExamples.js +24 -68
  244. package/dist/lib/injection-engine/skillExamples.js.map +1 -1
  245. package/dist/lib/injection-engine/skillGraph.js +52 -1
  246. package/dist/lib/injection-engine/skillGraph.js.map +1 -1
  247. package/dist/lib/injection-engine/skillGraphCheckup.js.map +1 -1
  248. package/dist/lib/injection-engine/skillNeverRoutes.js +234 -0
  249. package/dist/lib/injection-engine/skillNeverRoutes.js.map +1 -0
  250. package/dist/lib/injection-engine/skillPartition.js +277 -0
  251. package/dist/lib/injection-engine/skillPartition.js.map +1 -0
  252. package/dist/lib/injection-engine/skillsFromDir.js +121 -9
  253. package/dist/lib/injection-engine/skillsFromDir.js.map +1 -1
  254. package/dist/lib/injection-engine/skillsFromDirRoutes.js +174 -0
  255. package/dist/lib/injection-engine/skillsFromDirRoutes.js.map +1 -0
  256. package/dist/lib/injection-engine/startRuleClaim.js +85 -0
  257. package/dist/lib/injection-engine/startRuleClaim.js.map +1 -0
  258. package/dist/lib/injection-engine/toolOutcome.js +2 -1
  259. package/dist/lib/injection-engine/toolOutcome.js.map +1 -1
  260. package/dist/types/adapters/hosting/agentcore.d.ts +8 -0
  261. package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -1
  262. package/dist/types/adapters/hosting/firestoreSessions.d.ts +162 -14
  263. package/dist/types/adapters/hosting/firestoreSessions.d.ts.map +1 -1
  264. package/dist/types/adapters/hosting/googleAgentEngine.d.ts +16 -1
  265. package/dist/types/adapters/hosting/googleAgentEngine.d.ts.map +1 -1
  266. package/dist/types/artifacts/conformance/cases.d.ts.map +1 -1
  267. package/dist/types/artifacts/scopePath.d.ts +31 -0
  268. package/dist/types/artifacts/scopePath.d.ts.map +1 -1
  269. package/dist/types/core/Agent.d.ts +8 -1
  270. package/dist/types/core/Agent.d.ts.map +1 -1
  271. package/dist/types/core/agent/AgentBuilder.d.ts +43 -0
  272. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  273. package/dist/types/core/agent/buildAgentChart.d.ts +9 -0
  274. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  275. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  276. package/dist/types/core/agent/coverage/absent.d.ts +105 -0
  277. package/dist/types/core/agent/coverage/absent.d.ts.map +1 -0
  278. package/dist/types/core/agent/coverage/answer.d.ts +47 -0
  279. package/dist/types/core/agent/coverage/answer.d.ts.map +1 -0
  280. package/dist/types/core/agent/coverage/evidence.d.ts +69 -0
  281. package/dist/types/core/agent/coverage/evidence.d.ts.map +1 -0
  282. package/dist/types/core/agent/coverage/index.d.ts +18 -0
  283. package/dist/types/core/agent/coverage/index.d.ts.map +1 -0
  284. package/dist/types/core/agent/coverage/items.d.ts +36 -0
  285. package/dist/types/core/agent/coverage/items.d.ts.map +1 -0
  286. package/dist/types/core/agent/coverage/ledger.d.ts +81 -0
  287. package/dist/types/core/agent/coverage/ledger.d.ts.map +1 -0
  288. package/dist/types/core/agent/coverage/read.d.ts +52 -0
  289. package/dist/types/core/agent/coverage/read.d.ts.map +1 -0
  290. package/dist/types/core/agent/coverage/types.d.ts +143 -0
  291. package/dist/types/core/agent/coverage/types.d.ts.map +1 -0
  292. package/dist/types/core/agent/evidence/evidenceIndex.d.ts +7 -0
  293. package/dist/types/core/agent/evidence/evidenceIndex.d.ts.map +1 -1
  294. package/dist/types/core/agent/stages/prepareFinal.d.ts +18 -0
  295. package/dist/types/core/agent/stages/prepareFinal.d.ts.map +1 -1
  296. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  297. package/dist/types/core/agent/toolEffects.d.ts +2 -2
  298. package/dist/types/core/agent/types.d.ts +14 -0
  299. package/dist/types/core/agent/types.d.ts.map +1 -1
  300. package/dist/types/core/outputFallback.d.ts +15 -1
  301. package/dist/types/core/outputFallback.d.ts.map +1 -1
  302. package/dist/types/debug.d.ts +1 -0
  303. package/dist/types/debug.d.ts.map +1 -1
  304. package/dist/types/events/dispatcher.d.ts +1 -1
  305. package/dist/types/events/dispatcher.d.ts.map +1 -1
  306. package/dist/types/events/payloads.d.ts +82 -0
  307. package/dist/types/events/payloads.d.ts.map +1 -1
  308. package/dist/types/events/registry.d.ts +11 -1
  309. package/dist/types/events/registry.d.ts.map +1 -1
  310. package/dist/types/hosting/conformance/cases.d.ts +2 -1
  311. package/dist/types/hosting/conformance/cases.d.ts.map +1 -1
  312. package/dist/types/hosting/conformance/types.d.ts +7 -7
  313. package/dist/types/hosting/conformance/types.d.ts.map +1 -1
  314. package/dist/types/hosting/errors.d.ts +28 -0
  315. package/dist/types/hosting/errors.d.ts.map +1 -1
  316. package/dist/types/hosting/index.d.ts +11 -3
  317. package/dist/types/hosting/index.d.ts.map +1 -1
  318. package/dist/types/hosting/memorySessions.d.ts.map +1 -1
  319. package/dist/types/hosting/sessionRetention.d.ts +57 -0
  320. package/dist/types/hosting/sessionRetention.d.ts.map +1 -0
  321. package/dist/types/hosting/sqliteSessions.d.ts +11 -1
  322. package/dist/types/hosting/sqliteSessions.d.ts.map +1 -1
  323. package/dist/types/hosting/types.d.ts +184 -0
  324. package/dist/types/hosting/types.d.ts.map +1 -1
  325. package/dist/types/hosting-providers.d.ts +15 -11
  326. package/dist/types/hosting-providers.d.ts.map +1 -1
  327. package/dist/types/index.d.ts +1 -0
  328. package/dist/types/index.d.ts.map +1 -1
  329. package/dist/types/lib/context-bisect/arms/apply.d.ts +55 -0
  330. package/dist/types/lib/context-bisect/arms/apply.d.ts.map +1 -0
  331. package/dist/types/lib/context-bisect/arms/compare.d.ts +28 -0
  332. package/dist/types/lib/context-bisect/arms/compare.d.ts.map +1 -0
  333. package/dist/types/lib/context-bisect/arms/index.d.ts +17 -0
  334. package/dist/types/lib/context-bisect/arms/index.d.ts.map +1 -0
  335. package/dist/types/lib/context-bisect/arms/manifest.d.ts +80 -0
  336. package/dist/types/lib/context-bisect/arms/manifest.d.ts.map +1 -0
  337. package/dist/types/lib/context-bisect/arms/probe.d.ts +55 -0
  338. package/dist/types/lib/context-bisect/arms/probe.d.ts.map +1 -0
  339. package/dist/types/lib/context-bisect/arms/types.d.ts +320 -0
  340. package/dist/types/lib/context-bisect/arms/types.d.ts.map +1 -0
  341. package/dist/types/lib/context-bisect/arms/validate.d.ts +27 -0
  342. package/dist/types/lib/context-bisect/arms/validate.d.ts.map +1 -0
  343. package/dist/types/lib/context-bisect/arms/verdict.d.ts +54 -0
  344. package/dist/types/lib/context-bisect/arms/verdict.d.ts.map +1 -0
  345. package/dist/types/lib/context-bisect/index.d.ts +1 -0
  346. package/dist/types/lib/context-bisect/index.d.ts.map +1 -1
  347. package/dist/types/lib/injection-engine/index.d.ts +1 -1
  348. package/dist/types/lib/injection-engine/index.d.ts.map +1 -1
  349. package/dist/types/lib/injection-engine/skillExamples.d.ts +16 -13
  350. package/dist/types/lib/injection-engine/skillExamples.d.ts.map +1 -1
  351. package/dist/types/lib/injection-engine/skillGraph.d.ts +50 -0
  352. package/dist/types/lib/injection-engine/skillGraph.d.ts.map +1 -1
  353. package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts +6 -5
  354. package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts.map +1 -1
  355. package/dist/types/lib/injection-engine/skillNeverRoutes.d.ts +132 -0
  356. package/dist/types/lib/injection-engine/skillNeverRoutes.d.ts.map +1 -0
  357. package/dist/types/lib/injection-engine/skillPartition.d.ts +70 -0
  358. package/dist/types/lib/injection-engine/skillPartition.d.ts.map +1 -0
  359. package/dist/types/lib/injection-engine/skillsFromDir.d.ts +83 -3
  360. package/dist/types/lib/injection-engine/skillsFromDir.d.ts.map +1 -1
  361. package/dist/types/lib/injection-engine/skillsFromDirRoutes.d.ts +106 -0
  362. package/dist/types/lib/injection-engine/skillsFromDirRoutes.d.ts.map +1 -0
  363. package/dist/types/lib/injection-engine/startRuleClaim.d.ts +92 -0
  364. package/dist/types/lib/injection-engine/startRuleClaim.d.ts.map +1 -0
  365. package/dist/types/lib/injection-engine/toolOutcome.d.ts +12 -3
  366. package/dist/types/lib/injection-engine/toolOutcome.d.ts.map +1 -1
  367. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillNeverRoutes.js","sourceRoot":"","sources":["../../../../src/lib/injection-engine/skillNeverRoutes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * skillPartition — three ADVISORY signals about the shape of the partition
3
+ * itself: how a graph cut the world into skills.
4
+ *
5
+ * ## Why the partition is worth a check at all
6
+ *
7
+ * Everything else the check-up reports is a wiring fact — an edge to a skill
8
+ * that is not there, a rule that can never win. The partition is upstream of
9
+ * all of it, and it is the decision with the most leverage: a graph whose
10
+ * skills follow SYSTEMS rather than CAPABILITIES makes one ordinary question
11
+ * cross four skills, and the capability the user actually wanted ends up
12
+ * implemented outside the graph, by hand, where nothing can see it. Field use
13
+ * produced exactly that shape — nineteen skills, ONE declared edge, and tool
14
+ * names carrying the system that owns them (`influx_*`, `pmax_*`, `pstore_*`,
15
+ * `rvtools_*`) with the skills following the prefixes.
16
+ *
17
+ * The important part: all three of those are visible from NAMES AND STRUCTURE
18
+ * ALONE, at build time, with no run and no model. So the runtime can say them.
19
+ *
20
+ * ## Why every one of them is an advisory, and stays one
21
+ *
22
+ * Each signal has a legitimate design behind it:
23
+ *
24
+ * • a skill really can be one system's capability (a weather skill wrapping a
25
+ * weather API is not a mistake);
26
+ * • a deliberately FLAT menu of independent skills really does declare almost
27
+ * no edges — a scorer or the model picks, and there is nothing to hand off;
28
+ * • a one-call capability really can be one tool with a short body.
29
+ *
30
+ * So none of them is ever an error, and none of them says "this is wrong". Each
31
+ * message states the SIGNAL AS A FACT the author can check against their own
32
+ * intent — "your 19 skills declare 1 route", "every tool in `powerstore` shares
33
+ * the prefix `pstore_`" — and then names the design each fact usually indicates
34
+ * and the design it is also consistent with. This library's check-up teaches;
35
+ * it does not grade.
36
+ *
37
+ * ## The thresholds, and why each one is where it is
38
+ *
39
+ * Named constants below, each with the argument beside it. They are chosen so a
40
+ * SMALL graph is silent (small graphs have no partition problem worth naming),
41
+ * so a trivially-true observation is never dressed up as a finding (one tool
42
+ * shares a prefix with itself), and so the commonest honest naming convention —
43
+ * a VERB first — never fires.
44
+ *
45
+ * Zone: PURE CORE. Pinned by
46
+ * `test/lib/injection-engine/skill-graph-fence.test.ts`.
47
+ */
48
+ import type { GraphProblem } from './skillGraphCheckup.js';
49
+ import type { Injection } from './types.js';
50
+ /** What the partition check reads. Counts, because that is what the signals
51
+ * are about — no check here reasons about which edge goes where. */
52
+ export interface PartitionInput {
53
+ /** Every skill in the graph (wired or not). */
54
+ readonly skills: readonly Injection[];
55
+ /** Declared routes, of any kind (a bare/model edge is still a declared
56
+ * handoff — the signal is about handoffs that were never written down at
57
+ * all, not about how deterministic they are). */
58
+ readonly routeCount: number;
59
+ /** Declared entries — quoted in the ratio message so the fact is complete. */
60
+ readonly entryCount: number;
61
+ /** A decision `tree()` owns its own routing and declares no routes by
62
+ * construction, so the edge-ratio signal does not apply there. */
63
+ readonly isTree: boolean;
64
+ }
65
+ /**
66
+ * Run the three partition signals. Pure, and cheap: one pass over the skills
67
+ * and two integers. Every problem it produces is a WARNING.
68
+ */
69
+ export declare function checkPartition(input: PartitionInput): GraphProblem[];
@@ -0,0 +1,273 @@
1
+ /**
2
+ * skillPartition — three ADVISORY signals about the shape of the partition
3
+ * itself: how a graph cut the world into skills.
4
+ *
5
+ * ## Why the partition is worth a check at all
6
+ *
7
+ * Everything else the check-up reports is a wiring fact — an edge to a skill
8
+ * that is not there, a rule that can never win. The partition is upstream of
9
+ * all of it, and it is the decision with the most leverage: a graph whose
10
+ * skills follow SYSTEMS rather than CAPABILITIES makes one ordinary question
11
+ * cross four skills, and the capability the user actually wanted ends up
12
+ * implemented outside the graph, by hand, where nothing can see it. Field use
13
+ * produced exactly that shape — nineteen skills, ONE declared edge, and tool
14
+ * names carrying the system that owns them (`influx_*`, `pmax_*`, `pstore_*`,
15
+ * `rvtools_*`) with the skills following the prefixes.
16
+ *
17
+ * The important part: all three of those are visible from NAMES AND STRUCTURE
18
+ * ALONE, at build time, with no run and no model. So the runtime can say them.
19
+ *
20
+ * ## Why every one of them is an advisory, and stays one
21
+ *
22
+ * Each signal has a legitimate design behind it:
23
+ *
24
+ * • a skill really can be one system's capability (a weather skill wrapping a
25
+ * weather API is not a mistake);
26
+ * • a deliberately FLAT menu of independent skills really does declare almost
27
+ * no edges — a scorer or the model picks, and there is nothing to hand off;
28
+ * • a one-call capability really can be one tool with a short body.
29
+ *
30
+ * So none of them is ever an error, and none of them says "this is wrong". Each
31
+ * message states the SIGNAL AS A FACT the author can check against their own
32
+ * intent — "your 19 skills declare 1 route", "every tool in `powerstore` shares
33
+ * the prefix `pstore_`" — and then names the design each fact usually indicates
34
+ * and the design it is also consistent with. This library's check-up teaches;
35
+ * it does not grade.
36
+ *
37
+ * ## The thresholds, and why each one is where it is
38
+ *
39
+ * Named constants below, each with the argument beside it. They are chosen so a
40
+ * SMALL graph is silent (small graphs have no partition problem worth naming),
41
+ * so a trivially-true observation is never dressed up as a finding (one tool
42
+ * shares a prefix with itself), and so the commonest honest naming convention —
43
+ * a VERB first — never fires.
44
+ *
45
+ * Zone: PURE CORE. Pinned by
46
+ * `test/lib/injection-engine/skill-graph-fence.test.ts`.
47
+ */
48
+ import { skillToolNames } from './skillContract.js';
49
+ /**
50
+ * How many tools a skill needs before "they all share a prefix" is evidence of
51
+ * anything. ONE tool shares a prefix with itself — a trivial truth, and the
52
+ * skill it describes has its own signal below. TWO is two coin flips; a
53
+ * `get_order` / `get_invoice` pair is a naming habit, not a system boundary.
54
+ * At THREE the shared prefix is a decision somebody made about scope.
55
+ */
56
+ const MIN_TOOLS_FOR_PREFIX = 3;
57
+ /**
58
+ * First segments that are NOT a system — they are the verb half of the house
59
+ * naming convention (`get_price`, `issue_refund`, `lookup_order`). Verified
60
+ * against this repo's real tool names, where the ten commonest first segments
61
+ * are all verbs and outnumber every system prefix put together: without this
62
+ * list the check would fire on the best-named skills in the library, which is
63
+ * the fastest way to teach an author to ignore a check-up.
64
+ *
65
+ * The test is deliberately crude — a first segment either IS one of these words
66
+ * or it is not. A cleverer part-of-speech guess would be wrong more quietly.
67
+ */
68
+ const VERB_PREFIXES = new Set([
69
+ 'get',
70
+ 'set',
71
+ 'put',
72
+ 'post',
73
+ 'add',
74
+ 'read',
75
+ 'write',
76
+ 'list',
77
+ 'find',
78
+ 'fetch',
79
+ 'load',
80
+ 'save',
81
+ 'open',
82
+ 'close',
83
+ 'make',
84
+ 'create',
85
+ 'update',
86
+ 'delete',
87
+ 'remove',
88
+ 'search',
89
+ 'query',
90
+ 'count',
91
+ 'check',
92
+ 'verify',
93
+ 'validate',
94
+ 'run',
95
+ 'exec',
96
+ 'execute',
97
+ 'send',
98
+ 'call',
99
+ 'ask',
100
+ 'show',
101
+ 'lookup',
102
+ 'issue',
103
+ 'approve',
104
+ 'deny',
105
+ 'pay',
106
+ 'process',
107
+ 'resolve',
108
+ 'respond',
109
+ 'inspect',
110
+ 'export',
111
+ 'import',
112
+ 'start',
113
+ 'stop',
114
+ 'cancel',
115
+ 'compute',
116
+ 'calculate',
117
+ 'generate',
118
+ 'summarize',
119
+ 'transform',
120
+ ]);
121
+ /**
122
+ * The floor under ALL THREE signals: below this many skills there is no
123
+ * partition to have an opinion about.
124
+ *
125
+ * This is the module's one structural decision, and it is what keeps the
126
+ * advisories quiet by construction. Every signal here is a statement about how
127
+ * a graph CUT THE WORLD; a two-skill graph with no edge is a menu, one skill
128
+ * wrapping one tool is a choice about that skill, and a pair of tools sharing a
129
+ * prefix is a naming habit. None of those become a partition question until
130
+ * there are enough skills for the cut itself to be the design — and the field
131
+ * case that motivated all three had nineteen. A check that fires on a
132
+ * three-skill graph teaches an author to stop reading the check-up, which costs
133
+ * more than the finding is worth.
134
+ */
135
+ const MIN_SKILLS_FOR_PARTITION = 5;
136
+ /**
137
+ * The ratio that counts as "almost no declared edges". A graph needs N−1 edges
138
+ * to connect N skills into one routed structure; this fires below ONE EDGE PER
139
+ * FOUR SKILLS, which is four times sparser than a bare chain — far enough from
140
+ * the boundary that a graph tripping it is not a matter of taste. (The field
141
+ * case: 19 skills, 1 route — a ratio of 0.05.)
142
+ */
143
+ const MIN_ROUTES_PER_SKILL = 0.25;
144
+ /**
145
+ * Word count below which a body carries no knowledge the tool schema does not
146
+ * already carry. One sentence of real guidance — when to reach for this, what
147
+ * to check first, what the failure looks like — does not fit in 25 words, so a
148
+ * body under it is either a restatement of the tool's own description or a
149
+ * placeholder. Counted in whitespace-separated words, the only measure that
150
+ * means the same thing in prose as in markdown.
151
+ */
152
+ const THIN_BODY_WORDS = 25;
153
+ /**
154
+ * Run the three partition signals. Pure, and cheap: one pass over the skills
155
+ * and two integers. Every problem it produces is a WARNING.
156
+ */
157
+ export function checkPartition(input) {
158
+ const skillCount = input.skills.length;
159
+ // The floor, applied once for all three signals — see
160
+ // MIN_SKILLS_FOR_PARTITION. A small graph reports byte-identically to the
161
+ // way it did before this module existed.
162
+ if (skillCount < MIN_SKILLS_FOR_PARTITION)
163
+ return [];
164
+ const problems = [];
165
+ for (const skill of input.skills) {
166
+ const tools = skillToolNames(skill);
167
+ const id = idOf(skill);
168
+ // 1. tools-share-prefix — every tool in the skill comes from one system.
169
+ // Computed from the first `_`-delimited segment, which is where a system
170
+ // prefix lives when there is one (`pstore_list_volumes` → `pstore`), and
171
+ // silent when that segment is a verb (see VERB_PREFIXES).
172
+ const prefix = sharedPrefix(tools);
173
+ if (tools.length >= MIN_TOOLS_FOR_PREFIX && prefix !== undefined) {
174
+ problems.push({
175
+ kind: 'warning',
176
+ code: 'tools-share-prefix',
177
+ message: `Every tool in "${id}" shares the prefix \`${prefix}_\` (${tools.join(', ')}). ` +
178
+ `A skill whose whole tool set comes from one system is usually a wrapper around ` +
179
+ `that SYSTEM rather than a CAPABILITY — and when the skills follow the systems, one ` +
180
+ `ordinary question crosses several of them, so the thing the user actually asked ` +
181
+ `for gets built outside the graph where nothing can route it. Worth checking: ` +
182
+ `could a user's question be answered by "${id}" alone, or does answering it need ` +
183
+ `two of these skills at once? If it needs two, the capability is the skill and ` +
184
+ `\`${prefix}_\` is just where its tools happen to come from. If "${id}" really is ` +
185
+ `one system's capability, this is exactly right and there is nothing to fix.`,
186
+ skill: id,
187
+ });
188
+ }
189
+ // 2. skill-wraps-one-tool — one tool, and a body that adds nothing to its
190
+ // schema. The body is what the model reads to decide HOW to use the
191
+ // tool; when it is this short there is no procedure in the graph, only
192
+ // an endpoint with a name.
193
+ if (tools.length === 1 && tools[0] !== undefined) {
194
+ const words = wordCount(bodyOf(skill));
195
+ if (words < THIN_BODY_WORDS) {
196
+ problems.push({
197
+ kind: 'warning',
198
+ code: 'skill-wraps-one-tool',
199
+ message: `Skill "${id}" carries one tool (\`${tools[0]}\`) and a ${words}-word body — ` +
200
+ `under the ${THIN_BODY_WORDS} words it takes to say when to reach for it, what to ` +
201
+ `check first, or what a bad answer looks like. A skill whose body adds nothing to ` +
202
+ `the tool's own schema is an endpoint wrapper: the model already sees the tool, so ` +
203
+ `the skill buys nothing but a name and an activation. Either put the knowledge in ` +
204
+ `the body (that is the half a schema cannot carry), or drop the skill and register ` +
205
+ `\`${tools[0]}\` on the agent directly. If it is genuinely a one-call capability ` +
206
+ `with nothing to say about it, registering the tool is the smaller thing.`,
207
+ skill: id,
208
+ });
209
+ }
210
+ }
211
+ }
212
+ // 3. few-declared-edges — many skills, almost no declared handoffs. A graph
213
+ // level fact, reported once, and never for a tree (a tree declares its
214
+ // routing as the tree).
215
+ if (!input.isTree && input.routeCount < skillCount * MIN_ROUTES_PER_SKILL) {
216
+ problems.push({
217
+ kind: 'warning',
218
+ code: 'few-declared-edges',
219
+ message: `This graph declares ${plural(skillCount, 'skill', 'skills')}, ${plural(input.entryCount, 'entry', 'entries')} and ${plural(input.routeCount, 'route', 'routes')} — fewer than one route per four ` +
220
+ `skills, where connecting ${skillCount} skills into one routed structure takes at ` +
221
+ `least ${skillCount - 1}. When skills hand off to each other in PROSE ("then use the ` +
222
+ `capacity skill"), the handoff is invisible: nothing draws it, \`checkup()\` cannot ` +
223
+ `check it, the reachability walk cannot see it, and \`read_skill\`'s gate cannot ` +
224
+ `offer it — the model is left to infer the sequence every turn. Declaring the ones ` +
225
+ `you already rely on (\`.route(a, b, { onToolReturn })\`) turns each into something ` +
226
+ `the graph can route and a reader can see. If this is a deliberately flat menu of ` +
227
+ `independent skills — a scorer or the model picks one and it answers alone — then ` +
228
+ `there is nothing to hand off and this is the shape you meant.`,
229
+ });
230
+ }
231
+ return problems;
232
+ }
233
+ /** The id, read structurally like every other check in this family. */
234
+ function idOf(skill) {
235
+ return skill.id;
236
+ }
237
+ /** The skill's BODY — the prose that lands in the system slot when it
238
+ * activates. Same accessor `skillContract.ts` reads. */
239
+ function bodyOf(skill) {
240
+ return skill.inject?.systemPrompt ?? '';
241
+ }
242
+ function wordCount(text) {
243
+ const trimmed = text.trim();
244
+ return trimmed.length === 0 ? 0 : trimmed.split(/\s+/).length;
245
+ }
246
+ /**
247
+ * The first `_`-delimited segment shared by EVERY name, or undefined when there
248
+ * is none, when a name carries no `_` at all, or when the shared segment is a
249
+ * verb (a convention, not a system — see {@link VERB_PREFIXES}).
250
+ *
251
+ * Case-folded, because a tool registry that mixes `PSTORE_get` and
252
+ * `pstore_list` is still one system.
253
+ */
254
+ function sharedPrefix(names) {
255
+ if (names.length === 0)
256
+ return undefined;
257
+ let shared;
258
+ for (const name of names) {
259
+ const separator = name.indexOf('_');
260
+ if (separator <= 0)
261
+ return undefined; // no prefix segment to speak of
262
+ const segment = name.slice(0, separator).toLowerCase();
263
+ if (shared === undefined)
264
+ shared = segment;
265
+ else if (shared !== segment)
266
+ return undefined;
267
+ }
268
+ return shared !== undefined && !VERB_PREFIXES.has(shared) ? shared : undefined;
269
+ }
270
+ function plural(n, one, many) {
271
+ return `${n} ${n === 1 ? one : many}`;
272
+ }
273
+ //# sourceMappingURL=skillPartition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillPartition.js","sourceRoot":"","sources":["../../../../src/lib/injection-engine/skillPartition.ts"],"names":[],"mappings":"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"}
@@ -76,9 +76,36 @@
76
76
  * others take `defineSkill`'s defaults.
77
77
  * - unknown frontmatter keys are still IGNORED, not rejected, so a file
78
78
  * carrying another tool's metadata still loads here. `name`,
79
- * `description`, `tools`, `steps` and `onSkip` are the KNOWN keys — a file
80
- * that used one of those five for something else is the one case this
81
- * release can change, and it changes it loudly.
79
+ * `description`, `tools`, `steps`, `onSkip` and `routes` are the KNOWN
80
+ * keys — a file that used one of those six for something else is the one
81
+ * case a release can change, and it changes it loudly.
82
+ *
83
+ * ── The routing, and the door that reads it (9.43.0) ─────────────────────────
84
+ * A runbook says what to do, what to do it with, in what order — and where the
85
+ * work goes NEXT. That last part is the graph, and until now it had to be
86
+ * hand-wired in code even when the files said it plainly. `routes:` closes it:
87
+ *
88
+ * ---
89
+ * name: billing
90
+ * tools: lookup_order, issue_refund
91
+ * routes:
92
+ * - escalation: on issue_refund status=denied
93
+ * - receipts: on issue_refund
94
+ * ---
95
+ *
96
+ * const runbook = await runbookFromDir('./skills', { tools: [...] });
97
+ * const graph = skillGraph({ ...runbook, start: 'billing' });
98
+ *
99
+ * TWO DOORS, one truth: `skillsFromDir` returns skills and REFUSES a file that
100
+ * declares `routes:` (a door that dropped the routing would hand back a graph
101
+ * you believed was declared on disk); {@link runbookFromDir} returns
102
+ * `{ skills, steps }`. Same law as `tools:` — the file PICKS (a route names a
103
+ * skill id this directory declares; an unknown id is refused at load, by name,
104
+ * listing what is available), it never DEFINES. A guard is one of the two DATA
105
+ * conditions a route already has (`on <tool_name>`, `status=<outcome>`), and
106
+ * nothing else: a `when` predicate is CODE, no file can carry code, and that
107
+ * conditional stays in your source. See `skillsFromDirRoutes.ts` for the whole
108
+ * grammar and every refusal.
82
109
  *
83
110
  * A worked example feeding this into a graph:
84
111
  * `examples/features/47-skills-from-dir-graph.ts`.
@@ -111,6 +138,7 @@
111
138
  import type { Injection } from './types.js';
112
139
  import type { Tool } from '../../core/tools.js';
113
140
  import { type SurfaceMode } from './factories/defineSkill.js';
141
+ import type { SkillGraphStep } from './skillGraph.js';
114
142
  export interface SkillsFromDirOptions {
115
143
  /**
116
144
  * Where a loaded skill's body lands once activated. Defaults to
@@ -133,6 +161,31 @@ export interface SkillsFromDirOptions {
133
161
  */
134
162
  readonly tools?: readonly Tool[];
135
163
  }
164
+ /**
165
+ * A whole runbook directory, read as the two things a graph is made of
166
+ * (9.43.0): the skills, and the edges between them.
167
+ *
168
+ * Spreads straight into the graph — the field names are the graph's own, so
169
+ * nothing has to be translated at the call site:
170
+ *
171
+ * const runbook = await runbookFromDir('./skills', { tools });
172
+ * const graph = skillGraph({ ...runbook, start: 'triage' });
173
+ */
174
+ export interface DirRunbook {
175
+ /** Every `SKILL.md` under the directory, as Skill Injections — byte-identical
176
+ * to what `skillsFromDir` returns for the same directory. */
177
+ readonly skills: readonly Injection[];
178
+ /**
179
+ * The declared edges, ready for `skillGraph({ steps })`. In skill-name order,
180
+ * then file order, so a graph built from a directory is stable.
181
+ *
182
+ * NAME NOTE, because two different things are spelled `steps` in this
183
+ * library: a file's own `steps:` key is that SKILL's tool sequence (which
184
+ * rides on the skill, unchanged); these `steps` are the GRAPH's edges, read
185
+ * from each file's `routes:` key. They never meet.
186
+ */
187
+ readonly steps: readonly SkillGraphStep[];
188
+ }
136
189
  /**
137
190
  * Load every `SKILL.md` under `dir` as a Skill Injection.
138
191
  *
@@ -155,3 +208,30 @@ export interface SkillsFromDirOptions {
155
208
  * does not carry, or sequences a tool the file itself did not declare.
156
209
  */
157
210
  export declare function skillsFromDir(dir: string, opts?: SkillsFromDirOptions): Promise<readonly Injection[]>;
211
+ /**
212
+ * Load a directory as a whole RUNBOOK (9.43.0) — the skills AND the edges
213
+ * between them.
214
+ *
215
+ * `skillsFromDir` reads what one skill is; this reads what the skills are TO
216
+ * EACH OTHER, from each file's `routes:` key, and hands back both halves ready
217
+ * for `skillGraph({ ...runbook, start })`. Everything else is identical: same
218
+ * layouts, same frontmatter, same tool registry, same refusals.
219
+ *
220
+ * The routing a file may carry is deliberately small, and the boundary is a
221
+ * security property rather than a limitation — see `skillsFromDirRoutes.ts`:
222
+ * a route NAMES a skill this directory declares (an unknown id is refused at
223
+ * load, by name, listing what is available — never a half-graph), and a guard
224
+ * is one of the two DATA conditions a route already has (`on <tool>`,
225
+ * `status=<outcome>`). A `when` predicate is code, so no file can express one;
226
+ * that conditional stays in your source, where `skillGraph({ steps })` takes it.
227
+ *
228
+ * @param dir - A local filesystem path. Same rule as `skillsFromDir`.
229
+ * @param opts - Applied uniformly to every loaded skill.
230
+ *
231
+ * @throws everything `skillsFromDir` throws, plus: a route to an id no
232
+ * `SKILL.md` in the directory declares (the message lists what is
233
+ * available), a guard the grammar cannot express (the message quotes the
234
+ * whole grammar), a guard naming a tool the file itself does not declare,
235
+ * and a file routing to the same skill twice.
236
+ */
237
+ export declare function runbookFromDir(dir: string, opts?: SkillsFromDirOptions): Promise<DirRunbook>;
@@ -76,9 +76,36 @@
76
76
  * others take `defineSkill`'s defaults.
77
77
  * - unknown frontmatter keys are still IGNORED, not rejected, so a file
78
78
  * carrying another tool's metadata still loads here. `name`,
79
- * `description`, `tools`, `steps` and `onSkip` are the KNOWN keys — a file
80
- * that used one of those five for something else is the one case this
81
- * release can change, and it changes it loudly.
79
+ * `description`, `tools`, `steps`, `onSkip` and `routes` are the KNOWN
80
+ * keys — a file that used one of those six for something else is the one
81
+ * case a release can change, and it changes it loudly.
82
+ *
83
+ * ── The routing, and the door that reads it (9.43.0) ─────────────────────────
84
+ * A runbook says what to do, what to do it with, in what order — and where the
85
+ * work goes NEXT. That last part is the graph, and until now it had to be
86
+ * hand-wired in code even when the files said it plainly. `routes:` closes it:
87
+ *
88
+ * ---
89
+ * name: billing
90
+ * tools: lookup_order, issue_refund
91
+ * routes:
92
+ * - escalation: on issue_refund status=denied
93
+ * - receipts: on issue_refund
94
+ * ---
95
+ *
96
+ * const runbook = await runbookFromDir('./skills', { tools: [...] });
97
+ * const graph = skillGraph({ ...runbook, start: 'billing' });
98
+ *
99
+ * TWO DOORS, one truth: `skillsFromDir` returns skills and REFUSES a file that
100
+ * declares `routes:` (a door that dropped the routing would hand back a graph
101
+ * you believed was declared on disk); {@link runbookFromDir} returns
102
+ * `{ skills, steps }`. Same law as `tools:` — the file PICKS (a route names a
103
+ * skill id this directory declares; an unknown id is refused at load, by name,
104
+ * listing what is available), it never DEFINES. A guard is one of the two DATA
105
+ * conditions a route already has (`on <tool_name>`, `status=<outcome>`), and
106
+ * nothing else: a `when` predicate is CODE, no file can carry code, and that
107
+ * conditional stays in your source. See `skillsFromDirRoutes.ts` for the whole
108
+ * grammar and every refusal.
82
109
  *
83
110
  * A worked example feeding this into a graph:
84
111
  * `examples/features/47-skills-from-dir-graph.ts`.
@@ -109,6 +136,7 @@
109
136
  * import detonates a browser bundle at module-eval even when nothing calls it.
110
137
  */
111
138
  import { defineSkill } from './factories/defineSkill.js';
139
+ import { readDeclaredRoutes, toGraphSteps } from './skillsFromDirRoutes.js';
112
140
  /** The file name every skill folder is expected to use. */
113
141
  const SKILL_FILE = 'SKILL.md';
114
142
  /**
@@ -144,6 +172,68 @@ const NAMES_IN_REFUSAL = 20;
144
172
  * does not carry, or sequences a tool the file itself did not declare.
145
173
  */
146
174
  export async function skillsFromDir(dir, opts = {}) {
175
+ const { parsed, skills } = await loadSkillDir(dir, opts);
176
+ // A file that declares `routes:` loaded through THIS door would hand back a
177
+ // skill set with its routing silently dropped — the same failure the tool
178
+ // registry refuses ("a skill that loads without the tool it asked for still
179
+ // runs and still sounds sure of itself"). Refused by name, pointing at the
180
+ // door that reads the whole runbook.
181
+ const routing = parsed.find((skill) => skill.routes !== undefined);
182
+ if (routing !== undefined) {
183
+ throw new Error(`skillsFromDir: '${routing.file}' declares 'routes', and skillsFromDir returns SKILLS ` +
184
+ `only — the routing would be dropped without a word, leaving you a graph you thought ` +
185
+ `was declared on disk. Read the whole runbook instead: ` +
186
+ `const { skills, steps } = await runbookFromDir(dir, opts); ` +
187
+ `skillGraph({ skills, steps, start: '<entry id>' }). ` +
188
+ `(If 'routes' in this file belongs to another program, rename that key — it is a key ` +
189
+ `this loader now reads.)`);
190
+ }
191
+ return skills;
192
+ }
193
+ /**
194
+ * Load a directory as a whole RUNBOOK (9.43.0) — the skills AND the edges
195
+ * between them.
196
+ *
197
+ * `skillsFromDir` reads what one skill is; this reads what the skills are TO
198
+ * EACH OTHER, from each file's `routes:` key, and hands back both halves ready
199
+ * for `skillGraph({ ...runbook, start })`. Everything else is identical: same
200
+ * layouts, same frontmatter, same tool registry, same refusals.
201
+ *
202
+ * The routing a file may carry is deliberately small, and the boundary is a
203
+ * security property rather than a limitation — see `skillsFromDirRoutes.ts`:
204
+ * a route NAMES a skill this directory declares (an unknown id is refused at
205
+ * load, by name, listing what is available — never a half-graph), and a guard
206
+ * is one of the two DATA conditions a route already has (`on <tool>`,
207
+ * `status=<outcome>`). A `when` predicate is code, so no file can express one;
208
+ * that conditional stays in your source, where `skillGraph({ steps })` takes it.
209
+ *
210
+ * @param dir - A local filesystem path. Same rule as `skillsFromDir`.
211
+ * @param opts - Applied uniformly to every loaded skill.
212
+ *
213
+ * @throws everything `skillsFromDir` throws, plus: a route to an id no
214
+ * `SKILL.md` in the directory declares (the message lists what is
215
+ * available), a guard the grammar cannot express (the message quotes the
216
+ * whole grammar), a guard naming a tool the file itself does not declare,
217
+ * and a file routing to the same skill twice.
218
+ */
219
+ export async function runbookFromDir(dir, opts = {}) {
220
+ const { parsed, skills } = await loadSkillDir(dir, opts);
221
+ // Resolved only once every file is in hand: "which ids exist" is a fact about
222
+ // the DIRECTORY, and no single file can be asked it.
223
+ const known = new Set(parsed.map((skill) => skill.name));
224
+ const steps = parsed.flatMap((skill) => skill.routes === undefined ? [] : toGraphSteps(skill.name, skill.routes, skill.file, known));
225
+ return { skills, steps };
226
+ }
227
+ /**
228
+ * The shared load: read the directory, parse every file, resolve the tools,
229
+ * build the skills. Both doors run exactly this — they differ only in what they
230
+ * do with the ROUTING each file declared, which is the one thing a skill list
231
+ * cannot carry.
232
+ *
233
+ * Returns the parsed files and the built skills IN THE SAME ORDER (sorted by
234
+ * skill name), so a caller can pair them by index.
235
+ */
236
+ async function loadSkillDir(dir, opts) {
147
237
  assertLocalDirectoryArgument(dir);
148
238
  assertNoViaToolNameOption(opts);
149
239
  // Lazy node imports (browser-compat) — see module header.
@@ -205,10 +295,8 @@ export async function skillsFromDir(dir, opts = {}) {
205
295
  // shape. `undefined` (option omitted) is DISTINCT from an empty registry: the
206
296
  // refusals say different things, because the fixes are different.
207
297
  const registry = indexToolRegistry(opts.tools);
208
- return parsed
209
- .slice()
210
- .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))
211
- .map((skill) => {
298
+ const sorted = parsed.slice().sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
299
+ const skills = sorted.map((skill) => {
212
300
  const tools = resolveDeclaredTools(skill, opts.tools, registry);
213
301
  return defineSkill({
214
302
  id: skill.name,
@@ -220,6 +308,7 @@ export async function skillsFromDir(dir, opts = {}) {
220
308
  ...(opts.surfaceMode !== undefined && { surfaceMode: opts.surfaceMode }),
221
309
  });
222
310
  });
311
+ return { parsed: sorted, skills };
223
312
  }
224
313
  // ─── Authorship guard ──────────────────────────────────────────────
225
314
  /**
@@ -390,6 +479,7 @@ function parseSkillFile(raw, file) {
390
479
  const toolNames = readToolNames(fields, file);
391
480
  const steps = readSteps(fields, file, toolNames);
392
481
  const onSkip = readOnSkip(fields, file, steps);
482
+ const routes = readRoutes(fields, file, toolNames);
393
483
  return {
394
484
  name,
395
485
  description,
@@ -397,6 +487,7 @@ function parseSkillFile(raw, file) {
397
487
  ...(toolNames !== undefined && { toolNames }),
398
488
  ...(steps !== undefined && { steps }),
399
489
  ...(onSkip !== undefined && { onSkip }),
490
+ ...(routes !== undefined && { routes }),
400
491
  file,
401
492
  };
402
493
  }
@@ -553,6 +644,26 @@ function readSteps(fields, file, toolNames) {
553
644
  return { tool: item.key, note: item.value };
554
645
  });
555
646
  }
647
+ /**
648
+ * `routes:` → the edges out of this skill (9.43.0), as data.
649
+ *
650
+ * A block list, like `steps:` and for the same reason: routing is an ordered
651
+ * list of pairs (a target and its guard), and one line cannot carry that
652
+ * without a separator soup. The grammar itself — and every refusal in it —
653
+ * lives in `skillsFromDirRoutes.ts`; this reader only decides that the key is
654
+ * present and shaped like a list.
655
+ */
656
+ function readRoutes(fields, file, toolNames) {
657
+ const field = fields.get('routes');
658
+ if (field === undefined)
659
+ return undefined;
660
+ if (field.kind === 'scalar') {
661
+ throw new Error(`skillsFromDir: '${file}' writes 'routes' on one line, and routing is a LIST of edges ` +
662
+ `(each with its own guard). Write it as a block:\nroutes:\n - escalation: on ` +
663
+ `issue_refund status=denied\n - receipts: on issue_refund`);
664
+ }
665
+ return readDeclaredRoutes(field.items, file, toolNames);
666
+ }
556
667
  /** `onSkip:` → the skip policy. Refused here rather than at `defineSkill` so
557
668
  * the message can name the file the author has to open. */
558
669
  function readOnSkip(fields, file, steps) {