melchizedek-agents 0.18.0 → 0.20.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 (293) hide show
  1. package/.env.example +12 -0
  2. package/CHANGELOG.md +1001 -0
  3. package/DOCUMENTATION.md +252 -65
  4. package/QUICKSTART.md +9 -6
  5. package/README.md +42 -4
  6. package/config/agents/examples/harness.yaml +3 -1
  7. package/config/agents/examples/model_zoo.yaml +10 -9
  8. package/config/agents/syndicateSchema.yaml +53 -16
  9. package/config/agents/templates/account_memory.yaml +2 -6
  10. package/config/agents/templates/case_desk.yaml +3 -9
  11. package/config/agents/templates/draft_review.yaml +3 -9
  12. package/config/agents/templates/intake_extractor.yaml +1 -3
  13. package/config/agents/templates/research_brief.yaml +3 -9
  14. package/config/agents/templates/research_desk.yaml +3 -9
  15. package/config/agents/templates/review_panel.yaml +4 -12
  16. package/config/agents/templates/support_triage.yaml +4 -12
  17. package/config/agents/templates/systems_operator.yaml +2 -6
  18. package/db/hardening.sql +7 -1
  19. package/db/migrations/0013_tool_credentials.sql +260 -0
  20. package/dist/lib/a2a/app.d.ts +70 -6
  21. package/dist/lib/a2a/app.js +107 -2
  22. package/dist/lib/a2a/executor.d.ts +20 -3
  23. package/dist/lib/a2a/executor.js +66 -7
  24. package/dist/lib/a2a/remoteAgent.d.ts +24 -9
  25. package/dist/lib/a2a/remoteAgent.js +31 -14
  26. package/dist/lib/adkPeer.d.ts +63 -0
  27. package/dist/lib/adkPeer.js +240 -0
  28. package/dist/lib/compile.d.ts +159 -30
  29. package/dist/lib/compile.js +215 -134
  30. package/dist/lib/compileAdk.d.ts +32 -0
  31. package/dist/lib/compileAdk.js +142 -0
  32. package/dist/lib/compileNative.d.ts +111 -0
  33. package/dist/lib/compileNative.js +278 -0
  34. package/dist/lib/config.d.ts +6 -5
  35. package/dist/lib/config.js +6 -5
  36. package/dist/lib/doctor.d.ts +28 -1
  37. package/dist/lib/doctor.js +64 -2
  38. package/dist/lib/index.d.ts +6 -1
  39. package/dist/lib/index.js +7 -1
  40. package/dist/lib/loadSyndicate.d.ts +18 -0
  41. package/dist/lib/memory/erase.d.ts +5 -3
  42. package/dist/lib/memory/erase.js +5 -2
  43. package/dist/lib/memory/namespace.d.ts +8 -1
  44. package/dist/lib/memory/namespace.js +12 -6
  45. package/dist/lib/memory/providers.js +2 -1
  46. package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
  47. package/dist/lib/memory/supabaseMemoryService.js +42 -2
  48. package/dist/lib/model.d.ts +47 -0
  49. package/dist/lib/model.js +37 -0
  50. package/dist/lib/models/adapterResolver.d.ts +115 -0
  51. package/dist/lib/models/adapterResolver.js +137 -0
  52. package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
  53. package/dist/lib/models/adkGeminiAdapter.js +369 -0
  54. package/dist/lib/models/adkShim.d.ts +111 -0
  55. package/dist/lib/models/adkShim.js +136 -0
  56. package/dist/lib/models/capabilities.d.ts +3 -0
  57. package/dist/lib/models/capabilities.js +33 -23
  58. package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
  59. package/dist/lib/models/chatCompletionsAdapter.js +854 -0
  60. package/dist/lib/models/circuitBreaker.d.ts +42 -0
  61. package/dist/lib/models/circuitBreaker.js +70 -0
  62. package/dist/lib/models/claudeAdapter.d.ts +103 -0
  63. package/dist/lib/models/claudeAdapter.js +669 -0
  64. package/dist/lib/models/claudeLlm.d.ts +39 -62
  65. package/dist/lib/models/claudeLlm.js +45 -350
  66. package/dist/lib/models/claudeModels.d.ts +127 -0
  67. package/dist/lib/models/claudeModels.js +237 -0
  68. package/dist/lib/models/contract.d.ts +395 -0
  69. package/dist/lib/models/contract.js +39 -0
  70. package/dist/lib/models/endpoints.js +2 -1
  71. package/dist/lib/models/errorResponse.d.ts +50 -0
  72. package/dist/lib/models/errorResponse.js +48 -0
  73. package/dist/lib/models/fallback.d.ts +18 -11
  74. package/dist/lib/models/fallback.js +54 -43
  75. package/dist/lib/models/fallbackAdapter.d.ts +55 -0
  76. package/dist/lib/models/fallbackAdapter.js +62 -0
  77. package/dist/lib/models/gateway.js +2 -1
  78. package/dist/lib/models/gatewayAdapter.d.ts +51 -0
  79. package/dist/lib/models/gatewayAdapter.js +81 -0
  80. package/dist/lib/models/gatewayLlm.d.ts +12 -33
  81. package/dist/lib/models/gatewayLlm.js +16 -77
  82. package/dist/lib/models/geminiAdapter.d.ts +125 -0
  83. package/dist/lib/models/geminiAdapter.js +854 -0
  84. package/dist/lib/models/geminiState.d.ts +46 -0
  85. package/dist/lib/models/geminiState.js +51 -0
  86. package/dist/lib/models/genaiMapping.d.ts +283 -0
  87. package/dist/lib/models/genaiMapping.js +965 -0
  88. package/dist/lib/models/gptAdapter.d.ts +240 -0
  89. package/dist/lib/models/gptAdapter.js +804 -0
  90. package/dist/lib/models/gptLlm.d.ts +63 -123
  91. package/dist/lib/models/gptLlm.js +89 -603
  92. package/dist/lib/models/grokAdapter.d.ts +86 -0
  93. package/dist/lib/models/grokAdapter.js +142 -0
  94. package/dist/lib/models/grokLlm.d.ts +22 -45
  95. package/dist/lib/models/grokLlm.js +24 -69
  96. package/dist/lib/models/kimiAdapter.d.ts +157 -0
  97. package/dist/lib/models/kimiAdapter.js +203 -0
  98. package/dist/lib/models/kimiLlm.d.ts +15 -107
  99. package/dist/lib/models/kimiLlm.js +19 -155
  100. package/dist/lib/models/ollamaAdapter.d.ts +125 -0
  101. package/dist/lib/models/ollamaAdapter.js +155 -0
  102. package/dist/lib/models/ollamaLlm.d.ts +11 -107
  103. package/dist/lib/models/ollamaLlm.js +15 -153
  104. package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
  105. package/dist/lib/models/openAiCompatibleLlm.js +109 -671
  106. package/dist/lib/models/providerState.d.ts +63 -0
  107. package/dist/lib/models/providerState.js +61 -0
  108. package/dist/lib/models/reasoning.d.ts +26 -0
  109. package/dist/lib/models/reasoning.js +59 -0
  110. package/dist/lib/models/registry.d.ts +62 -35
  111. package/dist/lib/models/registry.js +100 -124
  112. package/dist/lib/models/retry.d.ts +2 -1
  113. package/dist/lib/models/retry.js +3 -2
  114. package/dist/lib/models/schemaNormalize.d.ts +98 -0
  115. package/dist/lib/models/schemaNormalize.js +321 -1
  116. package/dist/lib/models/tracedGemini.d.ts +41 -0
  117. package/dist/lib/models/tracedGemini.js +98 -0
  118. package/dist/lib/models/urls.d.ts +11 -0
  119. package/dist/lib/models/urls.js +16 -0
  120. package/dist/lib/net/redirects.d.ts +21 -2
  121. package/dist/lib/net/redirects.js +11 -34
  122. package/dist/lib/observability/audit.d.ts +5 -1
  123. package/dist/lib/observability/lineage.d.ts +14 -0
  124. package/dist/lib/observability/lineage.js +33 -0
  125. package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
  126. package/dist/lib/observability/supabaseSpanExporter.js +17 -9
  127. package/dist/lib/observability/tracer.d.ts +26 -8
  128. package/dist/lib/observability/tracer.js +43 -18
  129. package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
  130. package/dist/lib/runtime/adkMemoryBridge.js +68 -0
  131. package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
  132. package/dist/lib/runtime/adkSessionBridge.js +176 -0
  133. package/dist/lib/runtime/approvals.d.ts +5 -0
  134. package/dist/lib/runtime/approvals.js +39 -8
  135. package/dist/lib/runtime/credentials.d.ts +61 -0
  136. package/dist/lib/runtime/credentials.js +67 -0
  137. package/dist/lib/runtime/events.d.ts +229 -0
  138. package/dist/lib/runtime/events.js +274 -0
  139. package/dist/lib/runtime/logging.d.ts +32 -0
  140. package/dist/lib/runtime/logging.js +55 -0
  141. package/dist/lib/runtime/memoryService.d.ts +72 -0
  142. package/dist/lib/runtime/memoryService.js +25 -0
  143. package/dist/lib/runtime/native/agentLoop.d.ts +209 -0
  144. package/dist/lib/runtime/native/agentLoop.js +705 -0
  145. package/dist/lib/runtime/native/compaction.d.ts +103 -0
  146. package/dist/lib/runtime/native/compaction.js +191 -0
  147. package/dist/lib/runtime/native/delegate.d.ts +151 -0
  148. package/dist/lib/runtime/native/delegate.js +256 -0
  149. package/dist/lib/runtime/native/history.d.ts +83 -0
  150. package/dist/lib/runtime/native/history.js +312 -0
  151. package/dist/lib/runtime/native/interrupts.d.ts +129 -0
  152. package/dist/lib/runtime/native/interrupts.js +310 -0
  153. package/dist/lib/runtime/native/request.d.ts +215 -0
  154. package/dist/lib/runtime/native/request.js +585 -0
  155. package/dist/lib/runtime/native/selfCorrection.d.ts +164 -0
  156. package/dist/lib/runtime/native/selfCorrection.js +327 -0
  157. package/dist/lib/runtime/native/step.d.ts +156 -0
  158. package/dist/lib/runtime/native/step.js +263 -0
  159. package/dist/lib/runtime/native/taskMode.d.ts +64 -0
  160. package/dist/lib/runtime/native/taskMode.js +126 -0
  161. package/dist/lib/runtime/native/telemetry.d.ts +75 -0
  162. package/dist/lib/runtime/native/telemetry.js +299 -0
  163. package/dist/lib/runtime/native/tempState.d.ts +31 -0
  164. package/dist/lib/runtime/native/tempState.js +56 -0
  165. package/dist/lib/runtime/nativeTurn.d.ts +86 -0
  166. package/dist/lib/runtime/nativeTurn.js +107 -0
  167. package/dist/lib/runtime/questions.d.ts +29 -7
  168. package/dist/lib/runtime/questions.js +47 -32
  169. package/dist/lib/runtime/runtimeFlag.d.ts +38 -0
  170. package/dist/lib/runtime/runtimeFlag.js +58 -0
  171. package/dist/lib/runtime/sessions.d.ts +166 -0
  172. package/dist/lib/runtime/sessions.js +201 -0
  173. package/dist/lib/runtime/syndicateTurn.d.ts +46 -5
  174. package/dist/lib/runtime/syndicateTurn.js +233 -36
  175. package/dist/lib/runtime/valueDepth.d.ts +23 -0
  176. package/dist/lib/runtime/valueDepth.js +41 -0
  177. package/dist/lib/session/supabaseSessionService.d.ts +65 -18
  178. package/dist/lib/session/supabaseSessionService.js +148 -105
  179. package/dist/lib/session/transcript.d.ts +69 -23
  180. package/dist/lib/session/transcript.js +105 -29
  181. package/dist/lib/skills.js +33 -7
  182. package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
  183. package/dist/lib/storage/postgres/credentialStore.js +82 -0
  184. package/dist/lib/storage/postgres/index.d.ts +12 -0
  185. package/dist/lib/storage/postgres/index.js +6 -1
  186. package/dist/lib/storage/postgres/sessionService.d.ts +44 -16
  187. package/dist/lib/storage/postgres/sessionService.js +128 -66
  188. package/dist/lib/syndicateSchema.d.ts +49 -0
  189. package/dist/lib/syndicateSchema.js +99 -9
  190. package/dist/lib/toolRegistry.d.ts +26 -2
  191. package/dist/lib/toolRegistry.js +73 -17
  192. package/dist/lib/tools/adkTool.d.ts +81 -0
  193. package/dist/lib/tools/adkTool.js +268 -0
  194. package/dist/lib/tools/auth.d.ts +118 -0
  195. package/dist/lib/tools/auth.js +89 -0
  196. package/dist/lib/tools/collectionsSearchTool.d.ts +8 -7
  197. package/dist/lib/tools/collectionsSearchTool.js +12 -27
  198. package/dist/lib/tools/credentialCipher.d.ts +58 -0
  199. package/dist/lib/tools/credentialCipher.js +116 -0
  200. package/dist/lib/tools/credentialStore.d.ts +77 -0
  201. package/dist/lib/tools/credentialStore.js +246 -0
  202. package/dist/lib/tools/examples.d.ts +43 -0
  203. package/dist/lib/tools/examples.js +62 -0
  204. package/dist/lib/tools/generateImageTool.d.ts +15 -6
  205. package/dist/lib/tools/generateImageTool.js +19 -29
  206. package/dist/lib/tools/inspectImageTool.d.ts +9 -6
  207. package/dist/lib/tools/inspectImageTool.js +13 -17
  208. package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
  209. package/dist/lib/tools/mcpToolFactory.js +46 -47
  210. package/dist/lib/tools/memoryTools.d.ts +61 -0
  211. package/dist/lib/tools/memoryTools.js +119 -0
  212. package/dist/lib/tools/nativeTools.d.ts +38 -0
  213. package/dist/lib/tools/nativeTools.js +43 -0
  214. package/dist/lib/tools/oauthConsent.d.ts +181 -0
  215. package/dist/lib/tools/oauthConsent.js +330 -0
  216. package/dist/lib/tools/openapi/call.d.ts +108 -0
  217. package/dist/lib/tools/openapi/call.js +295 -0
  218. package/dist/lib/tools/openapi/parse.d.ts +147 -0
  219. package/dist/lib/tools/openapi/parse.js +570 -0
  220. package/dist/lib/tools/openapiTools.d.ts +25 -17
  221. package/dist/lib/tools/openapiTools.js +124 -121
  222. package/dist/lib/tools/scienceTools.d.ts +14 -14
  223. package/dist/lib/tools/skillToolset.d.ts +58 -87
  224. package/dist/lib/tools/skillToolset.js +61 -142
  225. package/dist/lib/tools/skills/env.d.ts +58 -0
  226. package/dist/lib/tools/skills/env.js +165 -0
  227. package/dist/lib/tools/skills/executor.d.ts +114 -0
  228. package/dist/lib/tools/skills/executor.js +331 -0
  229. package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
  230. package/dist/lib/tools/skills/frontmatter.js +215 -0
  231. package/dist/lib/tools/skills/loader.d.ts +91 -0
  232. package/dist/lib/tools/skills/loader.js +257 -0
  233. package/dist/lib/tools/skills/tools.d.ts +187 -0
  234. package/dist/lib/tools/skills/tools.js +386 -0
  235. package/dist/lib/tools/taskTools.d.ts +15 -15
  236. package/dist/lib/tools/tool.d.ts +354 -0
  237. package/dist/lib/tools/tool.js +304 -0
  238. package/dist/lib/tools/toolContract.d.ts +89 -33
  239. package/dist/lib/tools/toolContract.js +104 -82
  240. package/dist/lib/tools/urlContextTool.d.ts +4 -1
  241. package/dist/lib/tools/urlContextTool.js +6 -2
  242. package/dist/lib/tools/webExtractTool.d.ts +3 -3
  243. package/dist/lib/tools/webExtractTool.js +2 -1
  244. package/dist/lib/tools/webSearchTool.d.ts +7 -10
  245. package/dist/lib/tools/webSearchTool.js +15 -60
  246. package/dist/lib/tools/wikiTools.d.ts +19 -19
  247. package/dist/lib/tools/wikiTools.js +13 -9
  248. package/dist/lib/tools/xApiSearchTool.d.ts +3 -3
  249. package/dist/lib/tools/xApiSearchTool.js +2 -1
  250. package/dist/lib/tools/xSearchTool.d.ts +8 -10
  251. package/dist/lib/tools/xSearchTool.js +12 -63
  252. package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
  253. package/dist/lib/tools/xaiSearchParams.js +126 -0
  254. package/dist/lib/wiki/agentRun.d.ts +12 -0
  255. package/dist/lib/wiki/agentRun.js +74 -35
  256. package/dist/lib/wiki/entities.d.ts +12 -0
  257. package/dist/lib/wiki/entities.js +45 -3
  258. package/dist/lib/workflow/agentNode.d.ts +153 -0
  259. package/dist/lib/workflow/agentNode.js +365 -0
  260. package/dist/lib/workflow/graph.d.ts +178 -0
  261. package/dist/lib/workflow/graph.js +468 -0
  262. package/dist/lib/workflow/nodeEvents.d.ts +50 -0
  263. package/dist/lib/workflow/nodeEvents.js +101 -0
  264. package/dist/lib/workflow/pause.d.ts +122 -0
  265. package/dist/lib/workflow/pause.js +180 -0
  266. package/dist/lib/workflow/resume.d.ts +166 -0
  267. package/dist/lib/workflow/resume.js +441 -0
  268. package/dist/lib/workflow/route.d.ts +58 -0
  269. package/dist/lib/workflow/route.js +69 -0
  270. package/dist/lib/workflow/scheduler.d.ts +418 -0
  271. package/dist/lib/workflow/scheduler.js +756 -0
  272. package/dist/lib/workflow/toolNode.d.ts +127 -0
  273. package/dist/lib/workflow/toolNode.js +236 -0
  274. package/dist/lib/workflow/turn.d.ts +118 -0
  275. package/dist/lib/workflow/turn.js +216 -0
  276. package/dist/lib/workflow.d.ts +33 -11
  277. package/dist/lib/workflow.js +49 -27
  278. package/dist/lib/workflowConfig.d.ts +7 -2
  279. package/dist/lib/workflowConfig.js +7 -12
  280. package/dist/scripts/a2a_server.js +4 -3
  281. package/dist/scripts/assistant_worker.d.ts +2 -1
  282. package/dist/scripts/assistant_worker.js +9 -4
  283. package/dist/scripts/doctor.d.ts +3 -1
  284. package/dist/scripts/doctor.js +4 -2
  285. package/dist/scripts/syndicate_chat.js +19 -10
  286. package/package.json +9 -3
  287. package/skills/briefs/melchizedek-author.md +3 -2
  288. package/skills/briefs/melchizedek-models.md +13 -9
  289. package/skills/briefs/melchizedek.md +3 -3
  290. package/skills/melchizedek/SKILL.md +2 -1
  291. package/skills/melchizedek-author/SKILL.md +2 -1
  292. package/skills/melchizedek-author/assets/minimal.yaml +2 -0
  293. package/skills/melchizedek-models/SKILL.md +23 -6
@@ -0,0 +1,91 @@
1
+ /**
2
+ * lib/tools/skills/loader.ts — a directory of skills, loaded by the engine
3
+ * (WS3-3, ADR 0083).
4
+ *
5
+ * WHY this file exists:
6
+ * The skills harness (lib/tools/skillToolset.ts, ADR 0029) loaded skills
7
+ * through ADK's `loadAllSkillsInDir` until WS3-3. This module walks the
8
+ * same layout to the same objects, so a skill reads the same on either
9
+ * runtime:
10
+ * - a skill is a directory, other than the base itself, holding a file
11
+ * named SKILL.md in any case; its name must equal the directory's;
12
+ * - a directory that is not a skill is searched for skills below it;
13
+ * - a skill's `references/`, `assets/` and `scripts/` are read whole,
14
+ * each file keyed by its path inside that directory: UTF-8 text as a
15
+ * string, anything else as a Buffer; a script is `{ src }` and only a
16
+ * text one is kept;
17
+ * - build and tool directories (node_modules, .git, __pycache__, …) and
18
+ * compiled files (.pyc, …) are skipped, as are symbolic links, which a
19
+ * directory listing never reports as a file or a directory;
20
+ * - an invalid skill is skipped (validateSkillDir says why).
21
+ *
22
+ * WHAT IS BOUNDED (none of these limits exists in ADK's loader):
23
+ * SKILL_LIMITS caps a SKILL.md's size, each resource file's size, the
24
+ * files one skill may ship, and how deep the walk goes. A resource over
25
+ * its limit is skipped and reported through `onWarning`; nothing larger is
26
+ * read into memory.
27
+ *
28
+ * The files are the operator's: the YAML's `skills.dir` names the shelf,
29
+ * and nothing a model sends reaches this module.
30
+ */
31
+ import type { SkillFrontmatter } from './frontmatter.ts';
32
+ /** A file a skill ships: text as a string, binary as a Buffer. */
33
+ export type SkillFile = string | Buffer;
34
+ /** One skill, loaded: the shape load_skill and the tools read. */
35
+ export interface Skill {
36
+ frontmatter: SkillFrontmatter;
37
+ /** The SKILL.md body: the procedure. */
38
+ instructions: string;
39
+ resources: {
40
+ references: Record<string, SkillFile>;
41
+ assets: Record<string, SkillFile>;
42
+ scripts: Record<string, {
43
+ src: string;
44
+ }>;
45
+ };
46
+ }
47
+ /** What the loader refuses to read. */
48
+ /**
49
+ * A file's bytes read through one open handle, so the size checked is the
50
+ * size of the file read (no check-then-read race): undefined when it holds
51
+ * more than `maxBytes`, or is not a regular file.
52
+ */
53
+ export declare function readFileBounded(file: string, maxBytes: number): Promise<Buffer | undefined>;
54
+ export declare const SKILL_LIMITS: {
55
+ /** Bytes a SKILL.md may hold. */
56
+ readonly skillMdBytes: number;
57
+ /** Bytes one resource file may hold; a larger one is skipped. */
58
+ readonly resourceBytes: number;
59
+ /** Resource files one skill may ship; those past it are skipped. */
60
+ readonly resourceFiles: 1000;
61
+ /** Directory levels below the base (or below a resource directory) the walk descends. */
62
+ readonly depth: 12;
63
+ };
64
+ /** Directories never searched for skills or resources (ADK's list). */
65
+ export declare const IGNORED_DIRECTORIES: ReadonlySet<string>;
66
+ /** Files never read as resources (ADK's list). */
67
+ export declare const IGNORED_EXTENSIONS: ReadonlySet<string>;
68
+ /** Where the loader reports what it skipped for a limit. */
69
+ export interface LoadOptions {
70
+ onWarning?: (message: string) => void;
71
+ }
72
+ /** The SKILL.md in `skillDir`, checked; throws ADK's texts when it is missing or invalid. */
73
+ export declare function loadSkillFile(skillDir: string): Promise<{
74
+ frontmatter: SkillFrontmatter;
75
+ instructions: string;
76
+ }>;
77
+ /** One skill directory: its SKILL.md and the files it ships. */
78
+ export declare function loadSkillFromDir(skillDir: string, opts?: LoadOptions): Promise<Skill>;
79
+ /**
80
+ * Why `skillDir` is not a valid skill, or nothing: ADK's validateSkillDir.
81
+ * A SKILL.md that cannot be read or parsed is one problem; otherwise each
82
+ * frontmatter key the standard does not name, and a name that is not the
83
+ * directory's.
84
+ */
85
+ export declare function validateSkillDir(skillDir: string): Promise<string[]>;
86
+ /**
87
+ * Every valid skill under `basePath`, by name, in the order the walk finds
88
+ * them (a later skill of the same name replaces an earlier one, as in ADK).
89
+ * A base that is not a readable directory yields none.
90
+ */
91
+ export declare function loadAllSkillsInDir(basePath: string, opts?: LoadOptions): Promise<Record<string, Skill>>;
@@ -0,0 +1,257 @@
1
+ /**
2
+ * lib/tools/skills/loader.ts — a directory of skills, loaded by the engine
3
+ * (WS3-3, ADR 0083).
4
+ *
5
+ * WHY this file exists:
6
+ * The skills harness (lib/tools/skillToolset.ts, ADR 0029) loaded skills
7
+ * through ADK's `loadAllSkillsInDir` until WS3-3. This module walks the
8
+ * same layout to the same objects, so a skill reads the same on either
9
+ * runtime:
10
+ * - a skill is a directory, other than the base itself, holding a file
11
+ * named SKILL.md in any case; its name must equal the directory's;
12
+ * - a directory that is not a skill is searched for skills below it;
13
+ * - a skill's `references/`, `assets/` and `scripts/` are read whole,
14
+ * each file keyed by its path inside that directory: UTF-8 text as a
15
+ * string, anything else as a Buffer; a script is `{ src }` and only a
16
+ * text one is kept;
17
+ * - build and tool directories (node_modules, .git, __pycache__, …) and
18
+ * compiled files (.pyc, …) are skipped, as are symbolic links, which a
19
+ * directory listing never reports as a file or a directory;
20
+ * - an invalid skill is skipped (validateSkillDir says why).
21
+ *
22
+ * WHAT IS BOUNDED (none of these limits exists in ADK's loader):
23
+ * SKILL_LIMITS caps a SKILL.md's size, each resource file's size, the
24
+ * files one skill may ship, and how deep the walk goes. A resource over
25
+ * its limit is skipped and reported through `onWarning`; nothing larger is
26
+ * read into memory.
27
+ *
28
+ * The files are the operator's: the YAML's `skills.dir` names the shelf,
29
+ * and nothing a model sends reaches this module.
30
+ */
31
+ import { isUtf8 } from 'node:buffer';
32
+ import { constants as fsConstants } from 'node:fs';
33
+ import * as fs from 'node:fs/promises';
34
+ import * as path from 'node:path';
35
+ import { ALLOWED_FRONTMATTER_KEYS, MAX_SKILL_MD_CHARS, parseSkillMdContent } from './frontmatter.js';
36
+ /** What the loader refuses to read. */
37
+ /**
38
+ * A file's bytes read through one open handle, so the size checked is the
39
+ * size of the file read (no check-then-read race): undefined when it holds
40
+ * more than `maxBytes`, or is not a regular file.
41
+ */
42
+ export async function readFileBounded(file, maxBytes) {
43
+ const handle = await fs.open(file, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
44
+ try {
45
+ const stat = await handle.stat();
46
+ if (!stat.isFile() || stat.size > maxBytes)
47
+ return undefined;
48
+ const buffer = Buffer.alloc(maxBytes + 1);
49
+ let length = 0;
50
+ for (;;) {
51
+ const { bytesRead } = await handle.read(buffer, length, buffer.length - length, length);
52
+ if (bytesRead === 0)
53
+ break;
54
+ length += bytesRead;
55
+ if (length > maxBytes)
56
+ return undefined;
57
+ }
58
+ return buffer.subarray(0, length);
59
+ }
60
+ finally {
61
+ await handle.close();
62
+ }
63
+ }
64
+ export const SKILL_LIMITS = {
65
+ /** Bytes a SKILL.md may hold. */
66
+ skillMdBytes: MAX_SKILL_MD_CHARS,
67
+ /** Bytes one resource file may hold; a larger one is skipped. */
68
+ resourceBytes: 8 * 1024 * 1024,
69
+ /** Resource files one skill may ship; those past it are skipped. */
70
+ resourceFiles: 1000,
71
+ /** Directory levels below the base (or below a resource directory) the walk descends. */
72
+ depth: 12,
73
+ };
74
+ /** Directories never searched for skills or resources (ADK's list). */
75
+ export const IGNORED_DIRECTORIES = new Set([
76
+ '__pycache__',
77
+ '.pytest_cache',
78
+ '.mypy_cache',
79
+ '.ruff_cache',
80
+ 'node_modules',
81
+ 'coverage',
82
+ 'venv',
83
+ '.venv',
84
+ 'env',
85
+ '.env',
86
+ '.git',
87
+ '.vscode',
88
+ '.idea',
89
+ ]);
90
+ /** Files never read as resources (ADK's list). */
91
+ export const IGNORED_EXTENSIONS = new Set(['.pyc', '.pyo', '.pyd', '.tsbuildinfo', '.DS_Store']);
92
+ function decode(data) {
93
+ return isUtf8(data) ? data.toString('utf-8') : data;
94
+ }
95
+ /** Every file under `directoryPath`, keyed by its relative path, within the limits. Absent: none. */
96
+ async function loadDir(directoryPath, budget, opts) {
97
+ const files = {};
98
+ const walk = async (dir, depth) => {
99
+ if (depth > SKILL_LIMITS.depth) {
100
+ opts.onWarning?.(`skipped '${dir}': deeper than ${SKILL_LIMITS.depth} levels`);
101
+ return;
102
+ }
103
+ const entries = await fs.readdir(dir, { withFileTypes: true });
104
+ for (const entry of entries) {
105
+ const fullPath = path.join(dir, entry.name);
106
+ if (entry.isDirectory()) {
107
+ if (IGNORED_DIRECTORIES.has(entry.name))
108
+ continue;
109
+ await walk(fullPath, depth + 1);
110
+ }
111
+ else if (entry.isFile()) {
112
+ if (IGNORED_EXTENSIONS.has(path.extname(entry.name)))
113
+ continue;
114
+ const relativePath = path.relative(directoryPath, fullPath);
115
+ if (budget.files <= 0) {
116
+ opts.onWarning?.(`skipped '${fullPath}': a skill ships at most ${SKILL_LIMITS.resourceFiles} files`);
117
+ continue;
118
+ }
119
+ const bytes = await readFileBounded(fullPath, SKILL_LIMITS.resourceBytes);
120
+ if (!bytes) {
121
+ opts.onWarning?.(`skipped '${fullPath}': larger than ${SKILL_LIMITS.resourceBytes} bytes`);
122
+ continue;
123
+ }
124
+ budget.files -= 1;
125
+ Object.defineProperty(files, relativePath, { value: decode(bytes), enumerable: true, writable: true, configurable: true });
126
+ }
127
+ }
128
+ };
129
+ let isDir = false;
130
+ try {
131
+ isDir = (await fs.stat(directoryPath)).isDirectory();
132
+ }
133
+ catch {
134
+ return files; // no such directory: the skill ships none of this kind
135
+ }
136
+ if (isDir) {
137
+ try {
138
+ await walk(directoryPath, 0);
139
+ }
140
+ catch (e) {
141
+ opts.onWarning?.(`failed to load directory '${directoryPath}': ${e.message}`);
142
+ }
143
+ }
144
+ return files;
145
+ }
146
+ /** The SKILL.md in `skillDir`, checked; throws ADK's texts when it is missing or invalid. */
147
+ export async function loadSkillFile(skillDir) {
148
+ const resolvedDir = path.resolve(skillDir);
149
+ let content;
150
+ let entries = [];
151
+ try {
152
+ entries = await fs.readdir(resolvedDir, { withFileTypes: true });
153
+ }
154
+ catch {
155
+ entries = [];
156
+ }
157
+ for (const entry of entries) {
158
+ if (!entry.isFile() || entry.name.toLowerCase() !== 'skill.md')
159
+ continue;
160
+ const file = path.join(resolvedDir, entry.name);
161
+ try {
162
+ const bytes = await readFileBounded(file, SKILL_LIMITS.skillMdBytes);
163
+ if (!bytes)
164
+ throw new Error(`SKILL.md is larger than ${SKILL_LIMITS.skillMdBytes} bytes`);
165
+ content = bytes.toString('utf-8');
166
+ break;
167
+ }
168
+ catch (e) {
169
+ if (e.message.startsWith('SKILL.md is larger'))
170
+ throw e;
171
+ }
172
+ }
173
+ if (content === undefined)
174
+ throw new Error(`SKILL.md (or any case variation like skill.md) not found in '${skillDir}'.`);
175
+ const { frontmatter, body } = parseSkillMdContent(content);
176
+ const dirName = path.basename(resolvedDir);
177
+ if (dirName !== frontmatter.name)
178
+ throw new Error(`Skill name '${frontmatter.name}' does not match directory name '${dirName}'.`);
179
+ return { frontmatter, instructions: body };
180
+ }
181
+ /** One skill directory: its SKILL.md and the files it ships. */
182
+ export async function loadSkillFromDir(skillDir, opts = {}) {
183
+ const resolvedDir = path.resolve(skillDir);
184
+ const skill = await loadSkillFile(skillDir);
185
+ const budget = { files: SKILL_LIMITS.resourceFiles };
186
+ const references = await loadDir(path.join(resolvedDir, 'references'), budget, opts);
187
+ const assets = await loadDir(path.join(resolvedDir, 'assets'), budget, opts);
188
+ const rawScripts = await loadDir(path.join(resolvedDir, 'scripts'), budget, opts);
189
+ const scripts = {};
190
+ for (const [name, src] of Object.entries(rawScripts)) {
191
+ if (typeof src === 'string')
192
+ Object.defineProperty(scripts, name, { value: { src }, enumerable: true, writable: true, configurable: true });
193
+ }
194
+ return { ...skill, resources: { references, assets, scripts } };
195
+ }
196
+ /**
197
+ * Why `skillDir` is not a valid skill, or nothing: ADK's validateSkillDir.
198
+ * A SKILL.md that cannot be read or parsed is one problem; otherwise each
199
+ * frontmatter key the standard does not name, and a name that is not the
200
+ * directory's.
201
+ */
202
+ export async function validateSkillDir(skillDir) {
203
+ const resolvedDir = path.resolve(skillDir);
204
+ let skill;
205
+ try {
206
+ skill = await loadSkillFile(resolvedDir);
207
+ }
208
+ catch (e) {
209
+ return [e.message];
210
+ }
211
+ const problems = [];
212
+ const unknown = Object.keys(skill.frontmatter).filter((k) => !ALLOWED_FRONTMATTER_KEYS.has(k));
213
+ if (unknown.length > 0)
214
+ problems.push(`Unknown frontmatter fields: [${unknown.sort().join(', ')}]`);
215
+ const dirName = path.basename(resolvedDir);
216
+ if (dirName !== skill.frontmatter.name)
217
+ problems.push(`Skill name '${skill.frontmatter.name}' does not match directory name '${dirName}'.`);
218
+ return problems;
219
+ }
220
+ /**
221
+ * Every valid skill under `basePath`, by name, in the order the walk finds
222
+ * them (a later skill of the same name replaces an earlier one, as in ADK).
223
+ * A base that is not a readable directory yields none.
224
+ */
225
+ export async function loadAllSkillsInDir(basePath, opts = {}) {
226
+ const resolvedPath = path.resolve(basePath);
227
+ const skills = {};
228
+ const scan = async (currentDir, depth) => {
229
+ if (depth > SKILL_LIMITS.depth)
230
+ return;
231
+ let entries;
232
+ try {
233
+ entries = await fs.readdir(currentDir, { withFileTypes: true });
234
+ }
235
+ catch {
236
+ return;
237
+ }
238
+ const isSkillDir = currentDir !== resolvedPath && entries.some((e) => e.isFile() && e.name.toLowerCase() === 'skill.md');
239
+ if (isSkillDir) {
240
+ try {
241
+ const skill = await loadSkillFromDir(currentDir, opts);
242
+ Object.defineProperty(skills, skill.frontmatter.name, { value: skill, enumerable: true, writable: true, configurable: true });
243
+ }
244
+ catch {
245
+ // Skipped: validateSkillDir (skillSuiteProblems) reports why.
246
+ }
247
+ return;
248
+ }
249
+ for (const entry of entries) {
250
+ if (!entry.isDirectory() || IGNORED_DIRECTORIES.has(entry.name))
251
+ continue;
252
+ await scan(path.join(currentDir, entry.name), depth + 1);
253
+ }
254
+ };
255
+ await scan(resolvedPath, 0);
256
+ return skills;
257
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * lib/tools/skills/tools.ts — the skill tools and the toolset that holds
3
+ * them, on the engine's own tool base (lib/tools/tool.ts; WS3-3, ADR 0083).
4
+ *
5
+ * WHY this file exists:
6
+ * The skills harness (ADR 0029) was built on ADK's SkillToolset until
7
+ * WS3-3. These are the same tools as own Tools, so the native loop runs
8
+ * them directly and the ADK runtime runs them through the one tool
9
+ * adapter (lib/tools/adkTool.ts, toAdkToolset). A model is sent the same
10
+ * names, descriptions and parameters as before, and reads the same
11
+ * results and error texts, on either runtime:
12
+ * - list_skills: the `<available_skills>` index (ADK's ListSkillsTool).
13
+ * The harness leaves it out, since the index is in the instruction.
14
+ * - load_skill: the procedure, the frontmatter and the NAMES of the files
15
+ * the skill ships (ADR 0029's lean load), and it activates the skill.
16
+ * - load_skill_resource: one of those files (ADK's tool, word for word).
17
+ * A binary file is shown to the model as inline data in the next
18
+ * request, through the Tool's `contents` hook, as ADK's tool did in its
19
+ * processLlmRequest.
20
+ * - run_skill_script: asks a person to approve, then runs one script the
21
+ * skill ships on the local executor (./executor.ts).
22
+ *
23
+ * ACTIVATION: load_skill appends the skill's name to the session state key
24
+ * `_adk_activated_skill_<agent>` (ADK's key, so a session stored by
25
+ * either runtime reads the same). SkillToolset.getTools reads it and adds
26
+ * the tools the activated skills name (`metadata.adk_additional_tools`,
27
+ * filled from `allowed-tools`), but only from the tools the YAML handed
28
+ * it under `skills.tools`: a skill cannot grant itself a tool the YAML
29
+ * withheld.
30
+ *
31
+ * WHY NOT defineTool: a defineTool contract validates its arguments with
32
+ * zod and answers a bad call with its own error string. These tools keep
33
+ * ADK's answers to a bad call (`{ error, error_code }`), so their
34
+ * parameters are written here as JSON Schema and their arguments checked
35
+ * in execute.
36
+ *
37
+ * UNTRUSTED INPUT: every argument is the model's. A skill name and a file
38
+ * path are looked up as own keys of the loaded maps (never on a
39
+ * prototype), so nothing a model sends opens a file the skill did not
40
+ * ship or names a script outside its `scripts/`.
41
+ */
42
+ import type { ToolDeclaration } from '../../models/contract.ts';
43
+ import type { TurnContent } from '../../runtime/events.ts';
44
+ import type { Tool, ToolContext, Toolset, ToolsetContext } from '../tool.ts';
45
+ import { LocalScriptExecutor } from './executor.ts';
46
+ import type { Skill } from './loader.ts';
47
+ /** The `<available_skills>` index, in the skills' order (ADK's formatSkillsAsXml). */
48
+ export declare function formatSkillsAsXml(skills: Skill[]): string;
49
+ /** The session state key holding the skills an agent has loaded (ADK's). */
50
+ export declare function activatedSkillsKey(agentName: string | undefined): string;
51
+ /** Seconds a skill script may run before the local executor kills it. */
52
+ export declare const SCRIPT_TIMEOUT_SECONDS = 120;
53
+ export interface SkillToolsetOptions {
54
+ /** `local` runs a skill's scripts on this machine, each after approval; otherwise run_skill_script is absent. */
55
+ scripts?: 'none' | 'local';
56
+ /** The tools a loaded skill may unlock by name (the YAML's `skills.tools`, resolved). */
57
+ additionalTools?: readonly unknown[];
58
+ /** Variable names a script gets beyond the base allowlist (the YAML's `skills.env` and `skills.secret_env`; ADR 0086). */
59
+ envNames?: readonly string[];
60
+ /** The executor for `scripts: local`. Default: the local executor with SCRIPT_TIMEOUT_SECONDS and `envNames`. */
61
+ executor?: LocalScriptExecutor;
62
+ /** Where script output files are copied. Default: a private temp directory, made once. */
63
+ scriptOutputDir?: string;
64
+ }
65
+ /**
66
+ * The tools an agent with `skills:` carries: load_skill and
67
+ * load_skill_resource always, run_skill_script with `scripts: local`, and
68
+ * the tools its loaded skills unlock. A toolset by shape (getTools), which
69
+ * the native loop expands and lib/tools/adkTool.ts wraps for ADK.
70
+ */
71
+ export declare class SkillToolset implements Toolset {
72
+ #private;
73
+ readonly name = "skill_toolset";
74
+ readonly skills: Readonly<Record<string, Skill>>;
75
+ readonly executor?: LocalScriptExecutor;
76
+ constructor(skills: Record<string, Skill>, options?: SkillToolsetOptions);
77
+ /** The skill named `name`, or undefined. Only the toolset's own skills, never a prototype key. */
78
+ getSkill(name: string): Skill | undefined;
79
+ /** The harness tools, then the tools the agent's loaded skills unlock. */
80
+ getTools(context?: ToolsetContext): Promise<unknown[]>;
81
+ /**
82
+ * Where a script's output files are copied: the configured directory, or
83
+ * a private temp directory made once per toolset. Output file names are
84
+ * the script's choice, so they are never resolved against the server's
85
+ * working directory.
86
+ */
87
+ scriptOutputDir(): Promise<string>;
88
+ }
89
+ declare abstract class SkillTool implements Tool {
90
+ abstract readonly name: string;
91
+ abstract readonly description: string;
92
+ protected abstract readonly parameters: Record<string, unknown>;
93
+ protected readonly toolset: SkillToolset;
94
+ constructor(toolset: SkillToolset);
95
+ declaration(): ToolDeclaration;
96
+ abstract execute(args: Record<string, unknown>, ctx: ToolContext): Promise<unknown>;
97
+ }
98
+ /** list_skills: the index of every installed skill. The harness does not carry it (ADR 0029). */
99
+ export declare class ListSkillsTool extends SkillTool {
100
+ readonly name = "list_skills";
101
+ readonly description = "Lists all available skills with their names and descriptions.";
102
+ protected readonly parameters: {
103
+ type: string;
104
+ properties: {};
105
+ };
106
+ execute(): Promise<unknown>;
107
+ }
108
+ /**
109
+ * load_skill, lean (ADR 0029): the procedure, the frontmatter and the names
110
+ * of the files the skill ships, never their contents. Loading activates the
111
+ * skill, which unlocks its permitted `allowed-tools` on the next request.
112
+ */
113
+ export declare class LoadSkillTool extends SkillTool {
114
+ readonly name = "load_skill";
115
+ readonly description = "Read one installed skill in full: its SKILL.md procedure, its frontmatter, and the names of the files it ships. Read a skill before you follow it.";
116
+ protected readonly parameters: {
117
+ type: string;
118
+ properties: {
119
+ name: {
120
+ type: string;
121
+ description: string;
122
+ };
123
+ };
124
+ required: string[];
125
+ };
126
+ execute(args: Record<string, unknown>, ctx: ToolContext): Promise<unknown>;
127
+ }
128
+ /** load_skill_resource: one file a skill ships, by its path (ADK's tool). */
129
+ export declare class LoadSkillResourceTool extends SkillTool {
130
+ readonly name = "load_skill_resource";
131
+ readonly description = "Loads a resource file (from references/, assets/, or scripts/) from within a skill.";
132
+ protected readonly parameters: {
133
+ type: string;
134
+ properties: {
135
+ skill_name: {
136
+ type: string;
137
+ description: string;
138
+ };
139
+ path: {
140
+ type: string;
141
+ description: string;
142
+ };
143
+ };
144
+ required: string[];
145
+ };
146
+ execute(args: Record<string, unknown>): Promise<unknown>;
147
+ /**
148
+ * A binary file this tool just answered for is added to the request as
149
+ * inline data, after the history (ADK's processLlmRequest): the latest
150
+ * content, when it is the tools' answer, is searched for this tool's
151
+ * binary notices.
152
+ */
153
+ contents(contents: TurnContent[]): Promise<void>;
154
+ }
155
+ /**
156
+ * run_skill_script behind a person's approval: the first call asks for it
157
+ * (the hint names the skill, the script and the arguments) and returns
158
+ * without running; the call made again with the answer runs only when the
159
+ * answer approved it. The confirmation travels as `adk_request_confirmation`
160
+ * on either runtime, so the turn pauses `input-required` exactly as for
161
+ * `require_approval` (ADR 0028), with this tool's own texts.
162
+ */
163
+ export declare class RunSkillScriptTool extends SkillTool {
164
+ #private;
165
+ readonly name = "run_skill_script";
166
+ readonly description = "Executes a script from a skill's scripts/ directory.";
167
+ protected readonly parameters: {
168
+ type: string;
169
+ properties: {
170
+ skill_name: {
171
+ type: string;
172
+ description: string;
173
+ };
174
+ script_path: {
175
+ type: string;
176
+ description: string;
177
+ };
178
+ args: {
179
+ type: string;
180
+ description: string;
181
+ };
182
+ };
183
+ required: string[];
184
+ };
185
+ execute(args: Record<string, unknown>, ctx: ToolContext): Promise<unknown>;
186
+ }
187
+ export {};