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.
- package/.env.example +12 -0
- package/CHANGELOG.md +825 -0
- package/DOCUMENTATION.md +217 -59
- package/README.md +42 -1
- package/config/agents/examples/harness.yaml +3 -1
- package/config/agents/examples/model_zoo.yaml +10 -9
- package/config/agents/syndicateSchema.yaml +50 -16
- package/config/agents/templates/account_memory.yaml +2 -6
- package/config/agents/templates/case_desk.yaml +3 -9
- package/config/agents/templates/draft_review.yaml +3 -9
- package/config/agents/templates/intake_extractor.yaml +1 -3
- package/config/agents/templates/research_brief.yaml +3 -9
- package/config/agents/templates/research_desk.yaml +3 -9
- package/config/agents/templates/review_panel.yaml +4 -12
- package/config/agents/templates/support_triage.yaml +4 -12
- package/config/agents/templates/systems_operator.yaml +2 -6
- package/db/hardening.sql +7 -1
- package/db/migrations/0013_tool_credentials.sql +260 -0
- package/dist/lib/a2a/app.d.ts +70 -6
- package/dist/lib/a2a/app.js +107 -2
- package/dist/lib/a2a/executor.d.ts +20 -3
- package/dist/lib/a2a/executor.js +66 -7
- package/dist/lib/a2a/remoteAgent.d.ts +24 -9
- package/dist/lib/a2a/remoteAgent.js +31 -14
- package/dist/lib/compile.d.ts +121 -30
- package/dist/lib/compile.js +137 -124
- package/dist/lib/compileAdk.d.ts +29 -0
- package/dist/lib/compileAdk.js +132 -0
- package/dist/lib/compileNative.d.ts +85 -0
- package/dist/lib/compileNative.js +222 -0
- package/dist/lib/config.d.ts +6 -5
- package/dist/lib/config.js +6 -5
- package/dist/lib/doctor.js +1 -0
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.js +2 -1
- package/dist/lib/loadSyndicate.d.ts +18 -0
- package/dist/lib/memory/erase.d.ts +5 -3
- package/dist/lib/memory/erase.js +5 -2
- package/dist/lib/memory/namespace.d.ts +8 -1
- package/dist/lib/memory/namespace.js +12 -6
- package/dist/lib/memory/providers.js +2 -1
- package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
- package/dist/lib/memory/supabaseMemoryService.js +42 -2
- package/dist/lib/model.d.ts +47 -0
- package/dist/lib/model.js +37 -0
- package/dist/lib/models/adapterResolver.d.ts +113 -0
- package/dist/lib/models/adapterResolver.js +136 -0
- package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
- package/dist/lib/models/adkGeminiAdapter.js +367 -0
- package/dist/lib/models/adkShim.d.ts +111 -0
- package/dist/lib/models/adkShim.js +136 -0
- package/dist/lib/models/capabilities.d.ts +3 -0
- package/dist/lib/models/capabilities.js +20 -13
- package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
- package/dist/lib/models/chatCompletionsAdapter.js +854 -0
- package/dist/lib/models/circuitBreaker.d.ts +42 -0
- package/dist/lib/models/circuitBreaker.js +70 -0
- package/dist/lib/models/claudeAdapter.d.ts +103 -0
- package/dist/lib/models/claudeAdapter.js +669 -0
- package/dist/lib/models/claudeLlm.d.ts +39 -62
- package/dist/lib/models/claudeLlm.js +44 -349
- package/dist/lib/models/claudeModels.d.ts +127 -0
- package/dist/lib/models/claudeModels.js +237 -0
- package/dist/lib/models/contract.d.ts +395 -0
- package/dist/lib/models/contract.js +39 -0
- package/dist/lib/models/endpoints.js +2 -1
- package/dist/lib/models/errorResponse.d.ts +50 -0
- package/dist/lib/models/errorResponse.js +48 -0
- package/dist/lib/models/fallback.d.ts +17 -10
- package/dist/lib/models/fallback.js +53 -42
- package/dist/lib/models/fallbackAdapter.d.ts +55 -0
- package/dist/lib/models/fallbackAdapter.js +62 -0
- package/dist/lib/models/gateway.js +2 -1
- package/dist/lib/models/gatewayAdapter.d.ts +51 -0
- package/dist/lib/models/gatewayAdapter.js +81 -0
- package/dist/lib/models/gatewayLlm.d.ts +12 -33
- package/dist/lib/models/gatewayLlm.js +14 -75
- package/dist/lib/models/geminiAdapter.d.ts +123 -0
- package/dist/lib/models/geminiAdapter.js +831 -0
- package/dist/lib/models/geminiState.d.ts +25 -0
- package/dist/lib/models/geminiState.js +25 -0
- package/dist/lib/models/genaiMapping.d.ts +263 -0
- package/dist/lib/models/genaiMapping.js +862 -0
- package/dist/lib/models/gptAdapter.d.ts +240 -0
- package/dist/lib/models/gptAdapter.js +804 -0
- package/dist/lib/models/gptLlm.d.ts +63 -123
- package/dist/lib/models/gptLlm.js +88 -602
- package/dist/lib/models/grokAdapter.d.ts +86 -0
- package/dist/lib/models/grokAdapter.js +142 -0
- package/dist/lib/models/grokLlm.d.ts +22 -45
- package/dist/lib/models/grokLlm.js +22 -67
- package/dist/lib/models/kimiAdapter.d.ts +157 -0
- package/dist/lib/models/kimiAdapter.js +203 -0
- package/dist/lib/models/kimiLlm.d.ts +15 -107
- package/dist/lib/models/kimiLlm.js +17 -153
- package/dist/lib/models/ollamaAdapter.d.ts +125 -0
- package/dist/lib/models/ollamaAdapter.js +155 -0
- package/dist/lib/models/ollamaLlm.d.ts +11 -107
- package/dist/lib/models/ollamaLlm.js +13 -151
- package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
- package/dist/lib/models/openAiCompatibleLlm.js +109 -671
- package/dist/lib/models/providerState.d.ts +63 -0
- package/dist/lib/models/providerState.js +61 -0
- package/dist/lib/models/reasoning.d.ts +26 -0
- package/dist/lib/models/reasoning.js +59 -0
- package/dist/lib/models/registry.d.ts +58 -35
- package/dist/lib/models/registry.js +89 -118
- package/dist/lib/models/retry.d.ts +2 -1
- package/dist/lib/models/retry.js +3 -2
- package/dist/lib/models/schemaNormalize.d.ts +98 -0
- package/dist/lib/models/schemaNormalize.js +321 -1
- package/dist/lib/models/tracedGemini.d.ts +41 -0
- package/dist/lib/models/tracedGemini.js +98 -0
- package/dist/lib/models/urls.d.ts +11 -0
- package/dist/lib/models/urls.js +16 -0
- package/dist/lib/net/redirects.d.ts +21 -2
- package/dist/lib/net/redirects.js +11 -34
- package/dist/lib/observability/audit.d.ts +5 -1
- package/dist/lib/observability/lineage.d.ts +14 -0
- package/dist/lib/observability/lineage.js +33 -0
- package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
- package/dist/lib/observability/supabaseSpanExporter.js +17 -9
- package/dist/lib/observability/tracer.d.ts +26 -8
- package/dist/lib/observability/tracer.js +43 -18
- package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
- package/dist/lib/runtime/adkMemoryBridge.js +68 -0
- package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
- package/dist/lib/runtime/adkSessionBridge.js +176 -0
- package/dist/lib/runtime/credentials.d.ts +59 -0
- package/dist/lib/runtime/credentials.js +62 -0
- package/dist/lib/runtime/events.d.ts +229 -0
- package/dist/lib/runtime/events.js +274 -0
- package/dist/lib/runtime/logging.d.ts +32 -0
- package/dist/lib/runtime/logging.js +55 -0
- package/dist/lib/runtime/memoryService.d.ts +72 -0
- package/dist/lib/runtime/memoryService.js +25 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +203 -0
- package/dist/lib/runtime/native/agentLoop.js +687 -0
- package/dist/lib/runtime/native/compaction.d.ts +103 -0
- package/dist/lib/runtime/native/compaction.js +191 -0
- package/dist/lib/runtime/native/delegate.d.ts +98 -0
- package/dist/lib/runtime/native/delegate.js +173 -0
- package/dist/lib/runtime/native/history.d.ts +83 -0
- package/dist/lib/runtime/native/history.js +312 -0
- package/dist/lib/runtime/native/interrupts.d.ts +126 -0
- package/dist/lib/runtime/native/interrupts.js +291 -0
- package/dist/lib/runtime/native/request.d.ts +215 -0
- package/dist/lib/runtime/native/request.js +585 -0
- package/dist/lib/runtime/native/selfCorrection.d.ts +106 -0
- package/dist/lib/runtime/native/selfCorrection.js +253 -0
- package/dist/lib/runtime/native/step.d.ts +148 -0
- package/dist/lib/runtime/native/step.js +239 -0
- package/dist/lib/runtime/native/taskMode.d.ts +64 -0
- package/dist/lib/runtime/native/taskMode.js +126 -0
- package/dist/lib/runtime/native/telemetry.d.ts +75 -0
- package/dist/lib/runtime/native/telemetry.js +299 -0
- package/dist/lib/runtime/native/tempState.d.ts +31 -0
- package/dist/lib/runtime/native/tempState.js +56 -0
- package/dist/lib/runtime/nativeTurn.d.ts +86 -0
- package/dist/lib/runtime/nativeTurn.js +107 -0
- package/dist/lib/runtime/questions.d.ts +29 -7
- package/dist/lib/runtime/questions.js +47 -32
- package/dist/lib/runtime/runtimeFlag.d.ts +30 -0
- package/dist/lib/runtime/runtimeFlag.js +50 -0
- package/dist/lib/runtime/sessions.d.ts +166 -0
- package/dist/lib/runtime/sessions.js +201 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +41 -5
- package/dist/lib/runtime/syndicateTurn.js +202 -32
- package/dist/lib/session/supabaseSessionService.d.ts +64 -17
- package/dist/lib/session/supabaseSessionService.js +148 -105
- package/dist/lib/session/transcript.d.ts +68 -22
- package/dist/lib/session/transcript.js +102 -28
- package/dist/lib/skills.js +33 -7
- package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
- package/dist/lib/storage/postgres/credentialStore.js +82 -0
- package/dist/lib/storage/postgres/index.d.ts +12 -0
- package/dist/lib/storage/postgres/index.js +6 -1
- package/dist/lib/storage/postgres/sessionService.d.ts +43 -15
- package/dist/lib/storage/postgres/sessionService.js +128 -66
- package/dist/lib/syndicateSchema.d.ts +47 -0
- package/dist/lib/syndicateSchema.js +72 -3
- package/dist/lib/toolRegistry.d.ts +15 -2
- package/dist/lib/toolRegistry.js +33 -17
- package/dist/lib/tools/adkTool.d.ts +81 -0
- package/dist/lib/tools/adkTool.js +268 -0
- package/dist/lib/tools/auth.d.ts +118 -0
- package/dist/lib/tools/auth.js +89 -0
- package/dist/lib/tools/collectionsSearchTool.d.ts +7 -6
- package/dist/lib/tools/collectionsSearchTool.js +11 -26
- package/dist/lib/tools/credentialCipher.d.ts +58 -0
- package/dist/lib/tools/credentialCipher.js +116 -0
- package/dist/lib/tools/credentialStore.d.ts +77 -0
- package/dist/lib/tools/credentialStore.js +246 -0
- package/dist/lib/tools/examples.d.ts +43 -0
- package/dist/lib/tools/examples.js +62 -0
- package/dist/lib/tools/generateImageTool.d.ts +15 -6
- package/dist/lib/tools/generateImageTool.js +19 -29
- package/dist/lib/tools/inspectImageTool.d.ts +9 -6
- package/dist/lib/tools/inspectImageTool.js +13 -17
- package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
- package/dist/lib/tools/mcpToolFactory.js +46 -47
- package/dist/lib/tools/memoryTools.d.ts +61 -0
- package/dist/lib/tools/memoryTools.js +119 -0
- package/dist/lib/tools/nativeTools.d.ts +38 -0
- package/dist/lib/tools/nativeTools.js +43 -0
- package/dist/lib/tools/oauthConsent.d.ts +181 -0
- package/dist/lib/tools/oauthConsent.js +330 -0
- package/dist/lib/tools/openapi/call.d.ts +108 -0
- package/dist/lib/tools/openapi/call.js +295 -0
- package/dist/lib/tools/openapi/parse.d.ts +147 -0
- package/dist/lib/tools/openapi/parse.js +570 -0
- package/dist/lib/tools/openapiTools.d.ts +25 -17
- package/dist/lib/tools/openapiTools.js +124 -121
- package/dist/lib/tools/scienceTools.d.ts +14 -14
- package/dist/lib/tools/skillToolset.d.ts +58 -87
- package/dist/lib/tools/skillToolset.js +61 -142
- package/dist/lib/tools/skills/env.d.ts +58 -0
- package/dist/lib/tools/skills/env.js +165 -0
- package/dist/lib/tools/skills/executor.d.ts +114 -0
- package/dist/lib/tools/skills/executor.js +331 -0
- package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
- package/dist/lib/tools/skills/frontmatter.js +215 -0
- package/dist/lib/tools/skills/loader.d.ts +91 -0
- package/dist/lib/tools/skills/loader.js +257 -0
- package/dist/lib/tools/skills/tools.d.ts +187 -0
- package/dist/lib/tools/skills/tools.js +386 -0
- package/dist/lib/tools/taskTools.d.ts +15 -15
- package/dist/lib/tools/tool.d.ts +354 -0
- package/dist/lib/tools/tool.js +304 -0
- package/dist/lib/tools/toolContract.d.ts +89 -33
- package/dist/lib/tools/toolContract.js +104 -82
- package/dist/lib/tools/urlContextTool.d.ts +3 -0
- package/dist/lib/tools/urlContextTool.js +5 -1
- package/dist/lib/tools/webExtractTool.d.ts +2 -2
- package/dist/lib/tools/webExtractTool.js +2 -1
- package/dist/lib/tools/webSearchTool.d.ts +6 -9
- package/dist/lib/tools/webSearchTool.js +14 -59
- package/dist/lib/tools/wikiTools.d.ts +19 -19
- package/dist/lib/tools/wikiTools.js +13 -9
- package/dist/lib/tools/xApiSearchTool.d.ts +2 -2
- package/dist/lib/tools/xApiSearchTool.js +2 -1
- package/dist/lib/tools/xSearchTool.d.ts +7 -9
- package/dist/lib/tools/xSearchTool.js +11 -62
- package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
- package/dist/lib/tools/xaiSearchParams.js +126 -0
- package/dist/lib/wiki/agentRun.d.ts +12 -0
- package/dist/lib/wiki/agentRun.js +72 -34
- package/dist/lib/wiki/entities.d.ts +12 -0
- package/dist/lib/wiki/entities.js +45 -3
- package/dist/lib/workflow/agentNode.d.ts +136 -0
- package/dist/lib/workflow/agentNode.js +274 -0
- package/dist/lib/workflow/graph.d.ts +177 -0
- package/dist/lib/workflow/graph.js +458 -0
- package/dist/lib/workflow/nodeEvents.d.ts +50 -0
- package/dist/lib/workflow/nodeEvents.js +101 -0
- package/dist/lib/workflow/pause.d.ts +122 -0
- package/dist/lib/workflow/pause.js +180 -0
- package/dist/lib/workflow/resume.d.ts +165 -0
- package/dist/lib/workflow/resume.js +440 -0
- package/dist/lib/workflow/route.d.ts +58 -0
- package/dist/lib/workflow/route.js +69 -0
- package/dist/lib/workflow/scheduler.d.ts +415 -0
- package/dist/lib/workflow/scheduler.js +743 -0
- package/dist/lib/workflow/toolNode.d.ts +127 -0
- package/dist/lib/workflow/toolNode.js +236 -0
- package/dist/lib/workflow/turn.d.ts +118 -0
- package/dist/lib/workflow/turn.js +216 -0
- package/dist/lib/workflowConfig.d.ts +3 -2
- package/dist/lib/workflowConfig.js +3 -12
- package/dist/scripts/a2a_server.js +4 -3
- package/dist/scripts/assistant_worker.d.ts +2 -1
- package/dist/scripts/assistant_worker.js +9 -4
- package/dist/scripts/syndicate_chat.js +19 -10
- package/package.json +4 -1
- package/skills/briefs/melchizedek-author.md +3 -2
- package/skills/briefs/melchizedek-models.md +13 -9
- package/skills/briefs/melchizedek.md +3 -3
- package/skills/melchizedek/SKILL.md +2 -1
- package/skills/melchizedek-author/SKILL.md +2 -1
- package/skills/melchizedek-author/assets/minimal.yaml +2 -0
- 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 —
|
|
2
|
+
* generateImageTool — a tool contract for real image generation.
|
|
3
3
|
*
|
|
4
|
-
* WHY a
|
|
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
|
|
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 {
|
|
15
|
-
|
|
16
|
-
|
|
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 —
|
|
2
|
+
* generateImageTool — a tool contract for real image generation.
|
|
3
3
|
*
|
|
4
|
-
* WHY a
|
|
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
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 —
|
|
2
|
+
* inspectImageTool — a tool contract for BLIND visual inventory.
|
|
3
3
|
*
|
|
4
|
-
* WHY a
|
|
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
|
|
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 {
|
|
20
|
-
|
|
21
|
-
|
|
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 —
|
|
2
|
+
* inspectImageTool — a tool contract for BLIND visual inventory.
|
|
3
3
|
*
|
|
4
|
-
* WHY a
|
|
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
|
|
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
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
};
|