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,585 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/runtime/native/request.ts — one model request of the native loop,
|
|
3
|
+
* built from an agent and its session (ADR 0045, ADR 0066).
|
|
4
|
+
*
|
|
5
|
+
* WHY this file exists:
|
|
6
|
+
* The native runtime calls a ModelAdapter (lib/models/contract.ts) with a
|
|
7
|
+
* ModelRequest it builds itself, with no LlmRequest in between. A session
|
|
8
|
+
* moves between the runtimes and an agent must behave the same on either,
|
|
9
|
+
* so the request is the one the ADK runtime hands the same adapter for
|
|
10
|
+
* the same agent and session: what ADK's LlmAgent request processors and
|
|
11
|
+
* tools build, read through the shim's mapping (llmRequestToModelRequest,
|
|
12
|
+
* lib/models/genaiMapping.ts). tests/nativeStep.test.ts asserts that on
|
|
13
|
+
* whole syndicates.
|
|
14
|
+
*
|
|
15
|
+
* THE REQUEST, in the order ADK builds it:
|
|
16
|
+
* 1. The agent's generateContentConfig, and its output schema when it has
|
|
17
|
+
* no tools (or the model takes a schema beside tools: Gemini 2 and
|
|
18
|
+
* later on Vertex AI).
|
|
19
|
+
* 2. The system prompt, each piece joined to the last by a blank line:
|
|
20
|
+
* - the identity lines ("You are an agent. Your internal name is …"),
|
|
21
|
+
* unless the agent may transfer to no one (an output schema rules
|
|
22
|
+
* transfer out);
|
|
23
|
+
* - the root agent's globalInstruction, then the agent's instruction,
|
|
24
|
+
* each with `{state_key}` placeholders filled from session state,
|
|
25
|
+
* and, in a workflow node's run, `{input.field}` and
|
|
26
|
+
* `<input.field from Node>` filled from its workflow scope;
|
|
27
|
+
* - with an output schema beside tools on a model that cannot take
|
|
28
|
+
* both, the line asking for set_model_response;
|
|
29
|
+
* - then, at each tool's place in the agent's list, the text its
|
|
30
|
+
* `instruction(ctx)` writes: few-shot examples, preload_memory, an
|
|
31
|
+
* own Tool's note (ADR 0059, ADR 0062). The skills index is part of
|
|
32
|
+
* the instruction itself (lib/compile.ts appends it).
|
|
33
|
+
* A Tool's `contents` hook then adds to the history, after it is
|
|
34
|
+
* projected (load_skill_resource's binary file, ADR 0083).
|
|
35
|
+
* 3. The history: the session's events projected by includeContents
|
|
36
|
+
* (lib/runtime/native/history.ts), with Gemini code execution's parts
|
|
37
|
+
* as text when the agent runs code.
|
|
38
|
+
* 4. The tools, in the agent's order, a toolset expanded to its tools:
|
|
39
|
+
* each client-side tool's declaration (contractToolDeclaration), at
|
|
40
|
+
* most one per name, the later one winning; each server-side tool
|
|
41
|
+
* where the ADK runtime sends it (web_search everywhere, url_context
|
|
42
|
+
* and google_search on Gemini, x_search and collections_search on
|
|
43
|
+
* xAI); `code_execution` first among Gemini's own tools; and the
|
|
44
|
+
* set_model_response tool when (2) asked for it, then the caller's
|
|
45
|
+
* `extraTools` (self-correction's reflection tool, as ADK's plugin adds
|
|
46
|
+
* it to the toolsDict last). In `mode: task`, finish_task takes
|
|
47
|
+
* set_model_response's place and the output schema is never the
|
|
48
|
+
* response schema (lib/runtime/native/taskMode.ts).
|
|
49
|
+
* 5. Tool choice, the output schema or JSON mode, reasoning and sampling,
|
|
50
|
+
* read from the config by the mapping's own readers.
|
|
51
|
+
*
|
|
52
|
+
* WHAT IT DOES NOT DO (later tickets): resume an approval or an input
|
|
53
|
+
* request (ADK's confirmation and input processors run tools before the
|
|
54
|
+
* request; WS2-7), add transfer_to_agent
|
|
55
|
+
* (compiled syndicates never set subAgents; they delegate through subagent tools, delegate.ts),
|
|
56
|
+
* artifacts in an
|
|
57
|
+
* instruction (no runtime has an artifact service), and an ADK tool's own
|
|
58
|
+
* processLlmRequest side effects beyond its declaration (an own Tool says
|
|
59
|
+
* what it writes through `instruction`).
|
|
60
|
+
*
|
|
61
|
+
* ADK STAYS OUT OF THIS FILE'S OWN LOGIC: it reads tools by marker and by
|
|
62
|
+
* the own-tool symbols (lib/tools/tool.ts), and only lib/models/
|
|
63
|
+
* genaiMapping.ts, which stored history needs anyway (ADR 0048 item 8),
|
|
64
|
+
* names @google/*. An ADK tool or toolset an agent still carries (an
|
|
65
|
+
* AgentTool, an MCP or OpenAPI tool) is read through its declaration and
|
|
66
|
+
* `getTools`, by shape, as is the engine's own skills toolset.
|
|
67
|
+
*/
|
|
68
|
+
import { contentsToMessages, reasoningOf, samplingOf, systemText, toolChoiceOf } from '../../models/genaiMapping.js';
|
|
69
|
+
import { providerForModel } from '../../models/providerMap.js';
|
|
70
|
+
import { contractToolDeclaration, nativeToolOf, toContractJsonSchema } from '../../models/schemaNormalize.js';
|
|
71
|
+
import { createToolContext, instructionToolOf, isTool, toolOf } from '../../tools/tool.js';
|
|
72
|
+
import { convertCodeExecutionParts, projectHistory } from './history.js';
|
|
73
|
+
import { finishTaskTool } from './taskMode.js';
|
|
74
|
+
import { withStateOverlay } from './tempState.js';
|
|
75
|
+
// ── set_model_response (ADK's output schema beside tools) ────────────────────
|
|
76
|
+
/** The tool ADK adds when an agent has an output schema and tools a model cannot take together. */
|
|
77
|
+
export const SET_MODEL_RESPONSE = 'set_model_response';
|
|
78
|
+
const SET_MODEL_RESPONSE_DESCRIPTION = 'Call this tool to submit your final response conforming to the output schema. Use this tool only when you have collected all the information and are ready to return the final answer.';
|
|
79
|
+
const SET_MODEL_RESPONSE_INSTRUCTION = 'To output the final result, you must call the "set_model_response" function with the appropriate values. Do not output anything else.';
|
|
80
|
+
/** set_model_response as an own Tool: its answer is the arguments, as JSON text, and it ends the step. */
|
|
81
|
+
export function setModelResponseTool(outputSchema) {
|
|
82
|
+
return {
|
|
83
|
+
name: SET_MODEL_RESPONSE,
|
|
84
|
+
declaration: () => ({
|
|
85
|
+
name: SET_MODEL_RESPONSE,
|
|
86
|
+
description: SET_MODEL_RESPONSE_DESCRIPTION,
|
|
87
|
+
parameters: toContractJsonSchema(outputSchema),
|
|
88
|
+
}),
|
|
89
|
+
execute: async (args, ctx) => {
|
|
90
|
+
ctx.actions.skipSummarization = true;
|
|
91
|
+
return JSON.stringify(args);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// ── Model names (ADK's utils/model_name.js and output_schema_utils.js) ───────
|
|
96
|
+
/** A Vertex AI resource name's model id, else the id itself. Split, never a backtracking pattern. */
|
|
97
|
+
function modelName(model) {
|
|
98
|
+
const parts = model.split('/');
|
|
99
|
+
const isResource = parts.length >= 8 &&
|
|
100
|
+
parts[0] === 'projects' &&
|
|
101
|
+
parts[2] === 'locations' &&
|
|
102
|
+
parts[4] === 'publishers' &&
|
|
103
|
+
parts[6] === 'models' &&
|
|
104
|
+
[1, 3, 5].every((i) => (parts[i] ?? '') !== '');
|
|
105
|
+
if (!isResource)
|
|
106
|
+
return model;
|
|
107
|
+
const rest = parts.slice(7).join('/');
|
|
108
|
+
return rest === '' ? model : rest;
|
|
109
|
+
}
|
|
110
|
+
function isGeminiModel(model) {
|
|
111
|
+
return modelName(model).startsWith('gemini-');
|
|
112
|
+
}
|
|
113
|
+
function isGemini1Model(model) {
|
|
114
|
+
return modelName(model).startsWith('gemini-1');
|
|
115
|
+
}
|
|
116
|
+
function isGemini2OrAbove(model) {
|
|
117
|
+
const name = modelName(model);
|
|
118
|
+
if (!name.startsWith('gemini-'))
|
|
119
|
+
return false;
|
|
120
|
+
const version = name.slice('gemini-'.length).split('-', 1)[0] ?? '';
|
|
121
|
+
if (!/^\d+(\.\d+)*$/.test(version))
|
|
122
|
+
return false;
|
|
123
|
+
return Number.parseInt(version, 10) >= 2;
|
|
124
|
+
}
|
|
125
|
+
function envFlag(name) {
|
|
126
|
+
return ['true', '1'].includes((process.env[name] || '').toLowerCase());
|
|
127
|
+
}
|
|
128
|
+
/** ADK's enterprise switch: GOOGLE_GENAI_USE_ENTERPRISE, else the deprecated GOOGLE_GENAI_USE_VERTEXAI. */
|
|
129
|
+
function vertexAi() {
|
|
130
|
+
if (process.env.GOOGLE_GENAI_USE_ENTERPRISE !== undefined)
|
|
131
|
+
return envFlag('GOOGLE_GENAI_USE_ENTERPRISE');
|
|
132
|
+
return envFlag('GOOGLE_GENAI_USE_VERTEXAI');
|
|
133
|
+
}
|
|
134
|
+
/** Whether the model takes an output schema beside tools in one request (ADK: Vertex AI, Gemini 2 and later). */
|
|
135
|
+
export function outputSchemaWithTools(model) {
|
|
136
|
+
return vertexAi() && isGemini2OrAbove(model);
|
|
137
|
+
}
|
|
138
|
+
// ── Session state in an instruction (ADK's injectSessionState) ───────────────
|
|
139
|
+
const STATE_PREFIXES = ['app:', 'user:', 'temp:'];
|
|
140
|
+
const ARTIFACT_PREFIX = 'artifact.';
|
|
141
|
+
function isIdentifier(s) {
|
|
142
|
+
return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s);
|
|
143
|
+
}
|
|
144
|
+
function isStateName(name) {
|
|
145
|
+
const parts = name.split(':');
|
|
146
|
+
if (parts.length === 1)
|
|
147
|
+
return isIdentifier(name);
|
|
148
|
+
if (parts.length !== 2)
|
|
149
|
+
return false;
|
|
150
|
+
return STATE_PREFIXES.includes(`${parts[0]}:`) && isIdentifier(parts[1]);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Every `{…}` placeholder in a template, as ADK's `/\{+[^{}]*}+/g` finds
|
|
154
|
+
* them, by one linear scan (the regular expression backtracks on a long run
|
|
155
|
+
* of braces): a run of `{`, then no brace, then a run of `}`.
|
|
156
|
+
*/
|
|
157
|
+
function placeholders(template) {
|
|
158
|
+
const found = [];
|
|
159
|
+
let i = 0;
|
|
160
|
+
while (i < template.length) {
|
|
161
|
+
const open = template.indexOf('{', i);
|
|
162
|
+
if (open < 0)
|
|
163
|
+
break;
|
|
164
|
+
let j = open;
|
|
165
|
+
while (template[j] === '{')
|
|
166
|
+
j++;
|
|
167
|
+
let k = j;
|
|
168
|
+
while (k < template.length && template[k] !== '{' && template[k] !== '}')
|
|
169
|
+
k++;
|
|
170
|
+
if (template[k] !== '}') {
|
|
171
|
+
i = k;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
let end = k;
|
|
175
|
+
while (template[end] === '}')
|
|
176
|
+
end++;
|
|
177
|
+
found.push({ raw: template.slice(open, end), index: open });
|
|
178
|
+
i = end;
|
|
179
|
+
}
|
|
180
|
+
return found;
|
|
181
|
+
}
|
|
182
|
+
const isWordChar = (c) => c !== undefined && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '_');
|
|
183
|
+
const isIdentStart = (c) => c !== undefined && c !== '' && isWordChar(c) && !(c >= '0' && c <= '9');
|
|
184
|
+
const isSpace = (c) => c !== undefined && c !== '' && /\s/.test(c);
|
|
185
|
+
/** ADK's WORKFLOW_FIELD_KEY, `^[A-Za-z_]\w*\.[A-Za-z_]\w*$`, without a pattern: two identifiers joined by one dot. */
|
|
186
|
+
function isWorkflowFieldKey(key) {
|
|
187
|
+
const dot = key.indexOf('.');
|
|
188
|
+
return dot > 0 && isIdentifier(key.slice(0, dot)) && isIdentifier(key.slice(dot + 1));
|
|
189
|
+
}
|
|
190
|
+
/** The identifier `[A-Za-z_]\w*` starting at `i`, and where it ends; undefined when none starts there. */
|
|
191
|
+
function identAt(s, i) {
|
|
192
|
+
if (!isIdentStart(s[i]))
|
|
193
|
+
return undefined;
|
|
194
|
+
let end = i + 1;
|
|
195
|
+
while (isWordChar(s[end]))
|
|
196
|
+
end++;
|
|
197
|
+
return { name: s.slice(i, end), end };
|
|
198
|
+
}
|
|
199
|
+
function skipSpaces(s, i) {
|
|
200
|
+
while (isSpace(s[i]))
|
|
201
|
+
i++;
|
|
202
|
+
return i;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Every `<x.field from Node>` placeholder, as ADK's SOURCE_NODE_PLACEHOLDER,
|
|
206
|
+
* `/<\s*[A-Za-z_]\w*\.([A-Za-z_]\w*)\s+from\s+([A-Za-z_]\w*)\s*>/g`, finds
|
|
207
|
+
* them. Each token of that pattern is determined by the next character, so
|
|
208
|
+
* a hand parser takes the same matches; an attempt from a `<` cannot pass
|
|
209
|
+
* the next `<`, so the scan is linear.
|
|
210
|
+
*/
|
|
211
|
+
function sourcePlaceholders(template) {
|
|
212
|
+
const found = [];
|
|
213
|
+
let open = template.indexOf('<');
|
|
214
|
+
while (open >= 0) {
|
|
215
|
+
const match = sourcePlaceholderAt(template, open);
|
|
216
|
+
if (match)
|
|
217
|
+
found.push(match);
|
|
218
|
+
open = template.indexOf('<', match ? match.index + match.raw.length : open + 1);
|
|
219
|
+
}
|
|
220
|
+
return found;
|
|
221
|
+
}
|
|
222
|
+
function sourcePlaceholderAt(s, open) {
|
|
223
|
+
const head = identAt(s, skipSpaces(s, open + 1));
|
|
224
|
+
if (!head || s[head.end] !== '.')
|
|
225
|
+
return undefined;
|
|
226
|
+
const field = identAt(s, head.end + 1);
|
|
227
|
+
if (!field || !isSpace(s[field.end]))
|
|
228
|
+
return undefined;
|
|
229
|
+
const from = skipSpaces(s, field.end);
|
|
230
|
+
if (s.slice(from, from + 4) !== 'from' || !isSpace(s[from + 4]))
|
|
231
|
+
return undefined;
|
|
232
|
+
const node = identAt(s, skipSpaces(s, from + 4));
|
|
233
|
+
if (!node)
|
|
234
|
+
return undefined;
|
|
235
|
+
const close = skipSpaces(s, node.end);
|
|
236
|
+
if (s[close] !== '>')
|
|
237
|
+
return undefined;
|
|
238
|
+
return { raw: s.slice(open, close + 1), index: open, field: field.name, node: node.name };
|
|
239
|
+
}
|
|
240
|
+
/** ADK's resolveSourceNode: the field of the named node's object output, else the placeholder as written. */
|
|
241
|
+
function resolveSourceNode(raw, field, node, scope) {
|
|
242
|
+
const out = scope.outputsByNode[node];
|
|
243
|
+
if (out && typeof out === 'object' && field in out)
|
|
244
|
+
return formatValue(out[field]);
|
|
245
|
+
return raw;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* ADK's collectPredecessorOutputs: the output every event of the invocation
|
|
249
|
+
* carries, keyed by the last segment of its node path without the run
|
|
250
|
+
* suffix (`Graph.Fan.Agent@2` is `Agent`), the later event winning.
|
|
251
|
+
*/
|
|
252
|
+
export function predecessorOutputs(events, invocationId) {
|
|
253
|
+
const outputs = {};
|
|
254
|
+
for (const event of events) {
|
|
255
|
+
if (event.invocationId !== invocationId || event.output === undefined)
|
|
256
|
+
continue;
|
|
257
|
+
const path = event.nodeInfo?.path;
|
|
258
|
+
if (!path)
|
|
259
|
+
continue;
|
|
260
|
+
const leaf = path.slice(path.lastIndexOf('.') + 1);
|
|
261
|
+
const at = leaf.indexOf('@');
|
|
262
|
+
outputs[at >= 0 ? leaf.slice(0, at) : leaf] = event.output;
|
|
263
|
+
}
|
|
264
|
+
return outputs;
|
|
265
|
+
}
|
|
266
|
+
function stripBraces(raw) {
|
|
267
|
+
let start = 0;
|
|
268
|
+
let end = raw.length;
|
|
269
|
+
while (start < end && raw[start] === '{')
|
|
270
|
+
start++;
|
|
271
|
+
while (end > start && raw[end - 1] === '}')
|
|
272
|
+
end--;
|
|
273
|
+
return raw.slice(start, end).trim();
|
|
274
|
+
}
|
|
275
|
+
function formatValue(value) {
|
|
276
|
+
if (typeof value === 'string')
|
|
277
|
+
return value;
|
|
278
|
+
if (typeof value === 'object' && value !== null) {
|
|
279
|
+
try {
|
|
280
|
+
const json = JSON.stringify(value);
|
|
281
|
+
if (json !== undefined)
|
|
282
|
+
return json;
|
|
283
|
+
}
|
|
284
|
+
catch (e) {
|
|
285
|
+
throw new Error(`Failed to serialize value for instruction template: ${e instanceof Error ? e.message : String(e)}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return String(value);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* `template` with each `{key}` naming a state key replaced by the value in
|
|
292
|
+
* `state` (a string as it is, anything else as JSON). `{key?}` is optional
|
|
293
|
+
* and becomes empty when the key is absent; a required key that is absent
|
|
294
|
+
* fails, as on the ADK runtime. A placeholder that names no state key
|
|
295
|
+
* (`{ "a": 1 }` in an example) stays as it is. `{artifact.x}` fails: no
|
|
296
|
+
* runtime of this engine has an artifact service. Own keys only: a key such
|
|
297
|
+
* as `constructor` is absent unless the state holds it.
|
|
298
|
+
*
|
|
299
|
+
* With a workflow `scope` (a workflow agent node's run), ADK 2.2's two
|
|
300
|
+
* workflow placeholders are filled too, as its injectSessionState fills
|
|
301
|
+
* them: `{x.field}` (any identifier before the dot) from the node's input
|
|
302
|
+
* when it is an object holding `field`, `''` when optional and absent, else
|
|
303
|
+
* left as written (the key's first spelling); and `<x.field from Node>`
|
|
304
|
+
* from that node's stored object output, else left as written.
|
|
305
|
+
*/
|
|
306
|
+
export function injectSessionState(template, state, scope) {
|
|
307
|
+
const sources = scope ? sourcePlaceholders(template).map((m) => ({ raw: m.raw, index: m.index, text: resolveSourceNode(m.raw, m.field, m.node, scope) })) : [];
|
|
308
|
+
const matches = placeholders(template);
|
|
309
|
+
if (matches.length === 0 && sources.length === 0)
|
|
310
|
+
return template;
|
|
311
|
+
const parsed = matches.map((m) => {
|
|
312
|
+
let key = stripBraces(m.raw);
|
|
313
|
+
const optional = key.endsWith('?');
|
|
314
|
+
if (optional)
|
|
315
|
+
key = key.slice(0, -1);
|
|
316
|
+
return { ...m, key, optional, valid: key.startsWith(ARTIFACT_PREFIX) || isStateName(key) || (!!scope && isWorkflowFieldKey(key)) };
|
|
317
|
+
});
|
|
318
|
+
const unique = new Map();
|
|
319
|
+
for (const p of parsed) {
|
|
320
|
+
if (!p.valid)
|
|
321
|
+
continue;
|
|
322
|
+
const seen = unique.get(p.key);
|
|
323
|
+
if (seen)
|
|
324
|
+
seen.required ||= !p.optional;
|
|
325
|
+
else
|
|
326
|
+
unique.set(p.key, { required: !p.optional, raw: p.raw });
|
|
327
|
+
}
|
|
328
|
+
const values = new Map();
|
|
329
|
+
for (const [key, { required, raw }] of unique)
|
|
330
|
+
values.set(key, resolveKey(key, required, raw, state, scope));
|
|
331
|
+
// ADK's merge: both kinds in template order, each replaced in turn.
|
|
332
|
+
const all = [...parsed.map((p) => ({ raw: p.raw, index: p.index, text: p.valid ? values.get(p.key) : p.raw })), ...sources].sort((a, b) => a.index - b.index);
|
|
333
|
+
let out = '';
|
|
334
|
+
let last = 0;
|
|
335
|
+
for (const p of all) {
|
|
336
|
+
out += template.slice(last, p.index) + p.text;
|
|
337
|
+
last = p.index + p.raw.length;
|
|
338
|
+
}
|
|
339
|
+
return out + template.slice(last);
|
|
340
|
+
}
|
|
341
|
+
/** ADK's resolveKey for one placeholder key: an artifact (refused), a state key, or a workflow field. */
|
|
342
|
+
function resolveKey(key, required, raw, state, scope) {
|
|
343
|
+
if (key.startsWith(ARTIFACT_PREFIX))
|
|
344
|
+
throw new Error('Artifact service is not initialized.');
|
|
345
|
+
if (isStateName(key)) {
|
|
346
|
+
if (Object.hasOwn(state, key))
|
|
347
|
+
return formatValue(state[key]);
|
|
348
|
+
if (!required)
|
|
349
|
+
return '';
|
|
350
|
+
throw new Error(`Context variable not found: \`${key}\`.`);
|
|
351
|
+
}
|
|
352
|
+
if (scope && isWorkflowFieldKey(key)) {
|
|
353
|
+
const field = key.slice(key.indexOf('.') + 1);
|
|
354
|
+
const input = scope.input;
|
|
355
|
+
if (input && typeof input === 'object' && field in input)
|
|
356
|
+
return formatValue(input[field]);
|
|
357
|
+
if (!required)
|
|
358
|
+
return '';
|
|
359
|
+
}
|
|
360
|
+
return raw;
|
|
361
|
+
}
|
|
362
|
+
function isObject(value) {
|
|
363
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
364
|
+
}
|
|
365
|
+
/** ADK's appendInstructions: the texts joined by a blank line, after a blank line when there is text already. */
|
|
366
|
+
function appendInstructions(system, texts) {
|
|
367
|
+
const added = texts.join('\n\n');
|
|
368
|
+
return system ? `${system}\n\n${added}` : added;
|
|
369
|
+
}
|
|
370
|
+
async function resolveInstruction(instruction, ctx, scope) {
|
|
371
|
+
if (typeof instruction === 'function')
|
|
372
|
+
return instruction(ctx);
|
|
373
|
+
return injectSessionState(instruction ?? '', ctx.state, scope);
|
|
374
|
+
}
|
|
375
|
+
/** A toolset (the skills harness, an MCP toolset): something that yields tools, and is not one. */
|
|
376
|
+
export function isToolset(value) {
|
|
377
|
+
if (!isObject(value))
|
|
378
|
+
return false;
|
|
379
|
+
if (typeof value.getTools !== 'function')
|
|
380
|
+
return false;
|
|
381
|
+
return !('runAsync' in value) && !isTool(value) && typeof value._getDeclaration !== 'function';
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* `configTools`: how many entries Gemini's own tool list holds so far (its
|
|
385
|
+
* native tools, and one function-declarations entry per declared tool),
|
|
386
|
+
* which Gemini 1's search refuses to share.
|
|
387
|
+
*/
|
|
388
|
+
function placementOf(native, model, configTools) {
|
|
389
|
+
const provider = providerForModel(model);
|
|
390
|
+
switch (native) {
|
|
391
|
+
case 'web_search':
|
|
392
|
+
return provider === 'gemini' ? { in: 'config', as: 'web_search' } : { in: 'dict' };
|
|
393
|
+
case 'x_search':
|
|
394
|
+
case 'collections_search':
|
|
395
|
+
return provider === 'xai' ? { in: 'dict' } : { in: 'none' };
|
|
396
|
+
case 'url_context':
|
|
397
|
+
return provider === 'gemini' ? { in: 'config', as: 'url_context' } : { in: 'none' };
|
|
398
|
+
case 'google_search':
|
|
399
|
+
// ADK's own GoogleSearchTool: Gemini only, and alone on Gemini 1.
|
|
400
|
+
if (isGemini1Model(model)) {
|
|
401
|
+
if (configTools > 0)
|
|
402
|
+
throw new Error('Google search tool can not be used with other tools in Gemini 1.x.');
|
|
403
|
+
return { in: 'config', as: 'web_search' };
|
|
404
|
+
}
|
|
405
|
+
if (isGeminiModel(model))
|
|
406
|
+
return { in: 'config', as: 'web_search' };
|
|
407
|
+
throw new Error(`Google search tool is not supported for model ${model}`);
|
|
408
|
+
case 'code_execution':
|
|
409
|
+
// The agent's code_execution: gemini, never a listed tool.
|
|
410
|
+
return { in: 'none' };
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* The ModelRequest the native loop sends for `agent` now, with the
|
|
415
|
+
* client-side tools it declares. Throws where the ADK runtime throws
|
|
416
|
+
* building the same request: a required state key that is absent, a
|
|
417
|
+
* Gemini-only tool on another model, a config field LlmAgent refuses.
|
|
418
|
+
*/
|
|
419
|
+
export async function buildModelRequest(agent, ctx) {
|
|
420
|
+
const model = agent.model;
|
|
421
|
+
const cfg = { ...(agent.generateContentConfig ?? {}) };
|
|
422
|
+
// LlmAgent's own refusals, so a config that cannot run under ADK cannot run here.
|
|
423
|
+
if (cfg.tools)
|
|
424
|
+
throw new Error('All tools must be set via LlmAgent.tools.');
|
|
425
|
+
if (cfg.systemInstruction)
|
|
426
|
+
throw new Error('System instruction must be set via LlmAgent.instruction.');
|
|
427
|
+
if (cfg.responseSchema)
|
|
428
|
+
throw new Error('Response schema must be set via LlmAgent.output_schema.');
|
|
429
|
+
const listed = agent.tools ?? [];
|
|
430
|
+
const taskMode = agent.mode === 'task';
|
|
431
|
+
const schemaWithTools = !!agent.outputSchema && listed.length > 0 && !outputSchemaWithTools(model);
|
|
432
|
+
// 1. Basic: the config, and the output schema where the model takes it (never in task mode: finish_task carries it).
|
|
433
|
+
if (agent.outputSchema && !schemaWithTools && !taskMode) {
|
|
434
|
+
cfg.responseSchema = agent.outputSchema;
|
|
435
|
+
cfg.responseMimeType = 'application/json';
|
|
436
|
+
}
|
|
437
|
+
const state = withStateOverlay(ctx.session.state, ctx.stateOverlay);
|
|
438
|
+
const instructionCtx = {
|
|
439
|
+
agentName: agent.name,
|
|
440
|
+
invocationId: ctx.invocationId,
|
|
441
|
+
state,
|
|
442
|
+
...(ctx.userContent ? { userContent: ctx.userContent } : {}),
|
|
443
|
+
};
|
|
444
|
+
// 2. Identity, then the root's global instruction, then the agent's own.
|
|
445
|
+
let system;
|
|
446
|
+
const transferDisabled = !!agent.outputSchema || (agent.disallowTransferToParent === true && agent.disallowTransferToPeers === true);
|
|
447
|
+
if (!transferDisabled) {
|
|
448
|
+
const identity = [`You are an agent. Your internal name is "${agent.name}".`];
|
|
449
|
+
if (agent.description)
|
|
450
|
+
identity.push(`The description about you is "${agent.description}"`);
|
|
451
|
+
system = appendInstructions(system, identity);
|
|
452
|
+
}
|
|
453
|
+
const globalInstruction = (ctx.root ?? agent).globalInstruction;
|
|
454
|
+
if (globalInstruction)
|
|
455
|
+
system = appendInstructions(system, [await resolveInstruction(globalInstruction, instructionCtx, ctx.workflowScope)]);
|
|
456
|
+
if (agent.instruction)
|
|
457
|
+
system = appendInstructions(system, [await resolveInstruction(agent.instruction, instructionCtx, ctx.workflowScope)]);
|
|
458
|
+
if (schemaWithTools)
|
|
459
|
+
system = appendInstructions(system, [SET_MODEL_RESPONSE_INSTRUCTION]);
|
|
460
|
+
// 3. The history, and Gemini code execution's parts in it.
|
|
461
|
+
const contents = projectHistory(ctx.session.events, {
|
|
462
|
+
agentName: agent.name,
|
|
463
|
+
includeContents: agent.includeContents ?? 'default',
|
|
464
|
+
branch: ctx.branch,
|
|
465
|
+
isolationScope: ctx.isolationScope,
|
|
466
|
+
});
|
|
467
|
+
const configNatives = [];
|
|
468
|
+
if (agent.codeExecution === 'gemini') {
|
|
469
|
+
if (!isGemini2OrAbove(model))
|
|
470
|
+
throw new Error(`Gemini code execution tool is not supported for model ${model}`);
|
|
471
|
+
configNatives.push('code_execution');
|
|
472
|
+
for (const content of contents)
|
|
473
|
+
convertCodeExecutionParts(content);
|
|
474
|
+
}
|
|
475
|
+
// 4. The tools, in the agent's order.
|
|
476
|
+
const toolContext = createToolContext({
|
|
477
|
+
invocationId: ctx.invocationId,
|
|
478
|
+
agentName: agent.name,
|
|
479
|
+
userId: ctx.session.userId,
|
|
480
|
+
appName: ctx.session.appName,
|
|
481
|
+
sessionId: ctx.session.id,
|
|
482
|
+
state,
|
|
483
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
484
|
+
...(ctx.userContent ? { userContent: ctx.userContent } : {}),
|
|
485
|
+
...(ctx.memory ? { memory: ctx.memory } : {}),
|
|
486
|
+
});
|
|
487
|
+
// What a toolset reads to list its tools (ADK's ReadonlyContext, by shape).
|
|
488
|
+
const toolsetContext = {
|
|
489
|
+
agentName: agent.name,
|
|
490
|
+
invocationId: ctx.invocationId,
|
|
491
|
+
state: { get: (key) => state[key], has: (key) => Object.hasOwn(state, key) },
|
|
492
|
+
};
|
|
493
|
+
const all = [...listed];
|
|
494
|
+
// Task mode (lib/runtime/native/taskMode.ts): finish_task in set_model_response's place. ADK's
|
|
495
|
+
// instruction processor still writes the set_model_response line above; LlmAgent adds no such tool.
|
|
496
|
+
if (taskMode)
|
|
497
|
+
all.push(finishTaskTool(agent.outputSchema));
|
|
498
|
+
else if (schemaWithTools)
|
|
499
|
+
all.push(setModelResponseTool(agent.outputSchema));
|
|
500
|
+
if (ctx.extraTools)
|
|
501
|
+
all.push(...ctx.extraTools);
|
|
502
|
+
// One entry per name, in first-listed order, the later object winning (ADK's toolsDict).
|
|
503
|
+
const dict = new Map();
|
|
504
|
+
const instructionTexts = [];
|
|
505
|
+
const contentWriters = [];
|
|
506
|
+
for (const union of all) {
|
|
507
|
+
const expanded = isToolset(union) ? await union.getTools(toolsetContext) : [union];
|
|
508
|
+
for (const listedTool of expanded) {
|
|
509
|
+
const native = nativeToolOf(listedTool);
|
|
510
|
+
if (native) {
|
|
511
|
+
const declaredSoFar = [...dict.values()].filter((e) => e.declaration).length;
|
|
512
|
+
const placement = placementOf(native, model, configNatives.length + declaredSoFar);
|
|
513
|
+
if (placement.in === 'config')
|
|
514
|
+
configNatives.push(placement.as);
|
|
515
|
+
else if (placement.in === 'dict')
|
|
516
|
+
dict.set(listedTool.name, { tool: listedTool, native });
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const writer = instructionToolOf(listedTool);
|
|
520
|
+
if (writer) {
|
|
521
|
+
instructionTexts.push(() => writer.instruction(toolContext));
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const own = toolOf(listedTool);
|
|
525
|
+
const declaration = contractToolDeclaration(own ?? listedTool);
|
|
526
|
+
if (declaration) {
|
|
527
|
+
const key = typeof listedTool.name === 'string' ? listedTool.name : declaration.name;
|
|
528
|
+
dict.set(key, { tool: own ?? listedTool, declaration });
|
|
529
|
+
}
|
|
530
|
+
if (own?.instruction) {
|
|
531
|
+
const write = own.instruction.bind(own);
|
|
532
|
+
instructionTexts.push(() => write(toolContext));
|
|
533
|
+
}
|
|
534
|
+
if (own?.contents) {
|
|
535
|
+
const add = own.contents.bind(own);
|
|
536
|
+
contentWriters.push(() => add(contents, toolContext));
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// ADK runs each tool's processLlmRequest in turn; its instruction lands in that order.
|
|
541
|
+
for (const text of instructionTexts) {
|
|
542
|
+
const written = await text();
|
|
543
|
+
if (written)
|
|
544
|
+
system = appendInstructions(system, [written]);
|
|
545
|
+
}
|
|
546
|
+
// ...and what it adds to the history (load_skill_resource's binary file, ADR 0083), after the history.
|
|
547
|
+
for (const write of contentWriters)
|
|
548
|
+
await write();
|
|
549
|
+
// 5. Everything the config says, read as the shim's mapping reads it.
|
|
550
|
+
const { toolChoice, strict } = toolChoiceOf(cfg);
|
|
551
|
+
const tools = [];
|
|
552
|
+
const declared = new Map();
|
|
553
|
+
const nativeTools = new Set();
|
|
554
|
+
for (const [name, entry] of dict) {
|
|
555
|
+
if (entry.native)
|
|
556
|
+
nativeTools.add(entry.native);
|
|
557
|
+
if (entry.declaration) {
|
|
558
|
+
tools.push(strict ? { ...entry.declaration, strict } : entry.declaration);
|
|
559
|
+
declared.set(name, entry.tool);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
for (const native of configNatives)
|
|
563
|
+
nativeTools.add(native);
|
|
564
|
+
const schema = cfg.responseJsonSchema ?? cfg.responseSchema;
|
|
565
|
+
const jsonMode = !isObject(schema) && cfg.responseMimeType === 'application/json';
|
|
566
|
+
const reasoning = reasoningOf(cfg);
|
|
567
|
+
const sampling = samplingOf(cfg);
|
|
568
|
+
const systemPrompt = systemText(system);
|
|
569
|
+
const messages = contentsToMessages(contents).messages;
|
|
570
|
+
const request = {
|
|
571
|
+
model,
|
|
572
|
+
...(systemPrompt !== undefined ? { system: systemPrompt } : {}),
|
|
573
|
+
messages,
|
|
574
|
+
...(tools.length > 0 ? { tools } : {}),
|
|
575
|
+
...(nativeTools.size > 0 ? { nativeTools: [...nativeTools] } : {}),
|
|
576
|
+
...(toolChoice !== undefined ? { toolChoice } : {}),
|
|
577
|
+
...(isObject(schema) ? { outputSchema: toContractJsonSchema(schema) } : {}),
|
|
578
|
+
...(jsonMode ? { outputFormat: 'json' } : {}),
|
|
579
|
+
...(reasoning !== undefined ? { reasoning } : {}),
|
|
580
|
+
...(sampling ? { sampling } : {}),
|
|
581
|
+
stream: ctx.stream ?? false,
|
|
582
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
583
|
+
};
|
|
584
|
+
return { request, tools: declared };
|
|
585
|
+
}
|