melchizedek-agents 0.9.6 → 0.16.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 (246) hide show
  1. package/.env.example +272 -0
  2. package/AGENT_SETUP.md +39 -23
  3. package/CHANGELOG.md +623 -0
  4. package/DOCUMENTATION.md +429 -109
  5. package/QUICKSTART.md +92 -18
  6. package/README.md +45 -5
  7. package/config/agents/README.md +46 -1
  8. package/config/agents/examples/ares.yaml +117 -40
  9. package/config/agents/examples/assistant.yaml +159 -0
  10. package/config/agents/examples/augustin.yaml +154 -51
  11. package/config/agents/examples/cartographers.yaml +91 -23
  12. package/config/agents/examples/claude.yaml +45 -6
  13. package/config/agents/examples/council.yaml +23 -8
  14. package/config/agents/examples/critic.yaml +57 -21
  15. package/config/agents/examples/delegation.yaml +76 -9
  16. package/config/agents/examples/hierarchical.yaml +55 -8
  17. package/config/agents/examples/image_production.yaml +54 -16
  18. package/config/agents/examples/librarian.yaml +70 -14
  19. package/config/agents/examples/model_zoo.yaml +105 -29
  20. package/config/agents/examples/patient_advocate.yaml +102 -34
  21. package/config/agents/examples/research.yaml +250 -0
  22. package/config/agents/examples/scribe.yaml +242 -0
  23. package/config/agents/examples/scriptorium.yaml +87 -24
  24. package/config/agents/examples/style_council.yaml +31 -9
  25. package/config/agents/examples/syndicate.yaml +29 -10
  26. package/config/agents/examples/tutor.yaml +10 -3
  27. package/config/agents/syndicateSchema.yaml +44 -9
  28. package/config/agents/templates/README.md +58 -0
  29. package/config/agents/templates/account_memory.yaml +153 -0
  30. package/config/agents/templates/case_desk.yaml +188 -0
  31. package/config/agents/templates/conversational.yaml +110 -0
  32. package/config/agents/templates/draft_review.yaml +130 -0
  33. package/config/agents/templates/intake_extractor.yaml +152 -0
  34. package/config/agents/templates/research_brief.yaml +169 -0
  35. package/config/agents/templates/research_desk.yaml +152 -0
  36. package/config/agents/templates/review_panel.yaml +188 -0
  37. package/config/agents/templates/support_triage.yaml +273 -0
  38. package/config/agents/templates/systems_operator.yaml +157 -0
  39. package/db/hardening.sql +202 -0
  40. package/db/memory_v2.sql +77 -0
  41. package/db/migrations/0001_base.sql +167 -0
  42. package/db/migrations/0002_erase_scope.sql +172 -0
  43. package/db/migrations/0003_postgres_storage.sql +58 -0
  44. package/db/migrations/0004_usage.sql +94 -0
  45. package/db/migrations/0005_agent_registry.sql +200 -0
  46. package/db/migrations/0006_task_leases.sql +25 -0
  47. package/db/migrations/0007_memory_commit.sql +134 -0
  48. package/db/migrations/0008_memory_retention.sql +37 -0
  49. package/db/migrations/0009_task_queue.sql +51 -0
  50. package/db/migrations/0010_erase_complete.sql +180 -0
  51. package/db/telemetry.sql +401 -0
  52. package/demo/README.md +77 -0
  53. package/demo/a2a_demo.mjs +95 -0
  54. package/dist/lib/a2a/app.d.ts +253 -0
  55. package/dist/lib/a2a/app.js +751 -0
  56. package/dist/lib/a2a/executor.d.ts +167 -0
  57. package/dist/lib/a2a/executor.js +548 -0
  58. package/dist/lib/a2a/identity.d.ts +122 -0
  59. package/dist/lib/a2a/identity.js +254 -0
  60. package/dist/lib/a2a/policy.d.ts +110 -0
  61. package/dist/lib/a2a/policy.js +178 -0
  62. package/dist/lib/a2a/redisLimits.d.ts +26 -0
  63. package/dist/lib/a2a/redisLimits.js +31 -0
  64. package/dist/lib/a2a/remoteAgent.d.ts +91 -0
  65. package/dist/lib/a2a/remoteAgent.js +268 -0
  66. package/dist/lib/a2a/turnLock.d.ts +28 -0
  67. package/dist/lib/a2a/turnLock.js +76 -0
  68. package/dist/lib/compile.d.ts +11 -1
  69. package/dist/lib/compile.js +109 -8
  70. package/dist/lib/config.d.ts +7 -7
  71. package/dist/lib/config.js +7 -7
  72. package/dist/lib/doctor.d.ts +145 -0
  73. package/dist/lib/doctor.js +517 -0
  74. package/dist/lib/grounding.d.ts +2 -2
  75. package/dist/lib/grounding.js +2 -2
  76. package/dist/lib/guards/index.d.ts +37 -0
  77. package/dist/lib/guards/index.js +66 -0
  78. package/dist/lib/guards/science.d.ts +117 -0
  79. package/dist/lib/guards/science.js +444 -0
  80. package/dist/lib/index.d.ts +37 -2
  81. package/dist/lib/index.js +35 -4
  82. package/dist/lib/loadEnv.d.ts +23 -11
  83. package/dist/lib/loadEnv.js +77 -29
  84. package/dist/lib/loadSyndicate.d.ts +97 -5
  85. package/dist/lib/loadSyndicate.js +176 -76
  86. package/dist/lib/memory/erase.d.ts +24 -0
  87. package/dist/lib/memory/erase.js +47 -0
  88. package/dist/lib/memory/namespace.d.ts +37 -0
  89. package/dist/lib/memory/namespace.js +74 -0
  90. package/dist/lib/memory/providers.d.ts +99 -0
  91. package/dist/lib/memory/providers.js +240 -0
  92. package/dist/lib/memory/store.d.ts +74 -0
  93. package/dist/lib/memory/store.js +108 -0
  94. package/dist/lib/memory/supabaseMemoryService.d.ts +69 -9
  95. package/dist/lib/memory/supabaseMemoryService.js +239 -125
  96. package/dist/lib/models/capabilities.d.ts +103 -0
  97. package/dist/lib/models/capabilities.js +291 -0
  98. package/dist/lib/models/claudeLlm.d.ts +10 -1
  99. package/dist/lib/models/claudeLlm.js +57 -27
  100. package/dist/lib/models/endpoints.d.ts +100 -0
  101. package/dist/lib/models/endpoints.js +282 -0
  102. package/dist/lib/models/gateway.d.ts +107 -0
  103. package/dist/lib/models/gateway.js +157 -0
  104. package/dist/lib/models/gatewayLlm.d.ts +57 -0
  105. package/dist/lib/models/gatewayLlm.js +105 -0
  106. package/dist/lib/models/gptLlm.d.ts +51 -2
  107. package/dist/lib/models/gptLlm.js +169 -26
  108. package/dist/lib/models/grokLlm.d.ts +19 -10
  109. package/dist/lib/models/grokLlm.js +29 -14
  110. package/dist/lib/models/ollamaLlm.d.ts +26 -0
  111. package/dist/lib/models/ollamaLlm.js +43 -1
  112. package/dist/lib/models/openAiCompatibleLlm.d.ts +67 -2
  113. package/dist/lib/models/openAiCompatibleLlm.js +200 -37
  114. package/dist/lib/models/providerMap.js +9 -2
  115. package/dist/lib/models/registry.d.ts +42 -5
  116. package/dist/lib/models/registry.js +140 -29
  117. package/dist/lib/models/retry.d.ts +146 -0
  118. package/dist/lib/models/retry.js +305 -0
  119. package/dist/lib/models/schemaNormalize.d.ts +20 -0
  120. package/dist/lib/models/schemaNormalize.js +43 -0
  121. package/dist/lib/net/addressGuard.d.ts +60 -0
  122. package/dist/lib/net/addressGuard.js +216 -0
  123. package/dist/lib/observability/embeddings.d.ts +11 -2
  124. package/dist/lib/observability/embeddings.js +18 -14
  125. package/dist/lib/observability/metrics.d.ts +36 -0
  126. package/dist/lib/observability/metrics.js +86 -0
  127. package/dist/lib/observability/redact.d.ts +29 -0
  128. package/dist/lib/observability/redact.js +102 -0
  129. package/dist/lib/observability/supabaseSpanExporter.d.ts +2 -0
  130. package/dist/lib/observability/supabaseSpanExporter.js +22 -6
  131. package/dist/lib/observability/tracer.d.ts +10 -0
  132. package/dist/lib/observability/tracer.js +70 -3
  133. package/dist/lib/persistence/supabaseProvider.d.ts +11 -0
  134. package/dist/lib/persistence/supabaseProvider.js +32 -6
  135. package/dist/lib/registry.d.ts +87 -0
  136. package/dist/lib/registry.js +146 -0
  137. package/dist/lib/runtime/approvals.d.ts +44 -0
  138. package/dist/lib/runtime/approvals.js +67 -0
  139. package/dist/lib/runtime/syndicateTurn.d.ts +233 -0
  140. package/dist/lib/runtime/syndicateTurn.js +597 -0
  141. package/dist/lib/runtime/turnControl.d.ts +90 -0
  142. package/dist/lib/runtime/turnControl.js +131 -0
  143. package/dist/lib/session/supabaseSessionService.js +1 -1
  144. package/dist/lib/session/transcript.d.ts +9 -1
  145. package/dist/lib/session/transcript.js +15 -7
  146. package/dist/lib/skills.d.ts +50 -0
  147. package/dist/lib/skills.js +138 -0
  148. package/dist/lib/storage/postgres/index.d.ts +98 -0
  149. package/dist/lib/storage/postgres/index.js +146 -0
  150. package/dist/lib/storage/postgres/memoryStore.d.ts +11 -0
  151. package/dist/lib/storage/postgres/memoryStore.js +82 -0
  152. package/dist/lib/storage/postgres/sessionService.d.ts +51 -0
  153. package/dist/lib/storage/postgres/sessionService.js +225 -0
  154. package/dist/lib/storage/postgres/taskQueue.d.ts +22 -0
  155. package/dist/lib/storage/postgres/taskQueue.js +101 -0
  156. package/dist/lib/storage/postgres/taskStore.d.ts +55 -0
  157. package/dist/lib/storage/postgres/taskStore.js +168 -0
  158. package/dist/lib/storage/schema.d.ts +27 -0
  159. package/dist/lib/storage/schema.js +50 -0
  160. package/dist/lib/storage/schemaVersion.d.ts +25 -0
  161. package/dist/lib/storage/schemaVersion.js +46 -0
  162. package/dist/lib/syndicateSchema.d.ts +275 -0
  163. package/dist/lib/syndicateSchema.js +534 -0
  164. package/dist/lib/toolRegistry.d.ts +15 -0
  165. package/dist/lib/toolRegistry.js +59 -10
  166. package/dist/lib/tools/collectionsSearchTool.js +1 -1
  167. package/dist/lib/tools/mcpServe.d.ts +45 -0
  168. package/dist/lib/tools/mcpServe.js +88 -0
  169. package/dist/lib/tools/mcpToolFactory.d.ts +2 -0
  170. package/dist/lib/tools/mcpToolFactory.js +41 -21
  171. package/dist/lib/tools/science/corrections.d.ts +42 -0
  172. package/dist/lib/tools/science/corrections.js +115 -0
  173. package/dist/lib/tools/science/europepmc.d.ts +51 -0
  174. package/dist/lib/tools/science/europepmc.js +127 -0
  175. package/dist/lib/tools/science/http.d.ts +31 -0
  176. package/dist/lib/tools/science/http.js +104 -0
  177. package/dist/lib/tools/science/openalex.d.ts +30 -0
  178. package/dist/lib/tools/science/openalex.js +81 -0
  179. package/dist/lib/tools/science/trials.d.ts +58 -0
  180. package/dist/lib/tools/science/trials.js +203 -0
  181. package/dist/lib/tools/science/types.d.ts +69 -0
  182. package/dist/lib/tools/science/types.js +48 -0
  183. package/dist/lib/tools/scienceTools.d.ts +100 -0
  184. package/dist/lib/tools/scienceTools.js +277 -0
  185. package/dist/lib/tools/taskTools.d.ts +204 -0
  186. package/dist/lib/tools/taskTools.js +374 -0
  187. package/dist/lib/tools/toolContract.d.ts +17 -4
  188. package/dist/lib/tools/toolContract.js +13 -5
  189. package/dist/lib/tools/webExtractTool.d.ts +13 -10
  190. package/dist/lib/tools/webExtractTool.js +23 -55
  191. package/dist/lib/tools/webSearchTool.d.ts +2 -1
  192. package/dist/lib/tools/webSearchTool.js +4 -3
  193. package/dist/lib/tools/wikiTools.d.ts +2 -2
  194. package/dist/lib/tools/wikiTools.js +12 -12
  195. package/dist/lib/tools/xApiSearchTool.d.ts +194 -0
  196. package/dist/lib/tools/xApiSearchTool.js +583 -0
  197. package/dist/lib/tools/xSearchTool.js +1 -1
  198. package/dist/lib/wiki/agentRun.js +1 -1
  199. package/dist/lib/wiki/builder.js +21 -6
  200. package/dist/lib/wiki/dive.js +2 -2
  201. package/dist/lib/wiki/entities.d.ts +2 -2
  202. package/dist/lib/wiki/entities.js +7 -7
  203. package/dist/lib/wiki/extract.js +5 -1
  204. package/dist/lib/wiki/fill.js +5 -5
  205. package/dist/lib/wiki/format.d.ts +7 -5
  206. package/dist/lib/wiki/format.js +2 -2
  207. package/dist/lib/wiki/graph.js +2 -2
  208. package/dist/lib/wiki/lint.js +2 -2
  209. package/dist/lib/wiki/markdown.d.ts +1 -1
  210. package/dist/lib/wiki/markdown.js +6 -1
  211. package/dist/lib/wiki/pipeline.js +2 -2
  212. package/dist/lib/wiki/vault.js +2 -2
  213. package/dist/scripts/a2a_server.d.ts +17 -11
  214. package/dist/scripts/a2a_server.js +405 -1028
  215. package/dist/scripts/assistant_worker.d.ts +33 -0
  216. package/dist/scripts/assistant_worker.js +193 -0
  217. package/dist/scripts/db.d.ts +26 -0
  218. package/dist/scripts/db.js +179 -0
  219. package/dist/scripts/doctor.d.ts +23 -0
  220. package/dist/scripts/doctor.js +90 -0
  221. package/dist/scripts/init.d.ts +43 -0
  222. package/dist/scripts/init.js +152 -0
  223. package/dist/scripts/registry.d.ts +25 -0
  224. package/dist/scripts/registry.js +182 -0
  225. package/dist/scripts/skills_install.d.ts +7 -0
  226. package/dist/scripts/skills_install.js +137 -0
  227. package/dist/scripts/syndicate_chat.js +159 -276
  228. package/package.json +90 -16
  229. package/skills/README.md +74 -0
  230. package/skills/briefs/README.md +16 -0
  231. package/skills/briefs/_shared.md +16 -0
  232. package/skills/briefs/melchizedek-author.md +60 -0
  233. package/skills/briefs/melchizedek-memory.md +47 -0
  234. package/skills/briefs/melchizedek-models.md +47 -0
  235. package/skills/briefs/melchizedek-scribe.md +37 -0
  236. package/skills/briefs/melchizedek-serve.md +56 -0
  237. package/skills/briefs/melchizedek.md +69 -0
  238. package/skills/briefs/skills-readme.md +33 -0
  239. package/skills/melchizedek/SKILL.md +159 -0
  240. package/skills/melchizedek-author/SKILL.md +99 -0
  241. package/skills/melchizedek-author/templates/minimal.yaml +60 -0
  242. package/skills/melchizedek-memory/SKILL.md +138 -0
  243. package/skills/melchizedek-models/SKILL.md +137 -0
  244. package/skills/melchizedek-scribe/SKILL.md +72 -0
  245. package/skills/melchizedek-scribe/templates/brief.md +27 -0
  246. package/skills/melchizedek-serve/SKILL.md +119 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * lib/tools/scienceTools.ts — the clinical-evidence tools, defined as tool
3
+ * CONTRACTS (lib/tools/toolContract.ts): one zod schema per tool is the single
4
+ * source of truth, from which both deployment surfaces derive — the ADK
5
+ * FunctionTools consumed by lib/toolRegistry.ts, and the MCP tools/list entries
6
+ * served by scripts/science_mcp_server.ts.
7
+ *
8
+ * Ported from the science desk (2026-09-08). The four source clients under
9
+ * lib/tools/science/ are that desk's, verbatim: Europe PMC (literature and
10
+ * preprints), ClinicalTrials.gov v2 (registry), Crossref + OpenAlex
11
+ * (corrections), OpenAlex (citations). All free; all keyed on permanent
12
+ * identifiers (DOI, PMID, NCT).
13
+ *
14
+ * TWO DISCIPLINES the contracts enforce in CODE, not in the prompt:
15
+ *
16
+ * THE CHANNEL DECIDES THE CEILING. `search_literature` filters to peer-
17
+ * reviewed sources; `search_preprints` filters to preprint servers. A model
18
+ * cannot ask for "papers" and be handed a preprint it will then grade as
19
+ * reported, because the two are different tools with different ceilings
20
+ * printed on every record they return.
21
+ *
22
+ * A RESULT IS A TEXT BLOCK, NOT A JSON DUMP. Every record prints with its
23
+ * identifier, its ceiling, its facts and its verbatim abstract, so the model
24
+ * reads what a person would read. JSON invites restructuring; a labelled
25
+ * block invites quoting — and every identifier in it is one a downstream
26
+ * closure check can verify against.
27
+ */
28
+ import { z } from 'zod';
29
+ import type { ToolContract } from './toolContract.ts';
30
+ import type { Record_ } from './science/types.ts';
31
+ /** records a source tool returns at most; a ceiling, never a target */
32
+ export declare const RESULTS_PER_CALL = 10;
33
+ /** Exported for tests. lib/guards/science.ts parses exactly this layout to
34
+ * build its ledger, and the two lived in different files with nothing pinning
35
+ * the format across the boundary — so a test suite that hand-rolled the layout
36
+ * would keep passing while the guard went silent in production. Tests build
37
+ * their fixtures with this function for that reason. */
38
+ export declare function printRecord(r: Record_, n: number): string;
39
+ export declare const searchLiteratureContract: ToolContract<z.ZodObject<{
40
+ query: z.ZodString;
41
+ sort_by: z.ZodOptional<z.ZodEnum<{
42
+ cited: "cited";
43
+ recent: "recent";
44
+ }>>;
45
+ since_year: z.ZodOptional<z.ZodNumber>;
46
+ limit: z.ZodOptional<z.ZodNumber>;
47
+ }, z.core.$strip>>;
48
+ export declare const searchPreprintsContract: ToolContract<z.ZodObject<{
49
+ query: z.ZodString;
50
+ sort_by: z.ZodOptional<z.ZodEnum<{
51
+ cited: "cited";
52
+ recent: "recent";
53
+ }>>;
54
+ since_year: z.ZodOptional<z.ZodNumber>;
55
+ limit: z.ZodOptional<z.ZodNumber>;
56
+ }, z.core.$strip>>;
57
+ export declare const searchTrialsContract: ToolContract<z.ZodObject<{
58
+ query: z.ZodString;
59
+ status: z.ZodOptional<z.ZodEnum<{
60
+ ACTIVE_NOT_RECRUITING: "ACTIVE_NOT_RECRUITING";
61
+ COMPLETED: "COMPLETED";
62
+ ENROLLING_BY_INVITATION: "ENROLLING_BY_INVITATION";
63
+ NOT_YET_RECRUITING: "NOT_YET_RECRUITING";
64
+ RECRUITING: "RECRUITING";
65
+ SUSPENDED: "SUSPENDED";
66
+ TERMINATED: "TERMINATED";
67
+ UNKNOWN: "UNKNOWN";
68
+ WITHDRAWN: "WITHDRAWN";
69
+ }>>;
70
+ phase: z.ZodOptional<z.ZodEnum<{
71
+ 1: "1";
72
+ 2: "2";
73
+ 3: "3";
74
+ 4: "4";
75
+ }>>;
76
+ sort_by: z.ZodOptional<z.ZodEnum<{
77
+ newest: "newest";
78
+ relevance: "relevance";
79
+ }>>;
80
+ limit: z.ZodOptional<z.ZodNumber>;
81
+ }, z.core.$strip>>;
82
+ export declare const resolveIdentifierContract: ToolContract<z.ZodObject<{
83
+ id: z.ZodString;
84
+ }, z.core.$strip>>;
85
+ export declare const citedByContract: ToolContract<z.ZodObject<{
86
+ doi: z.ZodString;
87
+ limit: z.ZodOptional<z.ZodNumber>;
88
+ }, z.core.$strip>>;
89
+ export declare const surveyFieldContract: ToolContract<z.ZodObject<{
90
+ query: z.ZodString;
91
+ since_year: z.ZodOptional<z.ZodNumber>;
92
+ limit: z.ZodOptional<z.ZodNumber>;
93
+ }, z.core.$strip>>;
94
+ export declare const checkRetractionContract: ToolContract<z.ZodObject<{
95
+ doi: z.ZodString;
96
+ }, z.core.$strip>>;
97
+ /** Every science contract, in the order an MCP client sees them. Exposure is
98
+ * still a deliberate act: the registry below and the MCP server list these
99
+ * explicitly. */
100
+ export declare const SCIENCE_TOOL_CONTRACTS: readonly ToolContract<any>[];
@@ -0,0 +1,277 @@
1
+ /**
2
+ * lib/tools/scienceTools.ts — the clinical-evidence tools, defined as tool
3
+ * CONTRACTS (lib/tools/toolContract.ts): one zod schema per tool is the single
4
+ * source of truth, from which both deployment surfaces derive — the ADK
5
+ * FunctionTools consumed by lib/toolRegistry.ts, and the MCP tools/list entries
6
+ * served by scripts/science_mcp_server.ts.
7
+ *
8
+ * Ported from the science desk (2026-09-08). The four source clients under
9
+ * lib/tools/science/ are that desk's, verbatim: Europe PMC (literature and
10
+ * preprints), ClinicalTrials.gov v2 (registry), Crossref + OpenAlex
11
+ * (corrections), OpenAlex (citations). All free; all keyed on permanent
12
+ * identifiers (DOI, PMID, NCT).
13
+ *
14
+ * TWO DISCIPLINES the contracts enforce in CODE, not in the prompt:
15
+ *
16
+ * THE CHANNEL DECIDES THE CEILING. `search_literature` filters to peer-
17
+ * reviewed sources; `search_preprints` filters to preprint servers. A model
18
+ * cannot ask for "papers" and be handed a preprint it will then grade as
19
+ * reported, because the two are different tools with different ceilings
20
+ * printed on every record they return.
21
+ *
22
+ * A RESULT IS A TEXT BLOCK, NOT A JSON DUMP. Every record prints with its
23
+ * identifier, its ceiling, its facts and its verbatim abstract, so the model
24
+ * reads what a person would read. JSON invites restructuring; a labelled
25
+ * block invites quoting — and every identifier in it is one a downstream
26
+ * closure check can verify against.
27
+ */
28
+ import { z } from 'zod';
29
+ import { defineTool } from './toolContract.js';
30
+ import { searchLiterature, searchPreprints, resolveArticle } from './science/europepmc.js';
31
+ import { searchTrials, resolveTrial, TRIAL_STATUSES } from './science/trials.js';
32
+ import { citedBy, surveyField } from './science/openalex.js';
33
+ import { checkCorrections } from './science/corrections.js';
34
+ /** records a source tool returns at most; a ceiling, never a target */
35
+ export const RESULTS_PER_CALL = 10;
36
+ // ---------------------------------------------------------------------------
37
+ // Printing: what the model reads
38
+ // ---------------------------------------------------------------------------
39
+ /** Exported for tests. lib/guards/science.ts parses exactly this layout to
40
+ * build its ledger, and the two lived in different files with nothing pinning
41
+ * the format across the boundary — so a test suite that hand-rolled the layout
42
+ * would keep passing while the guard went silent in production. Tests build
43
+ * their fixtures with this function for that reason. */
44
+ export function printRecord(r, n) {
45
+ const lines = [];
46
+ const id = r.id ? `${r.id.kind.toUpperCase()} ${r.id.value}` : 'NO IDENTIFIER';
47
+ lines.push(`[${n}] ${r.title || '(untitled)'}`);
48
+ lines.push(` ${id}${r.id ? ` · ${r.id.url}` : ''}`);
49
+ // EVERY other identifier this record carries, which types.ts:36 has always
50
+ // said is why `alsoIds` exists ("so closure can match any of them") — and
51
+ // which nothing printed. Europe PMC puts the DOI first, so a record's PMID
52
+ // and PMCID never reached the model or the ledger: citing by PMID, which the
53
+ // grade ladder explicitly permits, was stamped UNVERIFIED by the guard.
54
+ for (const also of r.alsoIds ?? []) {
55
+ lines.push(` ${also.kind.toUpperCase()} ${also.value} · ${also.url}`);
56
+ }
57
+ if (r.acronym)
58
+ lines.push(` ACRONYM: ${r.acronym}`);
59
+ const where = [r.venue, r.year ? String(r.year) : r.date].filter(Boolean).join(', ');
60
+ if (where)
61
+ lines.push(` ${where}`);
62
+ if (r.authors?.length)
63
+ lines.push(` ${r.authors.slice(0, 6).join(', ')}${r.authors.length > 6 ? ', et al.' : ''}`);
64
+ lines.push(` CEILING: ${r.ceiling} — nothing from this record may be graded above it`);
65
+ if (r.retracted) {
66
+ lines.push(` *** ${r.retracted.kind.toUpperCase()} recorded by ${r.retracted.source}. This work cannot support a claim at any grade. ***`);
67
+ }
68
+ for (const [k, v] of r.facts ?? [])
69
+ lines.push(` ${k}: ${v}`);
70
+ if (r.text)
71
+ lines.push(` ---\n ${r.text.slice(0, 1600).replace(/\n+/g, '\n ')}`);
72
+ return lines.join('\n');
73
+ }
74
+ function block(head, records, tail) {
75
+ const body = records.length
76
+ ? records.map((r, i) => printRecord(r, i + 1)).join('\n\n')
77
+ : 'NOTHING RETURNED. This is one query against one source. It is not evidence that nothing exists — say so in exactly those terms, or rephrase and ask again.';
78
+ return [head, '', body, tail ? `\n${tail}` : ''].join('\n');
79
+ }
80
+ const limit = z.number().int().min(1).max(RESULTS_PER_CALL).optional()
81
+ .describe(`how many records, 1 to ${RESULTS_PER_CALL}`);
82
+ /**
83
+ * A contract returns an error STRING and never throws.
84
+ *
85
+ * That is the convention lib/tools/toolContract.ts states in writing and every
86
+ * hand-written tool in the other tool families follows; the science half followed
87
+ * neither. A `SourceError` escaping `execute` propagated through the ADK runner
88
+ * and aborted the agent's whole turn, where the same failure returned as text
89
+ * lets the model read what broke and reach for a different channel — and, just
90
+ * as importantly, keeps the turn alive so the guards still run over what DID
91
+ * come back.
92
+ *
93
+ * The wording matters too: a source that could not be reached is a failure to
94
+ * ASK, and the desk's whole discipline is that this is not the same as an
95
+ * absence of evidence.
96
+ */
97
+ async function answering(what, run) {
98
+ try {
99
+ return await run();
100
+ }
101
+ catch (e) {
102
+ const msg = e instanceof Error ? e.message : String(e);
103
+ return `${what} — SOURCE ERROR: ${msg}. The source could not be reached. This is a failure to ASK, not an answer, and it is NOT evidence that nothing exists: say the channel was unavailable, or try another one.`;
104
+ }
105
+ }
106
+ // ---------------------------------------------------------------------------
107
+ // The contracts
108
+ // ---------------------------------------------------------------------------
109
+ export const searchLiteratureContract = defineTool({
110
+ name: 'search_literature',
111
+ description: "Search PEER-REVIEWED literature (PubMed and PubMed Central) through Europe PMC. Returns papers with DOI, PMID, journal, year, authors and abstract. Preprints are excluded by construction — use search_preprints for those. Query syntax is Europe PMC's: bare terms are AND-ed, quotes make a phrase, OR groups alternatives.",
112
+ schema: z.object({
113
+ query: z.string().min(1).describe('the search, in the vocabulary the papers themselves use'),
114
+ sort_by: z.enum(['cited', 'recent']).optional()
115
+ .describe('`cited` (default) for the most-cited work on the subject; `recent` for the newest, when the question is about what has just been published'),
116
+ since_year: z.number().int().min(1800).max(3000).optional()
117
+ .describe('optional: only work first published in or after this year'),
118
+ limit,
119
+ }),
120
+ execute({ query, sort_by, since_year, limit: n }) {
121
+ return answering(`PEER-REVIEWED LITERATURE · query: ${query}`, async () => {
122
+ const { hits, records } = await searchLiterature(query, n ?? 6, {
123
+ sort: sort_by,
124
+ sinceYear: since_year,
125
+ });
126
+ return block(`PEER-REVIEWED LITERATURE · query: ${query}${since_year ? ` · since ${since_year}` : ''} · ${hits.toLocaleString()} matching records, ${records.length} shown, ${sort_by === 'recent' ? 'newest first' : 'most cited first'}`, records, hits > records.length
127
+ ? `${(hits - records.length).toLocaleString()} further records match and were not read. Any statement about how much exists is about this query, not about the literature.`
128
+ : undefined);
129
+ });
130
+ },
131
+ });
132
+ export const searchPreprintsContract = defineTool({
133
+ name: 'search_preprints',
134
+ description: "Search PREPRINT servers (bioRxiv, medRxiv and the rest of Europe PMC's preprint index). Returns the NEWEST first, which is the point of reading preprints at all. Everything returned is unreviewed and comes back capped at the `preprint` grade.",
135
+ schema: z.object({
136
+ query: z.string().min(1),
137
+ sort_by: z.enum(['recent', 'cited']).optional()
138
+ .describe('`recent` (default) for the newest postings; `cited` only when you want the most-discussed older preprints on the subject'),
139
+ since_year: z.number().int().min(1800).max(3000).optional()
140
+ .describe('optional: only work first posted in or after this year'),
141
+ limit,
142
+ }),
143
+ execute({ query, sort_by, since_year, limit: n }) {
144
+ return answering(`PREPRINTS · query: ${query}`, async () => {
145
+ const { hits, records } = await searchPreprints(query, n ?? 6, {
146
+ sort: sort_by,
147
+ sinceYear: since_year,
148
+ });
149
+ return block(`PREPRINTS · query: ${query}${since_year ? ` · since ${since_year}` : ''} · ${hits.toLocaleString()} matching, ${records.length} shown, ${sort_by === 'cited' ? 'most cited first' : 'newest first'}. NONE OF THESE HAVE BEEN PEER REVIEWED; every one is capped at the \`preprint\` grade and the word "preprint" appears wherever you name one.`, records);
150
+ });
151
+ },
152
+ });
153
+ export const searchTrialsContract = defineTool({
154
+ name: 'search_trials',
155
+ description: 'Search the ClinicalTrials.gov registry. Returns status, phase, design, enrollment, sponsor, the PRE-REGISTERED primary outcome, and whether results have been posted. Use this to find out what a study said it would measure before it knew what it found, and to find completed trials that have reported nothing.',
156
+ schema: z.object({
157
+ query: z.string().min(1).describe('condition, intervention, sponsor or free text'),
158
+ // Closed enums, not free strings. ClinicalTrials.gov treats overallStatus as
159
+ // a closed enum and answers 400 to anything else — `Recruiting` and `ACTIVE`
160
+ // both fail — while `aggFilters` answers 200 with ZERO results to a phase it
161
+ // does not recognise, which `block()` then presents to the model as a
162
+ // genuine registry silence. The second is the worse failure because nothing
163
+ // errors, and a free `z.string()` invited exactly the casings that trip it.
164
+ status: z.enum(TRIAL_STATUSES).optional()
165
+ .describe('optional registry status filter'),
166
+ phase: z.enum(['1', '2', '3', '4']).optional()
167
+ .describe('optional trial phase: 1, 2, 3 or 4'),
168
+ sort_by: z.enum(['relevance', 'newest']).optional()
169
+ .describe('`relevance` (default), or `newest` to order by registration date when the question is about what has recently been registered'),
170
+ limit,
171
+ }),
172
+ execute({ query, status, phase, sort_by, limit: n }) {
173
+ return answering(`TRIAL REGISTRY · query: ${query}`, async () => {
174
+ const { hits, records, overdue } = await searchTrials(query, {
175
+ limit: n ?? 6,
176
+ status,
177
+ phase,
178
+ sort: sort_by,
179
+ });
180
+ const silences = records
181
+ .filter((r) => r.resultsOverdue)
182
+ .map((r) => `${r.id?.value}: ${r.resultsOverdue.status}, primary completion ${r.resultsOverdue.completed}, ${r.resultsOverdue.months} months ago, NO RESULTS POSTED.`);
183
+ const tail = silences.length
184
+ ? `RESULTS OVERDUE — ${overdue} of ${records.length} records shown:\n${silences.join('\n')}\nThis is the visible shape of publication bias, and it is a fact about the record. State it when it bears on the question.`
185
+ : undefined;
186
+ return block(`TRIAL REGISTRY · query: ${query} · ${hits.toLocaleString()} matching, ${records.length} shown. A registry record says a study was BEGUN and what it said it would measure. It is never evidence of a result.`, records, tail);
187
+ });
188
+ },
189
+ });
190
+ export const resolveIdentifierContract = defineTool({
191
+ name: 'resolve_identifier',
192
+ description: 'Resolve ONE identifier to its record: an NCT number, a DOI, a PMID or a PMCID. Always call this when the question names an identifier, before anything else.',
193
+ schema: z.object({ id: z.string().min(1).describe('NCT########, a DOI, a PMID, or PMC#######') }),
194
+ execute({ id }) {
195
+ return answering(`RESOLVE ${id}`, async () => {
196
+ const rec = /^NCT\d{8}$/i.test(id.trim()) ? await resolveTrial(id) : await resolveArticle(id);
197
+ if (!rec) {
198
+ return `RESOLVE ${id} — NO RECORD. This identifier resolved to nothing in the registry or the literature index. It may be malformed, it may be too new to be indexed, or it may not exist. Do not write it into an answer as though it resolved.`;
199
+ }
200
+ return block(`RESOLVED ${id}`, [rec]);
201
+ });
202
+ },
203
+ });
204
+ export const citedByContract = defineTool({
205
+ name: 'cited_by',
206
+ description: 'What has cited a paper, most cited first, through OpenAlex. This is how a claim reaches the `established` grade: independent studies that agree. A citation count is not agreement — read what the citing work says.',
207
+ schema: z.object({ doi: z.string().min(1), limit }),
208
+ execute({ doi, limit: n }) {
209
+ return answering(`CITED BY · ${doi}`, async () => {
210
+ const { hits, records, citedByCount, found } = await citedBy(doi, n ?? 6);
211
+ // "OpenAlex does not index this work" and "this work has never been
212
+ // cited" are different facts, and the not-found path used to print the
213
+ // second for the first — in the one tool the `established` grade rests on.
214
+ if (!found) {
215
+ return `CITED BY · ${doi} — NOT INDEXED. OpenAlex has no work under this DOI, so its citation count is unknown. This is NOT a count of zero: do not report the work as uncited.`;
216
+ }
217
+ return block(`CITED BY · ${doi} · cited ${(citedByCount ?? hits).toLocaleString()} times, ${records.length} shown, most cited first. A citation is not agreement: a work is cited as often to be disputed as to be confirmed. Read what the citing work says before you count it as corroboration.`, records);
218
+ });
219
+ },
220
+ });
221
+ export const surveyFieldContract = defineTool({
222
+ name: 'survey_field',
223
+ description: 'How much has been published on a subject and the most cited of it, through OpenAlex. Use for the shape of a field before reading individual papers.',
224
+ schema: z.object({
225
+ query: z.string().min(1),
226
+ since_year: z.number().int().optional().describe('optional: only work published in or after this year'),
227
+ limit,
228
+ }),
229
+ execute({ query, since_year, limit: n }) {
230
+ return answering(`FIELD SURVEY · ${query}`, async () => {
231
+ const { hits, records } = await surveyField(query, n ?? 6, since_year);
232
+ return block(`FIELD SURVEY · ${query}${since_year ? ` · since ${since_year}` : ''} · ${hits.toLocaleString()} works, ${records.length} shown, most cited first. This is a count of what was PUBLISHED. Studies that found nothing are published less often than studies that found something, so the shape of this list is not the shape of the evidence.`, records);
233
+ });
234
+ },
235
+ });
236
+ export const checkRetractionContract = defineTool({
237
+ name: 'check_retraction',
238
+ description: 'Ask Crossref and OpenAlex whether a DOI has been retracted, withdrawn, or had an expression of concern issued. Call it on any work you are about to lean on.',
239
+ schema: z.object({ doi: z.string().min(1) }),
240
+ execute({ doi }) {
241
+ return answering(`RETRACTION CHECK · ${doi}`, async () => {
242
+ const v = await checkCorrections(doi);
243
+ if (!v.checked) {
244
+ return `RETRACTION CHECK · ${doi} — NOT CHECKED. Neither Crossref nor OpenAlex answered${v.signals.length ? ` (${v.signals.join(', ')})` : ''}. This is not a clean bill: say the work was not checked.`;
245
+ }
246
+ if (v.retracted) {
247
+ return `RETRACTION CHECK · ${doi} — ${(v.kind ?? 'retraction').toUpperCase()}${v.date ? ` on ${v.date}` : ''}, recorded by ${v.signals.join(' and ')}${v.notice ? `, notice ${v.notice}` : ''}. TERMINAL: this work cannot support a claim at any grade. Name the retraction wherever you mention the work.`;
248
+ }
249
+ // `v.sources`, not `v.signals`. Signals only ever records what FIRED, so
250
+ // on a clean work it is empty and the old fallback printed the literal
251
+ // "Crossref or OpenAlex" every time — reporting a one-source check in the
252
+ // same words as a two-source one, in the tool whose whole premise is that
253
+ // any single signal can miss.
254
+ const partial = v.sources.length < 2;
255
+ return `RETRACTION CHECK · ${doi} — no retraction, withdrawal or expression of concern recorded by ${v.sources.join(' or ')}${v.title ? ` for "${v.title}"` : ''}.${partial ? ` NOTE: only ${v.sources.join(' and ')} answered; the other corrections source did not, so this is a partial check.` : ''}`;
256
+ });
257
+ },
258
+ });
259
+ /** Every science contract, in the order an MCP client sees them. Exposure is
260
+ * still a deliberate act: the registry below and the MCP server list these
261
+ * explicitly. */
262
+ export const SCIENCE_TOOL_CONTRACTS = [
263
+ searchLiteratureContract,
264
+ searchPreprintsContract,
265
+ searchTrialsContract,
266
+ resolveIdentifierContract,
267
+ citedByContract,
268
+ surveyFieldContract,
269
+ checkRetractionContract,
270
+ ];
271
+ // No `export const …Tool = toFunctionTool(…)` line here on purpose.
272
+ // lib/toolRegistry.ts derives the ADK FunctionTools itself, straight from
273
+ // SCIENCE_TOOL_CONTRACTS — the same shape lib/tools/wikiTools.ts uses. Seven
274
+ // eagerly-built duplicates used to sit here that nothing imported, so every
275
+ // consumer of this module (the MCP server and scripts/wiki/build.ts among them,
276
+ // neither of which wants ADK tools at all) paid for seven schema conversions
277
+ // and seven FunctionTool constructions at import time.
@@ -0,0 +1,204 @@
1
+ /**
2
+ * lib/tools/taskTools.ts — a small task list and a background-job queue,
3
+ * defined once.
4
+ *
5
+ * WHY this file exists:
6
+ * The starter pack's Assistant (config/agents/examples/assistant.yaml)
7
+ * needs two things no other tool gives an agent: somewhere to keep the
8
+ * user's small tasks between conversations, and a way to hand off work
9
+ * that should not block the conversation. Both are the same primitive —
10
+ * a list of records with a status — so both live in one store:
11
+ *
12
+ * kind: todo the user's own tasks. open → done | cancelled.
13
+ * kind: background a job for the worker. queued → running → done | failed.
14
+ *
15
+ * The tools only WRITE the queue. They never run a job: a tool that
16
+ * orchestrates agents is exactly what the tool-contract doctrine refuses
17
+ * (the wiki_query/wiki_garden rule). The job is run by a separate
18
+ * process, scripts/assistant_worker.ts, which claims a queued record,
19
+ * runs the job's instruction through an agent declared in YAML, and
20
+ * writes the result back. The queue is the whole contract between the
21
+ * conversation and the worker; either side can be swapped.
22
+ *
23
+ * STORE (a TaskBackend, setTaskBackend):
24
+ * - The default is one JSON file, written atomically (temp file + rename).
25
+ * The path is deployment config, never model-chosen and never YAML (the
26
+ * XAI_COLLECTION_IDS doctrine): MELCHIZEDEK_TASKS_FILE, else
27
+ * outputs/tasks.json under the working directory (outputs/ is
28
+ * gitignored). Every operation re-reads the file, so the chat process
29
+ * and the worker see each other's writes. It is SINGLE-USER: every
30
+ * caller shares one list. Run ONE worker: the claim is a
31
+ * read-modify-write, not a lock.
32
+ * - With Postgres (postgresStorage().taskQueue, migration 0009) each
33
+ * caller has its own list, scoped by the caller the tool call carries
34
+ * (the A2A server's scope key), and any number of workers claim jobs
35
+ * with FOR UPDATE SKIP LOCKED under a renewed lease (ADR 0021).
36
+ *
37
+ * SECURITY:
38
+ * On the file store every caller of a shared endpoint shares one list: do
39
+ * not serve a syndicate carrying these tools to people who should not see
40
+ * each other's tasks unless the Postgres store is plugged in.
41
+ * Model-supplied strings are bounded by the schemas and stored as data;
42
+ * none of them reaches a path, a shell, or a query.
43
+ *
44
+ * FAILURE CONTRACT: never throws into the runner. A bad id, a refused
45
+ * transition, or an unreadable store comes back as an `Error:` string.
46
+ */
47
+ import { z } from 'zod';
48
+ export type TaskKind = 'todo' | 'background';
49
+ export type TaskStatus = 'open' | 'done' | 'cancelled' | 'queued' | 'running' | 'failed';
50
+ export interface TaskRecord {
51
+ id: string;
52
+ kind: TaskKind;
53
+ status: TaskStatus;
54
+ title: string;
55
+ notes?: string;
56
+ due?: string;
57
+ /** background only: what the worker should do, in plain language. */
58
+ instruction?: string;
59
+ /** background only: written by the worker. */
60
+ result?: string;
61
+ error?: string;
62
+ attempts?: number;
63
+ created_at: string;
64
+ updated_at: string;
65
+ started_at?: string;
66
+ finished_at?: string;
67
+ }
68
+ export interface TaskStore {
69
+ version: 1;
70
+ next_id: number;
71
+ tasks: TaskRecord[];
72
+ }
73
+ /** A background job with the owner whose list it belongs to. */
74
+ export type OwnedTask = TaskRecord & {
75
+ owner: string;
76
+ };
77
+ /** A worker's claim on the jobs it runs. */
78
+ export interface WorkerLease {
79
+ workerId: string;
80
+ /** How long a claimed job stays the worker's without a renewal. */
81
+ leaseMs: number;
82
+ }
83
+ export type JobOutcome = {
84
+ result: string;
85
+ } | {
86
+ error: string;
87
+ };
88
+ /**
89
+ * Where tasks live. Every rule (transitions, limits, pruning, formatting)
90
+ * stays in this file and runs on a TaskStore snapshot; a backend only
91
+ * supplies the snapshot and persists the change, atomically per owner.
92
+ */
93
+ export interface TaskBackend {
94
+ read(owner: string): Promise<TaskStore>;
95
+ mutate<T>(owner: string, change: (store: TaskStore) => T): Promise<T>;
96
+ /** The oldest queued background job of any owner, now running and leased. */
97
+ claimNext(worker: WorkerLease): Promise<OwnedTask | null>;
98
+ /** Keeps a claimed job leased while it runs. */
99
+ renew(worker: WorkerLease, job: OwnedTask): Promise<void>;
100
+ /** Records a job's outcome; a record no longer running is left alone. */
101
+ finish(job: OwnedTask, outcome: JobOutcome): Promise<void>;
102
+ /** Jobs whose worker died: queued again, or failed after MAX_ATTEMPTS. */
103
+ recover(): Promise<{
104
+ requeued: string[];
105
+ failed: string[];
106
+ }>;
107
+ }
108
+ /** Records kept at most; finished ones are pruned oldest-first to make room. */
109
+ export declare const MAX_TASKS = 500;
110
+ /** A worker result is stored up to this length, then cut with a marker. */
111
+ export declare const MAX_RESULT_CHARS = 20000;
112
+ /** A job interrupted this many times is marked failed instead of re-queued. */
113
+ export declare const MAX_ATTEMPTS = 2;
114
+ export declare function taskStorePath(): string;
115
+ /**
116
+ * Called once at worker start: a record left `running` means a worker died
117
+ * mid-job. It goes back to the queue, or fails once it has been tried
118
+ * MAX_ATTEMPTS times, so one poisoned job cannot loop forever.
119
+ */
120
+ export declare function recoverInterruptedJobs(): {
121
+ requeued: string[];
122
+ failed: string[];
123
+ };
124
+ /** Claims the oldest queued job (queued → running) and returns it, or null. */
125
+ export declare function claimNextJob(): TaskRecord | null;
126
+ /** Records a job's outcome. A record no longer `running` (edited by hand) is left alone. */
127
+ export declare function finishJob(id: string, outcome: JobOutcome): void;
128
+ /** The outcome rule, on a snapshot (every backend). */
129
+ export declare function applyFinish(store: TaskStore, id: string, outcome: JobOutcome): void;
130
+ /** The interrupted-job rule for one record: back to the queue, or failed. */
131
+ export declare function applyInterrupted(t: TaskRecord): 'requeued' | 'failed';
132
+ /** The single-user JSON file (the default backend). */
133
+ export declare const fileTaskBackend: TaskBackend;
134
+ /** Plugs in where tasks live (postgresStorage().taskQueue); default the JSON file. */
135
+ export declare function setTaskBackend(backend: TaskBackend): void;
136
+ export declare function getTaskBackend(): TaskBackend;
137
+ export declare const taskAddContract: import("./toolContract.ts").ToolContract<z.ZodObject<{
138
+ title: z.ZodString;
139
+ notes: z.ZodOptional<z.ZodString>;
140
+ due: z.ZodOptional<z.ZodString>;
141
+ }, z.core.$strip>>;
142
+ export declare const taskQueueContract: import("./toolContract.ts").ToolContract<z.ZodObject<{
143
+ title: z.ZodString;
144
+ instruction: z.ZodString;
145
+ }, z.core.$strip>>;
146
+ export declare const taskListContract: import("./toolContract.ts").ToolContract<z.ZodObject<{
147
+ status: z.ZodDefault<z.ZodEnum<{
148
+ active: "active";
149
+ all: "all";
150
+ cancelled: "cancelled";
151
+ done: "done";
152
+ failed: "failed";
153
+ open: "open";
154
+ queued: "queued";
155
+ running: "running";
156
+ }>>;
157
+ kind: z.ZodDefault<z.ZodEnum<{
158
+ any: "any";
159
+ background: "background";
160
+ todo: "todo";
161
+ }>>;
162
+ }, z.core.$strip>>;
163
+ export declare const taskGetContract: import("./toolContract.ts").ToolContract<z.ZodObject<{
164
+ id: z.ZodString;
165
+ }, z.core.$strip>>;
166
+ export declare const taskUpdateContract: import("./toolContract.ts").ToolContract<z.ZodObject<{
167
+ id: z.ZodString;
168
+ status: z.ZodOptional<z.ZodEnum<{
169
+ cancelled: "cancelled";
170
+ done: "done";
171
+ open: "open";
172
+ queued: "queued";
173
+ }>>;
174
+ title: z.ZodOptional<z.ZodString>;
175
+ notes: z.ZodOptional<z.ZodString>;
176
+ due: z.ZodOptional<z.ZodString>;
177
+ }, z.core.$strip>>;
178
+ /** Every task contract, in the order an agent's tool list shows them. */
179
+ export declare const TASK_TOOL_CONTRACTS: (import("./toolContract.ts").ToolContract<z.ZodObject<{
180
+ title: z.ZodString;
181
+ notes: z.ZodOptional<z.ZodString>;
182
+ due: z.ZodOptional<z.ZodString>;
183
+ }, z.core.$strip>> | import("./toolContract.ts").ToolContract<z.ZodObject<{
184
+ title: z.ZodString;
185
+ instruction: z.ZodString;
186
+ }, z.core.$strip>> | import("./toolContract.ts").ToolContract<z.ZodObject<{
187
+ status: z.ZodDefault<z.ZodEnum<{
188
+ active: "active";
189
+ all: "all";
190
+ cancelled: "cancelled";
191
+ done: "done";
192
+ failed: "failed";
193
+ open: "open";
194
+ queued: "queued";
195
+ running: "running";
196
+ }>>;
197
+ kind: z.ZodDefault<z.ZodEnum<{
198
+ any: "any";
199
+ background: "background";
200
+ todo: "todo";
201
+ }>>;
202
+ }, z.core.$strip>> | import("./toolContract.ts").ToolContract<z.ZodObject<{
203
+ id: z.ZodString;
204
+ }, z.core.$strip>>)[];