melchizedek-agents 0.18.0 → 0.19.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 (281) hide show
  1. package/.env.example +12 -0
  2. package/CHANGELOG.md +825 -0
  3. package/DOCUMENTATION.md +217 -59
  4. package/README.md +42 -1
  5. package/config/agents/examples/harness.yaml +3 -1
  6. package/config/agents/examples/model_zoo.yaml +10 -9
  7. package/config/agents/syndicateSchema.yaml +50 -16
  8. package/config/agents/templates/account_memory.yaml +2 -6
  9. package/config/agents/templates/case_desk.yaml +3 -9
  10. package/config/agents/templates/draft_review.yaml +3 -9
  11. package/config/agents/templates/intake_extractor.yaml +1 -3
  12. package/config/agents/templates/research_brief.yaml +3 -9
  13. package/config/agents/templates/research_desk.yaml +3 -9
  14. package/config/agents/templates/review_panel.yaml +4 -12
  15. package/config/agents/templates/support_triage.yaml +4 -12
  16. package/config/agents/templates/systems_operator.yaml +2 -6
  17. package/db/hardening.sql +7 -1
  18. package/db/migrations/0013_tool_credentials.sql +260 -0
  19. package/dist/lib/a2a/app.d.ts +70 -6
  20. package/dist/lib/a2a/app.js +107 -2
  21. package/dist/lib/a2a/executor.d.ts +20 -3
  22. package/dist/lib/a2a/executor.js +66 -7
  23. package/dist/lib/a2a/remoteAgent.d.ts +24 -9
  24. package/dist/lib/a2a/remoteAgent.js +31 -14
  25. package/dist/lib/compile.d.ts +121 -30
  26. package/dist/lib/compile.js +137 -124
  27. package/dist/lib/compileAdk.d.ts +29 -0
  28. package/dist/lib/compileAdk.js +132 -0
  29. package/dist/lib/compileNative.d.ts +85 -0
  30. package/dist/lib/compileNative.js +222 -0
  31. package/dist/lib/config.d.ts +6 -5
  32. package/dist/lib/config.js +6 -5
  33. package/dist/lib/doctor.js +1 -0
  34. package/dist/lib/index.d.ts +2 -1
  35. package/dist/lib/index.js +2 -1
  36. package/dist/lib/loadSyndicate.d.ts +18 -0
  37. package/dist/lib/memory/erase.d.ts +5 -3
  38. package/dist/lib/memory/erase.js +5 -2
  39. package/dist/lib/memory/namespace.d.ts +8 -1
  40. package/dist/lib/memory/namespace.js +12 -6
  41. package/dist/lib/memory/providers.js +2 -1
  42. package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
  43. package/dist/lib/memory/supabaseMemoryService.js +42 -2
  44. package/dist/lib/model.d.ts +47 -0
  45. package/dist/lib/model.js +37 -0
  46. package/dist/lib/models/adapterResolver.d.ts +113 -0
  47. package/dist/lib/models/adapterResolver.js +136 -0
  48. package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
  49. package/dist/lib/models/adkGeminiAdapter.js +367 -0
  50. package/dist/lib/models/adkShim.d.ts +111 -0
  51. package/dist/lib/models/adkShim.js +136 -0
  52. package/dist/lib/models/capabilities.d.ts +3 -0
  53. package/dist/lib/models/capabilities.js +20 -13
  54. package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
  55. package/dist/lib/models/chatCompletionsAdapter.js +854 -0
  56. package/dist/lib/models/circuitBreaker.d.ts +42 -0
  57. package/dist/lib/models/circuitBreaker.js +70 -0
  58. package/dist/lib/models/claudeAdapter.d.ts +103 -0
  59. package/dist/lib/models/claudeAdapter.js +669 -0
  60. package/dist/lib/models/claudeLlm.d.ts +39 -62
  61. package/dist/lib/models/claudeLlm.js +44 -349
  62. package/dist/lib/models/claudeModels.d.ts +127 -0
  63. package/dist/lib/models/claudeModels.js +237 -0
  64. package/dist/lib/models/contract.d.ts +395 -0
  65. package/dist/lib/models/contract.js +39 -0
  66. package/dist/lib/models/endpoints.js +2 -1
  67. package/dist/lib/models/errorResponse.d.ts +50 -0
  68. package/dist/lib/models/errorResponse.js +48 -0
  69. package/dist/lib/models/fallback.d.ts +17 -10
  70. package/dist/lib/models/fallback.js +53 -42
  71. package/dist/lib/models/fallbackAdapter.d.ts +55 -0
  72. package/dist/lib/models/fallbackAdapter.js +62 -0
  73. package/dist/lib/models/gateway.js +2 -1
  74. package/dist/lib/models/gatewayAdapter.d.ts +51 -0
  75. package/dist/lib/models/gatewayAdapter.js +81 -0
  76. package/dist/lib/models/gatewayLlm.d.ts +12 -33
  77. package/dist/lib/models/gatewayLlm.js +14 -75
  78. package/dist/lib/models/geminiAdapter.d.ts +123 -0
  79. package/dist/lib/models/geminiAdapter.js +831 -0
  80. package/dist/lib/models/geminiState.d.ts +25 -0
  81. package/dist/lib/models/geminiState.js +25 -0
  82. package/dist/lib/models/genaiMapping.d.ts +263 -0
  83. package/dist/lib/models/genaiMapping.js +862 -0
  84. package/dist/lib/models/gptAdapter.d.ts +240 -0
  85. package/dist/lib/models/gptAdapter.js +804 -0
  86. package/dist/lib/models/gptLlm.d.ts +63 -123
  87. package/dist/lib/models/gptLlm.js +88 -602
  88. package/dist/lib/models/grokAdapter.d.ts +86 -0
  89. package/dist/lib/models/grokAdapter.js +142 -0
  90. package/dist/lib/models/grokLlm.d.ts +22 -45
  91. package/dist/lib/models/grokLlm.js +22 -67
  92. package/dist/lib/models/kimiAdapter.d.ts +157 -0
  93. package/dist/lib/models/kimiAdapter.js +203 -0
  94. package/dist/lib/models/kimiLlm.d.ts +15 -107
  95. package/dist/lib/models/kimiLlm.js +17 -153
  96. package/dist/lib/models/ollamaAdapter.d.ts +125 -0
  97. package/dist/lib/models/ollamaAdapter.js +155 -0
  98. package/dist/lib/models/ollamaLlm.d.ts +11 -107
  99. package/dist/lib/models/ollamaLlm.js +13 -151
  100. package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
  101. package/dist/lib/models/openAiCompatibleLlm.js +109 -671
  102. package/dist/lib/models/providerState.d.ts +63 -0
  103. package/dist/lib/models/providerState.js +61 -0
  104. package/dist/lib/models/reasoning.d.ts +26 -0
  105. package/dist/lib/models/reasoning.js +59 -0
  106. package/dist/lib/models/registry.d.ts +58 -35
  107. package/dist/lib/models/registry.js +89 -118
  108. package/dist/lib/models/retry.d.ts +2 -1
  109. package/dist/lib/models/retry.js +3 -2
  110. package/dist/lib/models/schemaNormalize.d.ts +98 -0
  111. package/dist/lib/models/schemaNormalize.js +321 -1
  112. package/dist/lib/models/tracedGemini.d.ts +41 -0
  113. package/dist/lib/models/tracedGemini.js +98 -0
  114. package/dist/lib/models/urls.d.ts +11 -0
  115. package/dist/lib/models/urls.js +16 -0
  116. package/dist/lib/net/redirects.d.ts +21 -2
  117. package/dist/lib/net/redirects.js +11 -34
  118. package/dist/lib/observability/audit.d.ts +5 -1
  119. package/dist/lib/observability/lineage.d.ts +14 -0
  120. package/dist/lib/observability/lineage.js +33 -0
  121. package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
  122. package/dist/lib/observability/supabaseSpanExporter.js +17 -9
  123. package/dist/lib/observability/tracer.d.ts +26 -8
  124. package/dist/lib/observability/tracer.js +43 -18
  125. package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
  126. package/dist/lib/runtime/adkMemoryBridge.js +68 -0
  127. package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
  128. package/dist/lib/runtime/adkSessionBridge.js +176 -0
  129. package/dist/lib/runtime/credentials.d.ts +59 -0
  130. package/dist/lib/runtime/credentials.js +62 -0
  131. package/dist/lib/runtime/events.d.ts +229 -0
  132. package/dist/lib/runtime/events.js +274 -0
  133. package/dist/lib/runtime/logging.d.ts +32 -0
  134. package/dist/lib/runtime/logging.js +55 -0
  135. package/dist/lib/runtime/memoryService.d.ts +72 -0
  136. package/dist/lib/runtime/memoryService.js +25 -0
  137. package/dist/lib/runtime/native/agentLoop.d.ts +203 -0
  138. package/dist/lib/runtime/native/agentLoop.js +687 -0
  139. package/dist/lib/runtime/native/compaction.d.ts +103 -0
  140. package/dist/lib/runtime/native/compaction.js +191 -0
  141. package/dist/lib/runtime/native/delegate.d.ts +98 -0
  142. package/dist/lib/runtime/native/delegate.js +173 -0
  143. package/dist/lib/runtime/native/history.d.ts +83 -0
  144. package/dist/lib/runtime/native/history.js +312 -0
  145. package/dist/lib/runtime/native/interrupts.d.ts +126 -0
  146. package/dist/lib/runtime/native/interrupts.js +291 -0
  147. package/dist/lib/runtime/native/request.d.ts +215 -0
  148. package/dist/lib/runtime/native/request.js +585 -0
  149. package/dist/lib/runtime/native/selfCorrection.d.ts +106 -0
  150. package/dist/lib/runtime/native/selfCorrection.js +253 -0
  151. package/dist/lib/runtime/native/step.d.ts +148 -0
  152. package/dist/lib/runtime/native/step.js +239 -0
  153. package/dist/lib/runtime/native/taskMode.d.ts +64 -0
  154. package/dist/lib/runtime/native/taskMode.js +126 -0
  155. package/dist/lib/runtime/native/telemetry.d.ts +75 -0
  156. package/dist/lib/runtime/native/telemetry.js +299 -0
  157. package/dist/lib/runtime/native/tempState.d.ts +31 -0
  158. package/dist/lib/runtime/native/tempState.js +56 -0
  159. package/dist/lib/runtime/nativeTurn.d.ts +86 -0
  160. package/dist/lib/runtime/nativeTurn.js +107 -0
  161. package/dist/lib/runtime/questions.d.ts +29 -7
  162. package/dist/lib/runtime/questions.js +47 -32
  163. package/dist/lib/runtime/runtimeFlag.d.ts +30 -0
  164. package/dist/lib/runtime/runtimeFlag.js +50 -0
  165. package/dist/lib/runtime/sessions.d.ts +166 -0
  166. package/dist/lib/runtime/sessions.js +201 -0
  167. package/dist/lib/runtime/syndicateTurn.d.ts +41 -5
  168. package/dist/lib/runtime/syndicateTurn.js +202 -32
  169. package/dist/lib/session/supabaseSessionService.d.ts +64 -17
  170. package/dist/lib/session/supabaseSessionService.js +148 -105
  171. package/dist/lib/session/transcript.d.ts +68 -22
  172. package/dist/lib/session/transcript.js +102 -28
  173. package/dist/lib/skills.js +33 -7
  174. package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
  175. package/dist/lib/storage/postgres/credentialStore.js +82 -0
  176. package/dist/lib/storage/postgres/index.d.ts +12 -0
  177. package/dist/lib/storage/postgres/index.js +6 -1
  178. package/dist/lib/storage/postgres/sessionService.d.ts +43 -15
  179. package/dist/lib/storage/postgres/sessionService.js +128 -66
  180. package/dist/lib/syndicateSchema.d.ts +47 -0
  181. package/dist/lib/syndicateSchema.js +72 -3
  182. package/dist/lib/toolRegistry.d.ts +15 -2
  183. package/dist/lib/toolRegistry.js +33 -17
  184. package/dist/lib/tools/adkTool.d.ts +81 -0
  185. package/dist/lib/tools/adkTool.js +268 -0
  186. package/dist/lib/tools/auth.d.ts +118 -0
  187. package/dist/lib/tools/auth.js +89 -0
  188. package/dist/lib/tools/collectionsSearchTool.d.ts +7 -6
  189. package/dist/lib/tools/collectionsSearchTool.js +11 -26
  190. package/dist/lib/tools/credentialCipher.d.ts +58 -0
  191. package/dist/lib/tools/credentialCipher.js +116 -0
  192. package/dist/lib/tools/credentialStore.d.ts +77 -0
  193. package/dist/lib/tools/credentialStore.js +246 -0
  194. package/dist/lib/tools/examples.d.ts +43 -0
  195. package/dist/lib/tools/examples.js +62 -0
  196. package/dist/lib/tools/generateImageTool.d.ts +15 -6
  197. package/dist/lib/tools/generateImageTool.js +19 -29
  198. package/dist/lib/tools/inspectImageTool.d.ts +9 -6
  199. package/dist/lib/tools/inspectImageTool.js +13 -17
  200. package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
  201. package/dist/lib/tools/mcpToolFactory.js +46 -47
  202. package/dist/lib/tools/memoryTools.d.ts +61 -0
  203. package/dist/lib/tools/memoryTools.js +119 -0
  204. package/dist/lib/tools/nativeTools.d.ts +38 -0
  205. package/dist/lib/tools/nativeTools.js +43 -0
  206. package/dist/lib/tools/oauthConsent.d.ts +181 -0
  207. package/dist/lib/tools/oauthConsent.js +330 -0
  208. package/dist/lib/tools/openapi/call.d.ts +108 -0
  209. package/dist/lib/tools/openapi/call.js +295 -0
  210. package/dist/lib/tools/openapi/parse.d.ts +147 -0
  211. package/dist/lib/tools/openapi/parse.js +570 -0
  212. package/dist/lib/tools/openapiTools.d.ts +25 -17
  213. package/dist/lib/tools/openapiTools.js +124 -121
  214. package/dist/lib/tools/scienceTools.d.ts +14 -14
  215. package/dist/lib/tools/skillToolset.d.ts +58 -87
  216. package/dist/lib/tools/skillToolset.js +61 -142
  217. package/dist/lib/tools/skills/env.d.ts +58 -0
  218. package/dist/lib/tools/skills/env.js +165 -0
  219. package/dist/lib/tools/skills/executor.d.ts +114 -0
  220. package/dist/lib/tools/skills/executor.js +331 -0
  221. package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
  222. package/dist/lib/tools/skills/frontmatter.js +215 -0
  223. package/dist/lib/tools/skills/loader.d.ts +91 -0
  224. package/dist/lib/tools/skills/loader.js +257 -0
  225. package/dist/lib/tools/skills/tools.d.ts +187 -0
  226. package/dist/lib/tools/skills/tools.js +386 -0
  227. package/dist/lib/tools/taskTools.d.ts +15 -15
  228. package/dist/lib/tools/tool.d.ts +354 -0
  229. package/dist/lib/tools/tool.js +304 -0
  230. package/dist/lib/tools/toolContract.d.ts +89 -33
  231. package/dist/lib/tools/toolContract.js +104 -82
  232. package/dist/lib/tools/urlContextTool.d.ts +3 -0
  233. package/dist/lib/tools/urlContextTool.js +5 -1
  234. package/dist/lib/tools/webExtractTool.d.ts +2 -2
  235. package/dist/lib/tools/webExtractTool.js +2 -1
  236. package/dist/lib/tools/webSearchTool.d.ts +6 -9
  237. package/dist/lib/tools/webSearchTool.js +14 -59
  238. package/dist/lib/tools/wikiTools.d.ts +19 -19
  239. package/dist/lib/tools/wikiTools.js +13 -9
  240. package/dist/lib/tools/xApiSearchTool.d.ts +2 -2
  241. package/dist/lib/tools/xApiSearchTool.js +2 -1
  242. package/dist/lib/tools/xSearchTool.d.ts +7 -9
  243. package/dist/lib/tools/xSearchTool.js +11 -62
  244. package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
  245. package/dist/lib/tools/xaiSearchParams.js +126 -0
  246. package/dist/lib/wiki/agentRun.d.ts +12 -0
  247. package/dist/lib/wiki/agentRun.js +72 -34
  248. package/dist/lib/wiki/entities.d.ts +12 -0
  249. package/dist/lib/wiki/entities.js +45 -3
  250. package/dist/lib/workflow/agentNode.d.ts +136 -0
  251. package/dist/lib/workflow/agentNode.js +274 -0
  252. package/dist/lib/workflow/graph.d.ts +177 -0
  253. package/dist/lib/workflow/graph.js +458 -0
  254. package/dist/lib/workflow/nodeEvents.d.ts +50 -0
  255. package/dist/lib/workflow/nodeEvents.js +101 -0
  256. package/dist/lib/workflow/pause.d.ts +122 -0
  257. package/dist/lib/workflow/pause.js +180 -0
  258. package/dist/lib/workflow/resume.d.ts +165 -0
  259. package/dist/lib/workflow/resume.js +440 -0
  260. package/dist/lib/workflow/route.d.ts +58 -0
  261. package/dist/lib/workflow/route.js +69 -0
  262. package/dist/lib/workflow/scheduler.d.ts +415 -0
  263. package/dist/lib/workflow/scheduler.js +743 -0
  264. package/dist/lib/workflow/toolNode.d.ts +127 -0
  265. package/dist/lib/workflow/toolNode.js +236 -0
  266. package/dist/lib/workflow/turn.d.ts +118 -0
  267. package/dist/lib/workflow/turn.js +216 -0
  268. package/dist/lib/workflowConfig.d.ts +3 -2
  269. package/dist/lib/workflowConfig.js +3 -12
  270. package/dist/scripts/a2a_server.js +4 -3
  271. package/dist/scripts/assistant_worker.d.ts +2 -1
  272. package/dist/scripts/assistant_worker.js +9 -4
  273. package/dist/scripts/syndicate_chat.js +19 -10
  274. package/package.json +4 -1
  275. package/skills/briefs/melchizedek-author.md +3 -2
  276. package/skills/briefs/melchizedek-models.md +13 -9
  277. package/skills/briefs/melchizedek.md +3 -3
  278. package/skills/melchizedek/SKILL.md +2 -1
  279. package/skills/melchizedek-author/SKILL.md +2 -1
  280. package/skills/melchizedek-author/assets/minimal.yaml +2 -0
  281. package/skills/melchizedek-models/SKILL.md +23 -6
@@ -0,0 +1,62 @@
1
+ /**
2
+ * lib/tools/examples.ts — an agent's `examples:` as an instruction block
3
+ * (ADR 0059, ADR 0062).
4
+ *
5
+ * WHY this file exists:
6
+ * A YAML agent's `examples:` lists exchanges the model should imitate. ADK's
7
+ * ExampleTool wrote them into the system instruction of each request as a
8
+ * few-shot block, and the native runtime needs the same block without ADK.
9
+ * Writing into the instruction is what an InstructionTool does
10
+ * (lib/tools/tool.ts), so the examples are one: it declares no function,
11
+ * is never called, and adds the block, word for word as ExampleTool
12
+ * formatted it, before each request. The ADK runtime receives it through
13
+ * toAdkInstructionTool (lib/tools/adkTool.ts), which appends it where
14
+ * ExampleTool did.
15
+ *
16
+ * As with ExampleTool, the block is added only when the message that
17
+ * started the run begins with text. YAML examples are text alone, so the
18
+ * function-call renderings ExampleTool also had never apply.
19
+ *
20
+ * The examples are the agent author's prompt text, not the user's words.
21
+ * A LEAF: nothing in its import graph names @google/*.
22
+ */
23
+ /** The name ADK's ExampleTool carried, which the instruction block keeps. */
24
+ export const EXAMPLES_TOOL_NAME = 'example_tool';
25
+ // ExampleTool's strings (ADK's examples/example_util.js), kept word for word.
26
+ const EXAMPLES_INTRO = '<EXAMPLES>\nBegin few-shot\nThe following are examples of user queries and model responses using the available tools.\n\n';
27
+ const EXAMPLES_END = 'End few-shot\n<EXAMPLES>';
28
+ const EXAMPLE_END = 'End example\n\n';
29
+ const USER_PREFIX = '[user]\n';
30
+ const MODEL_PREFIX = '[model]\n';
31
+ /**
32
+ * The few-shot block for `examples`, as ExampleTool rendered text-only
33
+ * examples: each numbered, the user's text then the model's, each line
34
+ * ending in a newline. An empty input or output keeps its role line and
35
+ * adds no text, as ExampleTool did.
36
+ */
37
+ export function examplesInstruction(examples) {
38
+ let body = '';
39
+ examples.forEach((example, i) => {
40
+ body += `EXAMPLE ${i + 1}:\nBegin example\n${USER_PREFIX}`;
41
+ body += `${example.input ? example.input : ''}\n`;
42
+ body += MODEL_PREFIX;
43
+ if (example.output)
44
+ body += `${example.output}\n`;
45
+ body += EXAMPLE_END;
46
+ });
47
+ return `${EXAMPLES_INTRO}${body}${EXAMPLES_END}`;
48
+ }
49
+ /**
50
+ * An agent's examples as an InstructionTool, or undefined when it has none.
51
+ * It adds the block when the run's first message begins with text, as
52
+ * ExampleTool did, and nothing otherwise.
53
+ */
54
+ export function examplesInstructionTool(examples) {
55
+ if (!examples?.length)
56
+ return undefined;
57
+ const text = examplesInstruction(examples);
58
+ return {
59
+ name: EXAMPLES_TOOL_NAME,
60
+ instruction: async (ctx) => (ctx.userContent?.parts?.[0]?.text ? text : undefined),
61
+ };
62
+ }
@@ -1,16 +1,25 @@
1
1
  /**
2
- * generateImageTool — native ADK FunctionTool for real image generation.
2
+ * generateImageTool — a tool contract for real image generation.
3
3
  *
4
- * WHY a FunctionTool instead of an AgentTool subagent:
4
+ * WHY a function tool instead of an AgentTool subagent:
5
5
  * AgentTool converts all subagent output to a text function-response before
6
6
  * returning it to the orchestrator. Binary inlineData (the base64 image bytes
7
7
  * from an image-generation model) is therefore lost in transit.
8
8
  *
9
- * A FunctionTool runs arbitrary TypeScript: we call @google/genai directly,
9
+ * A function tool runs arbitrary TypeScript: we call @google/genai directly,
10
10
  * receive the raw response including inlineData, write the file to disk, and
11
11
  * return a plain-text confirmation with the saved file path. The orchestrator
12
12
  * sees the path and relays it to the user.
13
+ *
14
+ * The contract is an own Tool (lib/tools/tool.ts); `generateImageTool` is the
15
+ * FunctionTool the ADK runtime runs, made from it by toFunctionTool.
13
16
  */
14
- import { FunctionTool } from '@google/adk';
15
- import { type Schema } from '@google/genai';
16
- export declare const generateImageTool: FunctionTool<Schema>;
17
+ import { z } from 'zod';
18
+ export declare const generateImageContract: import("./toolContract.ts").DefinedTool<z.ZodObject<{
19
+ prompt: z.ZodString;
20
+ style: z.ZodOptional<z.ZodString>;
21
+ aspect_ratio: z.ZodOptional<z.ZodString>;
22
+ color_palette: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>, string>;
24
+ /** ADK surface, ready for the registry. */
25
+ export declare const generateImageTool: import("@google/adk").FunctionTool<undefined>;
@@ -1,50 +1,38 @@
1
1
  /**
2
- * generateImageTool — native ADK FunctionTool for real image generation.
2
+ * generateImageTool — a tool contract for real image generation.
3
3
  *
4
- * WHY a FunctionTool instead of an AgentTool subagent:
4
+ * WHY a function tool instead of an AgentTool subagent:
5
5
  * AgentTool converts all subagent output to a text function-response before
6
6
  * returning it to the orchestrator. Binary inlineData (the base64 image bytes
7
7
  * from an image-generation model) is therefore lost in transit.
8
8
  *
9
- * A FunctionTool runs arbitrary TypeScript: we call @google/genai directly,
9
+ * A function tool runs arbitrary TypeScript: we call @google/genai directly,
10
10
  * receive the raw response including inlineData, write the file to disk, and
11
11
  * return a plain-text confirmation with the saved file path. The orchestrator
12
12
  * sees the path and relays it to the user.
13
+ *
14
+ * The contract is an own Tool (lib/tools/tool.ts); `generateImageTool` is the
15
+ * FunctionTool the ADK runtime runs, made from it by toFunctionTool.
13
16
  */
14
- import { FunctionTool } from '@google/adk';
15
17
  import { GoogleGenAI } from '@google/genai';
16
18
  import { writeFileSync, mkdirSync } from 'node:fs';
17
19
  import { join, resolve } from 'node:path';
20
+ import { z } from 'zod';
21
+ import { toFunctionTool } from './adkTool.js';
22
+ import { defineTool } from './toolContract.js';
18
23
  const IMAGE_MODEL = 'gemini-3.1-flash-image-preview';
19
- export const generateImageTool = new FunctionTool({
24
+ export const generateImageContract = defineTool({
20
25
  name: 'generate_image',
21
26
  description: 'Generates a real image from a structured payload and saves it to the outputs/ directory. ' +
22
27
  'Returns the saved file path. Call this only after the user has approved the image payload.',
23
- parameters: {
24
- type: 'OBJECT',
25
- properties: {
26
- prompt: {
27
- type: 'STRING',
28
- description: 'The full image prompt describing the scene and subject.',
29
- },
30
- style: {
31
- type: 'STRING',
32
- description: 'Visual style (e.g. "Ansel Adams", "impressionist", "photorealistic").',
33
- },
34
- aspect_ratio: {
35
- type: 'STRING',
36
- description: 'Desired aspect ratio (e.g. "3:2", "16:9", "1:1").',
37
- },
38
- color_palette: {
39
- type: 'STRING',
40
- description: 'Color palette guidance (e.g. "monochromatic", "warm tones", "vivid").',
41
- },
42
- },
43
- required: ['prompt'],
44
- },
45
- execute: async (input) => {
28
+ schema: z.object({
29
+ prompt: z.string().describe('The full image prompt describing the scene and subject.'),
30
+ style: z.string().optional().describe('Visual style (e.g. "Ansel Adams", "impressionist", "photorealistic").'),
31
+ aspect_ratio: z.string().optional().describe('Desired aspect ratio (e.g. "3:2", "16:9", "1:1").'),
32
+ color_palette: z.string().optional().describe('Color palette guidance (e.g. "monochromatic", "warm tones", "vivid").'),
33
+ }),
34
+ execute: async ({ prompt, style, aspect_ratio, color_palette }) => {
46
35
  console.log("[ImageTool] Starting image generation execution...");
47
- const { prompt, style, aspect_ratio, color_palette } = input;
48
36
  const apiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_GENAI_API_KEY;
49
37
  if (!apiKey) {
50
38
  const err = new Error('Gemini API Key is not configured (neither GEMINI_API_KEY nor GOOGLE_GENAI_API_KEY was found in environment).');
@@ -117,3 +105,5 @@ export const generateImageTool = new FunctionTool({
117
105
  }
118
106
  },
119
107
  });
108
+ /** ADK surface, ready for the registry. */
109
+ export const generateImageTool = toFunctionTool(generateImageContract);
@@ -1,10 +1,10 @@
1
1
  /**
2
- * inspectImageTool — native ADK FunctionTool for BLIND visual inventory.
2
+ * inspectImageTool — a tool contract for BLIND visual inventory.
3
3
  *
4
- * WHY a FunctionTool instead of an AgentTool subagent:
4
+ * WHY a function tool instead of an AgentTool subagent:
5
5
  * Same constraint as generateImageTool in reverse — AgentTool converts all
6
6
  * traffic to text, so image bytes can't ride into a subagent's context.
7
- * A FunctionTool reads the saved file directly and calls the vision model
7
+ * A function tool reads the saved file directly and calls the vision model
8
8
  * itself.
9
9
  *
10
10
  * WHY the inventory prompt is hard-coded here and takes ONLY a file path:
@@ -16,6 +16,9 @@
16
16
  * system, not by good intentions. Judgment happens downstream in the
17
17
  * SpecAuditor subagent, which sees spec + inventory but never the image.
18
18
  */
19
- import { FunctionTool } from '@google/adk';
20
- import { type Schema } from '@google/genai';
21
- export declare const inspectImageTool: FunctionTool<Schema>;
19
+ import { z } from 'zod';
20
+ export declare const inspectImageContract: import("./toolContract.ts").DefinedTool<z.ZodObject<{
21
+ image_path: z.ZodString;
22
+ }, z.core.$strip>, string>;
23
+ /** ADK surface, ready for the registry. */
24
+ export declare const inspectImageTool: import("@google/adk").FunctionTool<undefined>;
@@ -1,10 +1,10 @@
1
1
  /**
2
- * inspectImageTool — native ADK FunctionTool for BLIND visual inventory.
2
+ * inspectImageTool — a tool contract for BLIND visual inventory.
3
3
  *
4
- * WHY a FunctionTool instead of an AgentTool subagent:
4
+ * WHY a function tool instead of an AgentTool subagent:
5
5
  * Same constraint as generateImageTool in reverse — AgentTool converts all
6
6
  * traffic to text, so image bytes can't ride into a subagent's context.
7
- * A FunctionTool reads the saved file directly and calls the vision model
7
+ * A function tool reads the saved file directly and calls the vision model
8
8
  * itself.
9
9
  *
10
10
  * WHY the inventory prompt is hard-coded here and takes ONLY a file path:
@@ -16,10 +16,12 @@
16
16
  * system, not by good intentions. Judgment happens downstream in the
17
17
  * SpecAuditor subagent, which sees spec + inventory but never the image.
18
18
  */
19
- import { FunctionTool } from '@google/adk';
20
19
  import { GoogleGenAI } from '@google/genai';
21
20
  import { readFileSync } from 'node:fs';
22
21
  import { resolve, extname, sep } from 'node:path';
22
+ import { z } from 'zod';
23
+ import { toFunctionTool } from './adkTool.js';
24
+ import { defineTool } from './toolContract.js';
23
25
  const VISION_MODEL = 'gemini-3.8-flash';
24
26
  const INVENTORY_PROTOCOL = `You are a blind visual inventory agent. You know NOTHING about what this image was supposed to be, who requested it, or why. Enumerate ONLY what is actually present:
25
27
 
@@ -36,23 +38,15 @@ RULES:
36
38
  - ZERO guesses about intent or purpose.
37
39
  - If something is genuinely ambiguous, write "uncertain:" and describe what is visible.
38
40
  Return a plain, factual, numbered inventory.`;
39
- export const inspectImageTool = new FunctionTool({
41
+ export const inspectImageContract = defineTool({
40
42
  name: 'inspect_image',
41
43
  description: 'Performs a BLIND visual inventory of a previously saved image (subjects with exact counts, ' +
42
44
  'composition, light, palette, medium cues, artifacts). Pass ONLY the file path returned by ' +
43
45
  'generate_image — never describe the expected content, or the inventory will be primed.',
44
- parameters: {
45
- type: 'OBJECT',
46
- properties: {
47
- image_path: {
48
- type: 'STRING',
49
- description: 'Path to the saved image, e.g. "outputs/image_1234.png".',
50
- },
51
- },
52
- required: ['image_path'],
53
- },
54
- execute: async (input) => {
55
- const { image_path } = input;
46
+ schema: z.object({
47
+ image_path: z.string().describe('Path to the saved image, e.g. "outputs/image_1234.png".'),
48
+ }),
49
+ execute: async ({ image_path }) => {
56
50
  console.log(`[InspectTool] Blind inventory requested for: ${image_path}`);
57
51
  // Confine reads to the outputs/ directory — this tool inventories
58
52
  // generated artifacts, not the filesystem. OUTPUTS_DIR env overrides
@@ -95,3 +89,5 @@ export const inspectImageTool = new FunctionTool({
95
89
  return `BLIND INVENTORY of ${image_path}:\n${text}`;
96
90
  },
97
91
  });
92
+ /** ADK surface, ready for the registry. */
93
+ export const inspectImageTool = toFunctionTool(inspectImageContract);
@@ -1,5 +1,23 @@
1
- import { FunctionTool } from '@google/adk';
1
+ /**
2
+ * lib/tools/mcpToolFactory.ts — a remote MCP server's tools, as the
3
+ * engine's own Tools (lib/tools/tool.ts, ADR 0062).
4
+ *
5
+ * An agent's `mcp_server_url:` connects here over SSE; every tool the
6
+ * server lists becomes an own Tool whose declaration is the server's
7
+ * description (bounded) and input schema, and whose execute calls the
8
+ * server and returns its text (bounded). loadMcpTools returns them for the
9
+ * native runtime; createMcpTools hands the ADK runtime the FunctionTool
10
+ * toFunctionTool (lib/tools/adkTool.ts) makes of each, so both runtimes
11
+ * declare the same parameters and run the same call.
12
+ *
13
+ * The server is an untrusted tool vendor (ADR 0041): its URL passes the
14
+ * SSRF guard, a credential goes only to its exact host, and its
15
+ * descriptions and results are cut to a bound.
16
+ */
17
+ import type { FunctionTool } from '@google/adk';
18
+ import type { ToolDeclaration } from '../models/contract.ts';
2
19
  import type { RedirectPolicy } from '../net/redirects.ts';
20
+ import type { Tool } from './tool.ts';
3
21
  export declare function assertSafeMcpUrl(raw: string, env?: NodeJS.ProcessEnv): Promise<URL>;
4
22
  export declare function mcpAuthHeaders(url: URL, env?: NodeJS.ProcessEnv): Record<string, string>;
5
23
  /**
@@ -19,6 +37,7 @@ export declare const mcpFetch: (input: string | URL | Request, init?: RequestIni
19
37
  * so a server cannot flood the context (or the bill) with either.
20
38
  */
21
39
  export declare const MAX_MCP_DESCRIPTION_CHARS = 1000;
40
+ /** Every tool's shared result limit (lib/tools/tool.ts). */
22
41
  export declare const MAX_MCP_RESULT_CHARS = 20000;
23
42
  /**
24
43
  * Close every open MCP connection. A connection otherwise lives as long as
@@ -27,4 +46,26 @@ export declare const MAX_MCP_RESULT_CHARS = 20000;
27
46
  * stops cleanly, or a test, calls this.
28
47
  */
29
48
  export declare function closeMcpConnections(): Promise<void>;
49
+ /**
50
+ * The parameters an MCP tool declares: the server's input schema, each
51
+ * top-level property given a type (string when the server names none) and a
52
+ * description, in the contract's lowercase dialect. They pass through
53
+ * Gemini's dialect first (toGeminiSchema), so the declaration is exactly
54
+ * what the ADK runtime's FunctionTool declares: `default`, `propertyNames`,
55
+ * `$schema` and a boolean `additionalProperties` are left out on both.
56
+ */
57
+ export declare function mcpToolParameters(inputSchema: {
58
+ properties?: Record<string, unknown>;
59
+ required?: string[];
60
+ } | undefined): ToolDeclaration['parameters'];
61
+ /**
62
+ * The tools a remote MCP server offers, as own Tools, or none when the
63
+ * server cannot be reached or refuses (logged, never thrown). The
64
+ * connection stays open for the tools' calls; closeMcpConnections ends it.
65
+ */
66
+ export declare function loadMcpTools(mcpServerUrl: string): Promise<Tool[]>;
67
+ /**
68
+ * The server's tools as the ADK runtime runs them: the FunctionTool
69
+ * toFunctionTool makes of each own Tool, carrying it for toolOf().
70
+ */
30
71
  export declare function createMcpTools(mcpServerUrl: string): Promise<FunctionTool[]>;
@@ -1,8 +1,11 @@
1
- import { FunctionTool } from '@google/adk';
2
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
2
  import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
3
+ import { toContractJsonSchema } from '../models/schemaNormalize.js';
4
4
  import { checkHost } from '../net/addressGuard.js';
5
5
  import { fetchWithRedirectPolicy } from '../net/redirects.js';
6
+ import { toFunctionTool } from './adkTool.js';
7
+ import { MAX_RESULT_CHARS } from './tool.js';
8
+ import { toGeminiSchema } from './toolContract.js';
6
9
  // Security (SSRF): mcp_server_url can arrive from a registry-stored syndicate
7
10
  // config. Only http(s), and the host must pass lib/net/addressGuard.ts (the
8
11
  // same guard web_extract uses): local names and non-public addresses —
@@ -87,7 +90,8 @@ export const mcpFetch = (input, init) => fetchWithRedirectPolicy(input, init, MC
87
90
  * so a server cannot flood the context (or the bill) with either.
88
91
  */
89
92
  export const MAX_MCP_DESCRIPTION_CHARS = 1_000;
90
- export const MAX_MCP_RESULT_CHARS = 20_000;
93
+ /** Every tool's shared result limit (lib/tools/tool.ts). */
94
+ export const MAX_MCP_RESULT_CHARS = MAX_RESULT_CHARS;
91
95
  const bounded = (text, max, what) => text.length <= max ? text : `${text.slice(0, max)}… [${what} cut at ${max} characters]`;
92
96
  /** Every MCP connection opened and still in use, so a shutdown can close them. */
93
97
  const openTransports = new Set();
@@ -102,7 +106,28 @@ export async function closeMcpConnections() {
102
106
  openTransports.clear();
103
107
  await Promise.all(all.map((t) => t.close().catch(() => { })));
104
108
  }
105
- export async function createMcpTools(mcpServerUrl) {
109
+ /**
110
+ * The parameters an MCP tool declares: the server's input schema, each
111
+ * top-level property given a type (string when the server names none) and a
112
+ * description, in the contract's lowercase dialect. They pass through
113
+ * Gemini's dialect first (toGeminiSchema), so the declaration is exactly
114
+ * what the ADK runtime's FunctionTool declares: `default`, `propertyNames`,
115
+ * `$schema` and a boolean `additionalProperties` are left out on both.
116
+ */
117
+ export function mcpToolParameters(inputSchema) {
118
+ const properties = {};
119
+ for (const [key, prop] of Object.entries(inputSchema?.properties ?? {})) {
120
+ properties[key] = { ...prop, type: prop?.type ?? 'string', description: prop?.description || '' };
121
+ }
122
+ const required = Array.isArray(inputSchema?.required) ? inputSchema.required : [];
123
+ return toContractJsonSchema(toGeminiSchema({ type: 'object', properties, required }));
124
+ }
125
+ /**
126
+ * The tools a remote MCP server offers, as own Tools, or none when the
127
+ * server cannot be reached or refuses (logged, never thrown). The
128
+ * connection stays open for the tools' calls; closeMcpConnections ends it.
129
+ */
130
+ export async function loadMcpTools(mcpServerUrl) {
106
131
  let transport;
107
132
  try {
108
133
  const url = await assertSafeMcpUrl(mcpServerUrl);
@@ -120,50 +145,17 @@ export async function createMcpTools(mcpServerUrl) {
120
145
  openTransports.add(transport);
121
146
  // Fetch available tools from the MCP server
122
147
  const toolsResponse = await client.listTools();
123
- // MCP servers describe tools in standard lowercase JSON Schema; the ADK
124
- // is Gemini-native and expects UPPERCASE type names. Uppercase deeply,
125
- // preserving enum/description/required and nested properties/items —
126
- // non-Gemini adapters normalize back to lowercase at request-build time
127
- // via lib/models/schemaNormalize.ts.
128
- const toGeminiSchema = (node) => {
129
- if (Array.isArray(node))
130
- return node.map(toGeminiSchema);
131
- if (!node || typeof node !== 'object')
132
- return node;
133
- const out = {};
134
- for (const [key, value] of Object.entries(node)) {
135
- if (key === 'type') {
136
- out[key] = typeof value === 'string' ? value.toUpperCase() : value;
137
- }
138
- else if (key === 'enum' || key === 'required') {
139
- out[key] = value; // value lists, not schema nodes
140
- }
141
- else {
142
- out[key] = toGeminiSchema(value);
143
- }
144
- }
145
- return out;
146
- };
147
- return toolsResponse.tools.map(tool => {
148
- const properties = {};
149
- const required = tool.inputSchema?.required || [];
150
- if (tool.inputSchema?.properties) {
151
- for (const [key, prop] of Object.entries(tool.inputSchema.properties)) {
152
- properties[key] = toGeminiSchema({
153
- ...prop,
154
- type: prop.type ?? 'string',
155
- description: prop.description || ''
156
- });
157
- }
158
- }
159
- return new FunctionTool({
148
+ return toolsResponse.tools.map((tool) => {
149
+ // MCP servers describe tools in standard lowercase JSON Schema, which
150
+ // is the contract's dialect; toFunctionTool derives Gemini's.
151
+ const declaration = {
160
152
  name: tool.name,
161
153
  description: bounded(tool.description || `MCP Tool: ${tool.name}`, MAX_MCP_DESCRIPTION_CHARS, 'description'),
162
- parameters: {
163
- type: 'OBJECT',
164
- properties,
165
- required
166
- },
154
+ parameters: mcpToolParameters(tool.inputSchema),
155
+ };
156
+ return {
157
+ name: tool.name,
158
+ declaration: () => declaration,
167
159
  execute: async (input) => {
168
160
  try {
169
161
  const result = await client.callTool({
@@ -177,8 +169,8 @@ export async function createMcpTools(mcpServerUrl) {
177
169
  catch (error) {
178
170
  return `[MCP ERROR] Tool ${tool.name} failed: ${error.message}`;
179
171
  }
180
- }
181
- });
172
+ },
173
+ };
182
174
  });
183
175
  }
184
176
  catch (error) {
@@ -191,3 +183,10 @@ export async function createMcpTools(mcpServerUrl) {
191
183
  return [];
192
184
  }
193
185
  }
186
+ /**
187
+ * The server's tools as the ADK runtime runs them: the FunctionTool
188
+ * toFunctionTool makes of each own Tool, carrying it for toolOf().
189
+ */
190
+ export async function createMcpTools(mcpServerUrl) {
191
+ return (await loadMcpTools(mcpServerUrl)).map((tool) => toFunctionTool(tool));
192
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * lib/tools/memoryTools.ts — long-term memory's two tools, as the engine's
3
+ * own (ADR 0020, ADR 0051, ADR 0059).
4
+ *
5
+ * WHY this file exists:
6
+ * A syndicate with `memory_system: long-term` reaches its memory through
7
+ * two YAML tool names. Both were ADK's own objects (LOAD_MEMORY and
8
+ * PRELOAD_MEMORY), which only ADK's loop can run. Here they are the
9
+ * engine's, so the native runtime runs them, and the ADK runtime runs the
10
+ * same objects through lib/tools/adkTool.ts:
11
+ *
12
+ * load_memory a Tool the model calls with a query. It returns the
13
+ * facts recalled for it, and while the run has memory it
14
+ * adds a note to the instruction saying memory exists.
15
+ * preload_memory an InstructionTool the model never calls. Before each
16
+ * request it recalls facts for the message that started
17
+ * the run and writes them into the instruction.
18
+ *
19
+ * WORD FOR WORD: the declaration, the note, the result's shape and the
20
+ * recalled block are what ADK's LoadMemoryTool and PreloadMemoryTool
21
+ * produced, so a model reads the same request on either runtime
22
+ * (tests/memoryTools.test.ts compares them with ADK's own tools).
23
+ *
24
+ * WHOSE MEMORY: both search through the context's `searchMemory`, which is
25
+ * bound to the run's own `<appName>/<userId>` silo; a query can choose what
26
+ * to recall, never whose. The runtime pins `appName` to the root syndicate's
27
+ * namespace (namespacedMemoryService, lib/memory/namespace.ts).
28
+ *
29
+ * Recalled facts are data the user's own conversations produced, never
30
+ * instructions: preload_memory fences them in a <PAST_CONVERSATIONS> block.
31
+ *
32
+ * No ADK here: nothing this module loads at runtime names @google/*.
33
+ */
34
+ import { z } from 'zod';
35
+ import type { MemoryEntry } from '../runtime/memoryService.ts';
36
+ import type { InstructionTool } from './tool.ts';
37
+ /** ADK's LoadMemoryTool note, word for word, line break included. */
38
+ export declare const LOAD_MEMORY_INSTRUCTION = "You have memory. You can use it to answer questions. If any questions need\nyou to look up the memory, you should call load_memory function with a query.";
39
+ /** What load_memory and ADK's LoadMemoryTool say when the run has no memory service. */
40
+ export declare const NO_MEMORY_SERVICE = "Memory service is not initialized.";
41
+ /** A memory's text: its parts' texts joined by a space, a part without text counting as empty, as ADK joins them. */
42
+ export declare function memoryText(memory: MemoryEntry): string;
43
+ /** One recalled memory as load_memory returns it. */
44
+ export interface LoadedMemory {
45
+ content: string;
46
+ author?: string;
47
+ timestamp?: string;
48
+ }
49
+ export declare const loadMemoryTool: import("./toolContract.ts").DefinedTool<z.ZodObject<{
50
+ query: z.ZodString;
51
+ }, z.core.$strip>, {
52
+ memories: LoadedMemory[];
53
+ }>;
54
+ /**
55
+ * The block preload_memory writes for the recalled memories, word for word
56
+ * ADK's PreloadMemoryTool's: each memory's time on its own line when it has
57
+ * one, then its text, prefixed by its author when it has one. Undefined when
58
+ * no memory has text.
59
+ */
60
+ export declare function preloadMemoryInstruction(memories: MemoryEntry[]): string | undefined;
61
+ export declare const preloadMemoryTool: InstructionTool;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * lib/tools/memoryTools.ts — long-term memory's two tools, as the engine's
3
+ * own (ADR 0020, ADR 0051, ADR 0059).
4
+ *
5
+ * WHY this file exists:
6
+ * A syndicate with `memory_system: long-term` reaches its memory through
7
+ * two YAML tool names. Both were ADK's own objects (LOAD_MEMORY and
8
+ * PRELOAD_MEMORY), which only ADK's loop can run. Here they are the
9
+ * engine's, so the native runtime runs them, and the ADK runtime runs the
10
+ * same objects through lib/tools/adkTool.ts:
11
+ *
12
+ * load_memory a Tool the model calls with a query. It returns the
13
+ * facts recalled for it, and while the run has memory it
14
+ * adds a note to the instruction saying memory exists.
15
+ * preload_memory an InstructionTool the model never calls. Before each
16
+ * request it recalls facts for the message that started
17
+ * the run and writes them into the instruction.
18
+ *
19
+ * WORD FOR WORD: the declaration, the note, the result's shape and the
20
+ * recalled block are what ADK's LoadMemoryTool and PreloadMemoryTool
21
+ * produced, so a model reads the same request on either runtime
22
+ * (tests/memoryTools.test.ts compares them with ADK's own tools).
23
+ *
24
+ * WHOSE MEMORY: both search through the context's `searchMemory`, which is
25
+ * bound to the run's own `<appName>/<userId>` silo; a query can choose what
26
+ * to recall, never whose. The runtime pins `appName` to the root syndicate's
27
+ * namespace (namespacedMemoryService, lib/memory/namespace.ts).
28
+ *
29
+ * Recalled facts are data the user's own conversations produced, never
30
+ * instructions: preload_memory fences them in a <PAST_CONVERSATIONS> block.
31
+ *
32
+ * No ADK here: nothing this module loads at runtime names @google/*.
33
+ */
34
+ import { z } from 'zod';
35
+ import { defineTool } from './toolContract.js';
36
+ /** ADK's LoadMemoryTool note, word for word, line break included. */
37
+ export const LOAD_MEMORY_INSTRUCTION = `You have memory. You can use it to answer questions. If any questions need
38
+ you to look up the memory, you should call load_memory function with a query.`;
39
+ /** What load_memory and ADK's LoadMemoryTool say when the run has no memory service. */
40
+ export const NO_MEMORY_SERVICE = 'Memory service is not initialized.';
41
+ /** A memory's text: its parts' texts joined by a space, a part without text counting as empty, as ADK joins them. */
42
+ export function memoryText(memory) {
43
+ return memory.content.parts?.map((p) => p.text ?? '').join(' ') ?? '';
44
+ }
45
+ export const loadMemoryTool = defineTool({
46
+ name: 'load_memory',
47
+ description: 'Loads the memory for the current user.\n\nNOTE: Currently this tool only uses text part from the memory.',
48
+ schema: z.object({
49
+ query: z.string().describe('The query to load the memory for.'),
50
+ }),
51
+ async execute({ query }, ctx) {
52
+ if (!ctx.searchMemory)
53
+ throw new Error(NO_MEMORY_SERVICE);
54
+ try {
55
+ const { memories } = await ctx.searchMemory(query);
56
+ return {
57
+ memories: memories.map((m) => ({ content: memoryText(m), author: m.author, timestamp: m.timestamp })),
58
+ };
59
+ }
60
+ catch (err) {
61
+ // A failed search is the call's error, reported to the model by the
62
+ // runtime. The query is the user's words, so it is not logged.
63
+ console.error('[load_memory] Memory search failed:', err instanceof Error ? err.message : String(err));
64
+ throw err;
65
+ }
66
+ },
67
+ // Only while the run has memory: a syndicate without it is not told it has some.
68
+ instruction: async (ctx) => (ctx.searchMemory ? LOAD_MEMORY_INSTRUCTION : undefined),
69
+ });
70
+ /**
71
+ * The block preload_memory writes for the recalled memories, word for word
72
+ * ADK's PreloadMemoryTool's: each memory's time on its own line when it has
73
+ * one, then its text, prefixed by its author when it has one. Undefined when
74
+ * no memory has text.
75
+ */
76
+ export function preloadMemoryInstruction(memories) {
77
+ const lines = [];
78
+ for (const memory of memories) {
79
+ if (memory.timestamp)
80
+ lines.push(`Time: ${memory.timestamp}`);
81
+ const text = memoryText(memory);
82
+ if (text)
83
+ lines.push(memory.author ? `${memory.author}: ${text}` : text);
84
+ }
85
+ if (lines.length === 0)
86
+ return undefined;
87
+ return `The following content is from your previous conversations with the user.
88
+ They may be useful for answering the user's current query.
89
+ <PAST_CONVERSATIONS>
90
+ ${lines.join('\n')}
91
+ </PAST_CONVERSATIONS>
92
+ `;
93
+ }
94
+ export const preloadMemoryTool = {
95
+ name: 'preload_memory',
96
+ /**
97
+ * The facts recalled for the first text part of the message that started
98
+ * the run. Nothing when that part has no text, the run has no memory,
99
+ * nothing is recalled, or the search fails: a failed recall never fails
100
+ * the request.
101
+ */
102
+ async instruction(ctx) {
103
+ const query = ctx.userContent?.parts?.[0]?.text;
104
+ if (!query || !ctx.searchMemory)
105
+ return undefined;
106
+ let memories;
107
+ try {
108
+ ({ memories } = await ctx.searchMemory(query));
109
+ }
110
+ catch (err) {
111
+ // ADK logged the query here; it is the user's words, so this does not.
112
+ console.warn('[preload_memory] Memory search failed; the request goes without recalled facts:', err instanceof Error ? err.message : String(err));
113
+ return undefined;
114
+ }
115
+ if (!memories || memories.length === 0)
116
+ return undefined;
117
+ return preloadMemoryInstruction(memories);
118
+ },
119
+ };