melchizedek-agents 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/.env.example +12 -0
  2. package/CHANGELOG.md +825 -0
  3. package/DOCUMENTATION.md +217 -59
  4. package/README.md +42 -1
  5. package/config/agents/examples/harness.yaml +3 -1
  6. package/config/agents/examples/model_zoo.yaml +10 -9
  7. package/config/agents/syndicateSchema.yaml +50 -16
  8. package/config/agents/templates/account_memory.yaml +2 -6
  9. package/config/agents/templates/case_desk.yaml +3 -9
  10. package/config/agents/templates/draft_review.yaml +3 -9
  11. package/config/agents/templates/intake_extractor.yaml +1 -3
  12. package/config/agents/templates/research_brief.yaml +3 -9
  13. package/config/agents/templates/research_desk.yaml +3 -9
  14. package/config/agents/templates/review_panel.yaml +4 -12
  15. package/config/agents/templates/support_triage.yaml +4 -12
  16. package/config/agents/templates/systems_operator.yaml +2 -6
  17. package/db/hardening.sql +7 -1
  18. package/db/migrations/0013_tool_credentials.sql +260 -0
  19. package/dist/lib/a2a/app.d.ts +70 -6
  20. package/dist/lib/a2a/app.js +107 -2
  21. package/dist/lib/a2a/executor.d.ts +20 -3
  22. package/dist/lib/a2a/executor.js +66 -7
  23. package/dist/lib/a2a/remoteAgent.d.ts +24 -9
  24. package/dist/lib/a2a/remoteAgent.js +31 -14
  25. package/dist/lib/compile.d.ts +121 -30
  26. package/dist/lib/compile.js +137 -124
  27. package/dist/lib/compileAdk.d.ts +29 -0
  28. package/dist/lib/compileAdk.js +132 -0
  29. package/dist/lib/compileNative.d.ts +85 -0
  30. package/dist/lib/compileNative.js +222 -0
  31. package/dist/lib/config.d.ts +6 -5
  32. package/dist/lib/config.js +6 -5
  33. package/dist/lib/doctor.js +1 -0
  34. package/dist/lib/index.d.ts +2 -1
  35. package/dist/lib/index.js +2 -1
  36. package/dist/lib/loadSyndicate.d.ts +18 -0
  37. package/dist/lib/memory/erase.d.ts +5 -3
  38. package/dist/lib/memory/erase.js +5 -2
  39. package/dist/lib/memory/namespace.d.ts +8 -1
  40. package/dist/lib/memory/namespace.js +12 -6
  41. package/dist/lib/memory/providers.js +2 -1
  42. package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
  43. package/dist/lib/memory/supabaseMemoryService.js +42 -2
  44. package/dist/lib/model.d.ts +47 -0
  45. package/dist/lib/model.js +37 -0
  46. package/dist/lib/models/adapterResolver.d.ts +113 -0
  47. package/dist/lib/models/adapterResolver.js +136 -0
  48. package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
  49. package/dist/lib/models/adkGeminiAdapter.js +367 -0
  50. package/dist/lib/models/adkShim.d.ts +111 -0
  51. package/dist/lib/models/adkShim.js +136 -0
  52. package/dist/lib/models/capabilities.d.ts +3 -0
  53. package/dist/lib/models/capabilities.js +20 -13
  54. package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
  55. package/dist/lib/models/chatCompletionsAdapter.js +854 -0
  56. package/dist/lib/models/circuitBreaker.d.ts +42 -0
  57. package/dist/lib/models/circuitBreaker.js +70 -0
  58. package/dist/lib/models/claudeAdapter.d.ts +103 -0
  59. package/dist/lib/models/claudeAdapter.js +669 -0
  60. package/dist/lib/models/claudeLlm.d.ts +39 -62
  61. package/dist/lib/models/claudeLlm.js +44 -349
  62. package/dist/lib/models/claudeModels.d.ts +127 -0
  63. package/dist/lib/models/claudeModels.js +237 -0
  64. package/dist/lib/models/contract.d.ts +395 -0
  65. package/dist/lib/models/contract.js +39 -0
  66. package/dist/lib/models/endpoints.js +2 -1
  67. package/dist/lib/models/errorResponse.d.ts +50 -0
  68. package/dist/lib/models/errorResponse.js +48 -0
  69. package/dist/lib/models/fallback.d.ts +17 -10
  70. package/dist/lib/models/fallback.js +53 -42
  71. package/dist/lib/models/fallbackAdapter.d.ts +55 -0
  72. package/dist/lib/models/fallbackAdapter.js +62 -0
  73. package/dist/lib/models/gateway.js +2 -1
  74. package/dist/lib/models/gatewayAdapter.d.ts +51 -0
  75. package/dist/lib/models/gatewayAdapter.js +81 -0
  76. package/dist/lib/models/gatewayLlm.d.ts +12 -33
  77. package/dist/lib/models/gatewayLlm.js +14 -75
  78. package/dist/lib/models/geminiAdapter.d.ts +123 -0
  79. package/dist/lib/models/geminiAdapter.js +831 -0
  80. package/dist/lib/models/geminiState.d.ts +25 -0
  81. package/dist/lib/models/geminiState.js +25 -0
  82. package/dist/lib/models/genaiMapping.d.ts +263 -0
  83. package/dist/lib/models/genaiMapping.js +862 -0
  84. package/dist/lib/models/gptAdapter.d.ts +240 -0
  85. package/dist/lib/models/gptAdapter.js +804 -0
  86. package/dist/lib/models/gptLlm.d.ts +63 -123
  87. package/dist/lib/models/gptLlm.js +88 -602
  88. package/dist/lib/models/grokAdapter.d.ts +86 -0
  89. package/dist/lib/models/grokAdapter.js +142 -0
  90. package/dist/lib/models/grokLlm.d.ts +22 -45
  91. package/dist/lib/models/grokLlm.js +22 -67
  92. package/dist/lib/models/kimiAdapter.d.ts +157 -0
  93. package/dist/lib/models/kimiAdapter.js +203 -0
  94. package/dist/lib/models/kimiLlm.d.ts +15 -107
  95. package/dist/lib/models/kimiLlm.js +17 -153
  96. package/dist/lib/models/ollamaAdapter.d.ts +125 -0
  97. package/dist/lib/models/ollamaAdapter.js +155 -0
  98. package/dist/lib/models/ollamaLlm.d.ts +11 -107
  99. package/dist/lib/models/ollamaLlm.js +13 -151
  100. package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
  101. package/dist/lib/models/openAiCompatibleLlm.js +109 -671
  102. package/dist/lib/models/providerState.d.ts +63 -0
  103. package/dist/lib/models/providerState.js +61 -0
  104. package/dist/lib/models/reasoning.d.ts +26 -0
  105. package/dist/lib/models/reasoning.js +59 -0
  106. package/dist/lib/models/registry.d.ts +58 -35
  107. package/dist/lib/models/registry.js +89 -118
  108. package/dist/lib/models/retry.d.ts +2 -1
  109. package/dist/lib/models/retry.js +3 -2
  110. package/dist/lib/models/schemaNormalize.d.ts +98 -0
  111. package/dist/lib/models/schemaNormalize.js +321 -1
  112. package/dist/lib/models/tracedGemini.d.ts +41 -0
  113. package/dist/lib/models/tracedGemini.js +98 -0
  114. package/dist/lib/models/urls.d.ts +11 -0
  115. package/dist/lib/models/urls.js +16 -0
  116. package/dist/lib/net/redirects.d.ts +21 -2
  117. package/dist/lib/net/redirects.js +11 -34
  118. package/dist/lib/observability/audit.d.ts +5 -1
  119. package/dist/lib/observability/lineage.d.ts +14 -0
  120. package/dist/lib/observability/lineage.js +33 -0
  121. package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
  122. package/dist/lib/observability/supabaseSpanExporter.js +17 -9
  123. package/dist/lib/observability/tracer.d.ts +26 -8
  124. package/dist/lib/observability/tracer.js +43 -18
  125. package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
  126. package/dist/lib/runtime/adkMemoryBridge.js +68 -0
  127. package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
  128. package/dist/lib/runtime/adkSessionBridge.js +176 -0
  129. package/dist/lib/runtime/credentials.d.ts +59 -0
  130. package/dist/lib/runtime/credentials.js +62 -0
  131. package/dist/lib/runtime/events.d.ts +229 -0
  132. package/dist/lib/runtime/events.js +274 -0
  133. package/dist/lib/runtime/logging.d.ts +32 -0
  134. package/dist/lib/runtime/logging.js +55 -0
  135. package/dist/lib/runtime/memoryService.d.ts +72 -0
  136. package/dist/lib/runtime/memoryService.js +25 -0
  137. package/dist/lib/runtime/native/agentLoop.d.ts +203 -0
  138. package/dist/lib/runtime/native/agentLoop.js +687 -0
  139. package/dist/lib/runtime/native/compaction.d.ts +103 -0
  140. package/dist/lib/runtime/native/compaction.js +191 -0
  141. package/dist/lib/runtime/native/delegate.d.ts +98 -0
  142. package/dist/lib/runtime/native/delegate.js +173 -0
  143. package/dist/lib/runtime/native/history.d.ts +83 -0
  144. package/dist/lib/runtime/native/history.js +312 -0
  145. package/dist/lib/runtime/native/interrupts.d.ts +126 -0
  146. package/dist/lib/runtime/native/interrupts.js +291 -0
  147. package/dist/lib/runtime/native/request.d.ts +215 -0
  148. package/dist/lib/runtime/native/request.js +585 -0
  149. package/dist/lib/runtime/native/selfCorrection.d.ts +106 -0
  150. package/dist/lib/runtime/native/selfCorrection.js +253 -0
  151. package/dist/lib/runtime/native/step.d.ts +148 -0
  152. package/dist/lib/runtime/native/step.js +239 -0
  153. package/dist/lib/runtime/native/taskMode.d.ts +64 -0
  154. package/dist/lib/runtime/native/taskMode.js +126 -0
  155. package/dist/lib/runtime/native/telemetry.d.ts +75 -0
  156. package/dist/lib/runtime/native/telemetry.js +299 -0
  157. package/dist/lib/runtime/native/tempState.d.ts +31 -0
  158. package/dist/lib/runtime/native/tempState.js +56 -0
  159. package/dist/lib/runtime/nativeTurn.d.ts +86 -0
  160. package/dist/lib/runtime/nativeTurn.js +107 -0
  161. package/dist/lib/runtime/questions.d.ts +29 -7
  162. package/dist/lib/runtime/questions.js +47 -32
  163. package/dist/lib/runtime/runtimeFlag.d.ts +30 -0
  164. package/dist/lib/runtime/runtimeFlag.js +50 -0
  165. package/dist/lib/runtime/sessions.d.ts +166 -0
  166. package/dist/lib/runtime/sessions.js +201 -0
  167. package/dist/lib/runtime/syndicateTurn.d.ts +41 -5
  168. package/dist/lib/runtime/syndicateTurn.js +202 -32
  169. package/dist/lib/session/supabaseSessionService.d.ts +64 -17
  170. package/dist/lib/session/supabaseSessionService.js +148 -105
  171. package/dist/lib/session/transcript.d.ts +68 -22
  172. package/dist/lib/session/transcript.js +102 -28
  173. package/dist/lib/skills.js +33 -7
  174. package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
  175. package/dist/lib/storage/postgres/credentialStore.js +82 -0
  176. package/dist/lib/storage/postgres/index.d.ts +12 -0
  177. package/dist/lib/storage/postgres/index.js +6 -1
  178. package/dist/lib/storage/postgres/sessionService.d.ts +43 -15
  179. package/dist/lib/storage/postgres/sessionService.js +128 -66
  180. package/dist/lib/syndicateSchema.d.ts +47 -0
  181. package/dist/lib/syndicateSchema.js +72 -3
  182. package/dist/lib/toolRegistry.d.ts +15 -2
  183. package/dist/lib/toolRegistry.js +33 -17
  184. package/dist/lib/tools/adkTool.d.ts +81 -0
  185. package/dist/lib/tools/adkTool.js +268 -0
  186. package/dist/lib/tools/auth.d.ts +118 -0
  187. package/dist/lib/tools/auth.js +89 -0
  188. package/dist/lib/tools/collectionsSearchTool.d.ts +7 -6
  189. package/dist/lib/tools/collectionsSearchTool.js +11 -26
  190. package/dist/lib/tools/credentialCipher.d.ts +58 -0
  191. package/dist/lib/tools/credentialCipher.js +116 -0
  192. package/dist/lib/tools/credentialStore.d.ts +77 -0
  193. package/dist/lib/tools/credentialStore.js +246 -0
  194. package/dist/lib/tools/examples.d.ts +43 -0
  195. package/dist/lib/tools/examples.js +62 -0
  196. package/dist/lib/tools/generateImageTool.d.ts +15 -6
  197. package/dist/lib/tools/generateImageTool.js +19 -29
  198. package/dist/lib/tools/inspectImageTool.d.ts +9 -6
  199. package/dist/lib/tools/inspectImageTool.js +13 -17
  200. package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
  201. package/dist/lib/tools/mcpToolFactory.js +46 -47
  202. package/dist/lib/tools/memoryTools.d.ts +61 -0
  203. package/dist/lib/tools/memoryTools.js +119 -0
  204. package/dist/lib/tools/nativeTools.d.ts +38 -0
  205. package/dist/lib/tools/nativeTools.js +43 -0
  206. package/dist/lib/tools/oauthConsent.d.ts +181 -0
  207. package/dist/lib/tools/oauthConsent.js +330 -0
  208. package/dist/lib/tools/openapi/call.d.ts +108 -0
  209. package/dist/lib/tools/openapi/call.js +295 -0
  210. package/dist/lib/tools/openapi/parse.d.ts +147 -0
  211. package/dist/lib/tools/openapi/parse.js +570 -0
  212. package/dist/lib/tools/openapiTools.d.ts +25 -17
  213. package/dist/lib/tools/openapiTools.js +124 -121
  214. package/dist/lib/tools/scienceTools.d.ts +14 -14
  215. package/dist/lib/tools/skillToolset.d.ts +58 -87
  216. package/dist/lib/tools/skillToolset.js +61 -142
  217. package/dist/lib/tools/skills/env.d.ts +58 -0
  218. package/dist/lib/tools/skills/env.js +165 -0
  219. package/dist/lib/tools/skills/executor.d.ts +114 -0
  220. package/dist/lib/tools/skills/executor.js +331 -0
  221. package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
  222. package/dist/lib/tools/skills/frontmatter.js +215 -0
  223. package/dist/lib/tools/skills/loader.d.ts +91 -0
  224. package/dist/lib/tools/skills/loader.js +257 -0
  225. package/dist/lib/tools/skills/tools.d.ts +187 -0
  226. package/dist/lib/tools/skills/tools.js +386 -0
  227. package/dist/lib/tools/taskTools.d.ts +15 -15
  228. package/dist/lib/tools/tool.d.ts +354 -0
  229. package/dist/lib/tools/tool.js +304 -0
  230. package/dist/lib/tools/toolContract.d.ts +89 -33
  231. package/dist/lib/tools/toolContract.js +104 -82
  232. package/dist/lib/tools/urlContextTool.d.ts +3 -0
  233. package/dist/lib/tools/urlContextTool.js +5 -1
  234. package/dist/lib/tools/webExtractTool.d.ts +2 -2
  235. package/dist/lib/tools/webExtractTool.js +2 -1
  236. package/dist/lib/tools/webSearchTool.d.ts +6 -9
  237. package/dist/lib/tools/webSearchTool.js +14 -59
  238. package/dist/lib/tools/wikiTools.d.ts +19 -19
  239. package/dist/lib/tools/wikiTools.js +13 -9
  240. package/dist/lib/tools/xApiSearchTool.d.ts +2 -2
  241. package/dist/lib/tools/xApiSearchTool.js +2 -1
  242. package/dist/lib/tools/xSearchTool.d.ts +7 -9
  243. package/dist/lib/tools/xSearchTool.js +11 -62
  244. package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
  245. package/dist/lib/tools/xaiSearchParams.js +126 -0
  246. package/dist/lib/wiki/agentRun.d.ts +12 -0
  247. package/dist/lib/wiki/agentRun.js +72 -34
  248. package/dist/lib/wiki/entities.d.ts +12 -0
  249. package/dist/lib/wiki/entities.js +45 -3
  250. package/dist/lib/workflow/agentNode.d.ts +136 -0
  251. package/dist/lib/workflow/agentNode.js +274 -0
  252. package/dist/lib/workflow/graph.d.ts +177 -0
  253. package/dist/lib/workflow/graph.js +458 -0
  254. package/dist/lib/workflow/nodeEvents.d.ts +50 -0
  255. package/dist/lib/workflow/nodeEvents.js +101 -0
  256. package/dist/lib/workflow/pause.d.ts +122 -0
  257. package/dist/lib/workflow/pause.js +180 -0
  258. package/dist/lib/workflow/resume.d.ts +165 -0
  259. package/dist/lib/workflow/resume.js +440 -0
  260. package/dist/lib/workflow/route.d.ts +58 -0
  261. package/dist/lib/workflow/route.js +69 -0
  262. package/dist/lib/workflow/scheduler.d.ts +415 -0
  263. package/dist/lib/workflow/scheduler.js +743 -0
  264. package/dist/lib/workflow/toolNode.d.ts +127 -0
  265. package/dist/lib/workflow/toolNode.js +236 -0
  266. package/dist/lib/workflow/turn.d.ts +118 -0
  267. package/dist/lib/workflow/turn.js +216 -0
  268. package/dist/lib/workflowConfig.d.ts +3 -2
  269. package/dist/lib/workflowConfig.js +3 -12
  270. package/dist/scripts/a2a_server.js +4 -3
  271. package/dist/scripts/assistant_worker.d.ts +2 -1
  272. package/dist/scripts/assistant_worker.js +9 -4
  273. package/dist/scripts/syndicate_chat.js +19 -10
  274. package/package.json +4 -1
  275. package/skills/briefs/melchizedek-author.md +3 -2
  276. package/skills/briefs/melchizedek-models.md +13 -9
  277. package/skills/briefs/melchizedek.md +3 -3
  278. package/skills/melchizedek/SKILL.md +2 -1
  279. package/skills/melchizedek-author/SKILL.md +2 -1
  280. package/skills/melchizedek-author/assets/minimal.yaml +2 -0
  281. package/skills/melchizedek-models/SKILL.md +23 -6
@@ -12,7 +12,27 @@
12
12
  * Tools are declared once (YAML / MCP discovery) in the Gemini dialect, so
13
13
  * every non-Gemini adapter runs its tool schemas through this function at
14
14
  * request-build time. See DOCUMENTATION.md §7.1.
15
+ *
16
+ * The engine's own model contract (lib/models/contract.ts, ADR 0048) takes
17
+ * tools in its own shape instead: contractToolDeclaration() builds a
18
+ * ToolDeclaration from an own Tool (lib/tools/tool.ts), an ADK tool or a
19
+ * defineTool contract, converting Gemini's dialect once, where the tool
20
+ * enters, and nativeToolOf() names the server-side tools that declare
21
+ * nothing. The functions above stay as they are for the ADK path's adapters.
22
+ *
23
+ * zodInputJsonSchema() is the one place a zod schema becomes JSON Schema,
24
+ * for every surface: the declaration, the ADK FunctionTool and the MCP
25
+ * tools/list entry all derive from it.
26
+ */
27
+ import { z } from 'zod';
28
+ import type { JsonSchema, NativeTool, ToolDeclaration } from './contract.ts';
29
+ /**
30
+ * Standard JSON Schema for what a caller may send a zod schema: its input
31
+ * side (`io: 'input'`), so a field with a default is optional to the model,
32
+ * as it is to the schema. `$schema` is dropped: it is metadata about the
33
+ * document, noise on the wire.
15
34
  */
35
+ export declare function zodInputJsonSchema(schema: z.ZodType): Record<string, unknown>;
16
36
  /**
17
37
  * Deep-clones a Gemini/ADK-style JSON schema, lowercasing every `type` value
18
38
  * ('OBJECT' → 'object', ['STRING','NULL'] → ['string','null']) while leaving
@@ -49,3 +69,81 @@ export declare function toolDeclarationFor(tool: unknown): {
49
69
  * names the harness expects rather than improvised ones.
50
70
  */
51
71
  export declare function toStrictJsonSchema(schema: unknown): Record<string, unknown>;
72
+ /**
73
+ * A deep copy of `schema` with `visit` applied to every schema node of the
74
+ * copy, children first, following only the keywords that hold schemas (a
75
+ * property named `default` or `additionalProperties` is a property, not the
76
+ * keyword). Never mutates `schema`. Undefined for a schema that is not an
77
+ * object.
78
+ */
79
+ export declare function mapSchemaNodes(schema: unknown, visit: (node: Record<string, unknown>) => void): Record<string, unknown> | undefined;
80
+ /**
81
+ * A copy of `schema` as lowercase JSON Schema, the contract's one dialect
82
+ * (`JsonSchema`), from either dialect. With `strict`, every object node
83
+ * that has properties, at any depth (inside `items`, `anyOf`, `$defs`, …),
84
+ * lists all of them as required and sets `additionalProperties: false`: the
85
+ * form OpenAI's and Anthropic's strict modes demand. An object without
86
+ * properties (a map) is left open, so a strict provider refuses it instead
87
+ * of receiving a field the model can never fill. Never mutates `schema`.
88
+ */
89
+ export declare function toContractJsonSchema(schema: unknown, options?: {
90
+ strict?: boolean;
91
+ }): JsonSchema;
92
+ /**
93
+ * The parameters a zod schema declares, in the contract's dialect: its
94
+ * input side (zodInputJsonSchema) without the keywords the ADK path cannot
95
+ * carry (dropZodOnlyKeywords). defineTool's declaration() and
96
+ * contractToolDeclaration() both build from here, so a contract declares the
97
+ * same parameters on either runtime.
98
+ */
99
+ export declare function zodToolParameters(schema: z.ZodType, options?: {
100
+ strict?: boolean;
101
+ }): JsonSchema;
102
+ /**
103
+ * The declaration a model receives for one client-side tool, in the
104
+ * contract's shape (ToolDeclaration, lib/models/contract.ts).
105
+ *
106
+ * - An own Tool (lib/tools/tool.ts), defineTool's among them: its own
107
+ * `declaration()`.
108
+ * - A plain defineTool-shaped contract (name, description, zod schema,
109
+ * execute): built directly from its zod schema (zodToolParameters), never
110
+ * through Gemini's uppercase dialect. The input side is declared, so a
111
+ * field with a default is optional; the `default` keyword and a boolean
112
+ * `additionalProperties` are left out, as the ADK path leaves them out,
113
+ * and a record's value schema is kept, as the ADK path keeps it. The
114
+ * contract and the FunctionTool that toFunctionTool() makes of it declare
115
+ * the same parameters.
116
+ * - An ADK tool (FunctionTool, AgentTool, the memory tools, MCP tools): read
117
+ * from its own `_getDeclaration()` (ADR 0019), `parameters` or else
118
+ * `parametersJsonSchema`, and converted from Gemini's dialect once, here.
119
+ * - A plain object with `name` and `parameters` (tests, hand-built tools).
120
+ *
121
+ * With `strict`, the parameters take the strict form (toContractJsonSchema)
122
+ * and the declaration carries `strict: true`. Returns undefined for a tool
123
+ * that declares nothing (a server-side tool: see nativeToolOf, or an
124
+ * InstructionTool such as `preload_memory`, which only writes into the
125
+ * instruction, as itself or as its ADK tool) and for one with no name.
126
+ */
127
+ export declare function contractToolDeclaration(tool: unknown, options?: {
128
+ strict?: boolean;
129
+ }): ToolDeclaration | undefined;
130
+ /**
131
+ * The NativeTool a tool object stands for, or undefined for any other tool.
132
+ * Recognised by marker, never by class or by shape, and every marker lives
133
+ * in the global symbol registry, so a second copy of a module (or of ADK)
134
+ * still matches (ADR 0062):
135
+ * - the engine's own marker (lib/tools/tool.ts, NATIVE_TOOL): a
136
+ * NativeToolMarker (lib/tools/nativeTools.ts) and the ADK sentinel made
137
+ * from it (lib/tools/webSearchTool.ts and its siblings);
138
+ * - Gemini code execution: ADK's built-in code executor, by ADK's marker
139
+ * (the agent's `codeExecutor`, from `code_execution: gemini`);
140
+ * - ADK's own GOOGLE_SEARCH and URL_CONTEXT, by ADK's marker for a tool
141
+ * the model runs, and their name.
142
+ * A client-side tool registered under one of those names carries no marker,
143
+ * so it stays a client-side tool, as does a tool that merely declares
144
+ * nothing. ADK's other in-model tools (Vertex AI Search, enterprise web
145
+ * search, Maps grounding, RAG retrieval) have no NativeTool: they are
146
+ * undefined here and declare nothing, so a caller building a request
147
+ * reports them as dropped.
148
+ */
149
+ export declare function nativeToolOf(tool: unknown): NativeTool | undefined;
@@ -12,7 +12,38 @@
12
12
  * Tools are declared once (YAML / MCP discovery) in the Gemini dialect, so
13
13
  * every non-Gemini adapter runs its tool schemas through this function at
14
14
  * request-build time. See DOCUMENTATION.md §7.1.
15
+ *
16
+ * The engine's own model contract (lib/models/contract.ts, ADR 0048) takes
17
+ * tools in its own shape instead: contractToolDeclaration() builds a
18
+ * ToolDeclaration from an own Tool (lib/tools/tool.ts), an ADK tool or a
19
+ * defineTool contract, converting Gemini's dialect once, where the tool
20
+ * enters, and nativeToolOf() names the server-side tools that declare
21
+ * nothing. The functions above stay as they are for the ADK path's adapters.
22
+ *
23
+ * zodInputJsonSchema() is the one place a zod schema becomes JSON Schema,
24
+ * for every surface: the declaration, the ADK FunctionTool and the MCP
25
+ * tools/list entry all derive from it.
15
26
  */
27
+ import { z } from 'zod';
28
+ import { isInstructionTool, isTool, nativeToolMarkerOf } from '../tools/tool.js';
29
+ /**
30
+ * Standard JSON Schema for what a caller may send a zod schema: its input
31
+ * side (`io: 'input'`), so a field with a default is optional to the model,
32
+ * as it is to the schema. `$schema` is dropped: it is metadata about the
33
+ * document, noise on the wire.
34
+ */
35
+ export function zodInputJsonSchema(schema) {
36
+ const { $schema: _drop, ...json } = z.toJSONSchema(schema, { io: 'input' });
37
+ // A record's `propertyNames: { type: 'string' }` says only that its keys
38
+ // are strings, which JSON keys always are. Gemini refuses the keyword
39
+ // with a 400 (live, 2026-10-08), so it is left out here, the one source
40
+ // every path reads, and the paths keep declaring the same parameters.
41
+ return mapSchemaNodes(json, (node) => {
42
+ const names = node.propertyNames;
43
+ if (isPlainObject(names) && Object.keys(names).length === 1 && names.type === 'string')
44
+ delete node.propertyNames;
45
+ }) ?? json;
46
+ }
16
47
  /**
17
48
  * Deep-clones a Gemini/ADK-style JSON schema, lowercasing every `type` value
18
49
  * ('OBJECT' → 'object', ['STRING','NULL'] → ['string','null']) while leaving
@@ -72,7 +103,7 @@ function normalizeNode(node) {
72
103
  * search sentinels) and for tools with no name.
73
104
  */
74
105
  export function toolDeclarationFor(tool) {
75
- if (!tool || typeof tool !== 'object')
106
+ if (!tool || typeof tool !== 'object' || nativeToolMarkerOf(tool))
76
107
  return undefined;
77
108
  const t = tool;
78
109
  let decl;
@@ -133,3 +164,292 @@ function strictNode(node) {
133
164
  function isPlainObject(v) {
134
165
  return !!v && typeof v === 'object' && !Array.isArray(v);
135
166
  }
167
+ // ── Tool declarations in the contract's shape (ADR 0048) ─────────────────────
168
+ /** Keywords whose value is one schema (`items` may also be a draft-4 tuple of them). */
169
+ const SUBSCHEMA_KEYWORDS = new Set([
170
+ 'items', 'additionalItems', 'additionalProperties', 'unevaluatedItems', 'unevaluatedProperties',
171
+ 'contains', 'propertyNames', 'not', 'if', 'then', 'else',
172
+ ]);
173
+ /** Keywords whose value is a list of schemas. */
174
+ const SUBSCHEMA_LIST_KEYWORDS = new Set(['anyOf', 'oneOf', 'allOf', 'prefixItems']);
175
+ /** Keywords whose value maps names to schemas. */
176
+ const SUBSCHEMA_MAP_KEYWORDS = new Set(['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas']);
177
+ /** Bounds Gemini's Schema spells as int64 strings (`'1'`); JSON Schema wants integers. */
178
+ const INTEGER_KEYWORDS = ['minLength', 'maxLength', 'minItems', 'maxItems', 'minProperties', 'maxProperties'];
179
+ /**
180
+ * What a declaration built from a zod schema leaves out, as the ADK path
181
+ * (toGeminiSchema) leaves it out, so a contract declares the same
182
+ * parameters whichever path resolves it: the `default` keyword (zod applies
183
+ * defaults at parse time, and the field is already optional), and an
184
+ * `additionalProperties` that is only `true` or `false`. An
185
+ * `additionalProperties` holding a schema, a record's value schema, is kept.
186
+ */
187
+ function dropZodOnlyKeywords(node) {
188
+ delete node.default;
189
+ if (typeof node.additionalProperties === 'boolean')
190
+ delete node.additionalProperties;
191
+ }
192
+ /** ADK's built-ins that a model runs itself, by the NativeTool their name is. */
193
+ const ADK_IN_MODEL_NATIVE_TOOLS = ['google_search', 'url_context'];
194
+ /**
195
+ * ADK's markers, read through the global symbol registry so this module
196
+ * imports nothing from ADK: a tool the model runs itself (GOOGLE_SEARCH,
197
+ * ADK's URL_CONTEXT) and Gemini's built-in code executor.
198
+ */
199
+ const ADK_IN_MODEL_TOOL = Symbol.for('google.adk.inModelTool');
200
+ const ADK_BUILT_IN_CODE_EXECUTOR = Symbol.for('google.adk.builtInCodeExecutor');
201
+ /** A deep copy of a JSON value, so a walk can change it without touching a tool's own schema. */
202
+ function cloneJson(value) {
203
+ if (Array.isArray(value))
204
+ return value.map(cloneJson);
205
+ if (isPlainObject(value))
206
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, cloneJson(v)]));
207
+ return value;
208
+ }
209
+ /**
210
+ * Visits every schema node in place, children first. It follows only the
211
+ * keywords that hold schemas, so a property named `type`, `enum` or
212
+ * `default` is a property like any other, and data (`enum`, `const`,
213
+ * `default`, `examples`) is never read as a schema.
214
+ */
215
+ function walkSchema(node, visit) {
216
+ if (!isPlainObject(node))
217
+ return; // boolean schemas, malformed nodes
218
+ for (const [key, value] of Object.entries(node)) {
219
+ if (SUBSCHEMA_KEYWORDS.has(key)) {
220
+ if (Array.isArray(value))
221
+ value.forEach((v) => walkSchema(v, visit));
222
+ else
223
+ walkSchema(value, visit);
224
+ }
225
+ else if (SUBSCHEMA_LIST_KEYWORDS.has(key) && Array.isArray(value)) {
226
+ value.forEach((v) => walkSchema(v, visit));
227
+ }
228
+ else if (SUBSCHEMA_MAP_KEYWORDS.has(key) && isPlainObject(value)) {
229
+ Object.values(value).forEach((v) => walkSchema(v, visit));
230
+ }
231
+ }
232
+ visit(node);
233
+ }
234
+ /**
235
+ * A deep copy of `schema` with `visit` applied to every schema node of the
236
+ * copy, children first, following only the keywords that hold schemas (a
237
+ * property named `default` or `additionalProperties` is a property, not the
238
+ * keyword). Never mutates `schema`. Undefined for a schema that is not an
239
+ * object.
240
+ */
241
+ export function mapSchemaNodes(schema, visit) {
242
+ if (!isPlainObject(schema))
243
+ return undefined;
244
+ const root = cloneJson(schema);
245
+ walkSchema(root, visit);
246
+ return root;
247
+ }
248
+ /** Keywords that say what a node is about, not what it admits; they stay put when a nullable node is wrapped. */
249
+ const ANNOTATION_KEYWORDS = new Set(['title', 'description', 'default', 'examples', '$comment', 'deprecated', 'readOnly', 'writeOnly', '$defs', 'definitions']);
250
+ /** Keywords beside which a type that admits null still refuses it. */
251
+ const NULL_REFUSING_KEYWORDS = ['allOf', 'anyOf', 'oneOf', '$ref', 'not', 'const'];
252
+ /**
253
+ * One node from Gemini's dialect into lowercase JSON Schema: types
254
+ * lowercased, int64 bounds as integers, the strict form when asked, and
255
+ * OpenAPI's `nullable: true` turned into a schema that admits null. Strict
256
+ * comes before null, because admitting null may move the node's properties
257
+ * into an anyOf branch the walk has already passed. A node already in the
258
+ * standard dialect passes through unchanged.
259
+ */
260
+ function toContractNode(node, fromZod, strict) {
261
+ delete node.$schema;
262
+ if (fromZod)
263
+ dropZodOnlyKeywords(node);
264
+ if (typeof node.type === 'string')
265
+ node.type = node.type.toLowerCase();
266
+ else if (Array.isArray(node.type))
267
+ node.type = node.type.map((t) => (typeof t === 'string' ? t.toLowerCase() : t));
268
+ for (const key of INTEGER_KEYWORDS) {
269
+ const bound = node[key];
270
+ if (typeof bound === 'string' && /^\d+$/.test(bound))
271
+ node[key] = Number(bound);
272
+ }
273
+ if (strict)
274
+ toStrictNode(node);
275
+ if (typeof node.nullable !== 'boolean')
276
+ return;
277
+ const nullable = node.nullable;
278
+ delete node.nullable;
279
+ if (nullable)
280
+ admitNull(node);
281
+ }
282
+ /**
283
+ * Make `node` admit null. A plain typed node gains `null` in its type (and
284
+ * its enum, which would otherwise refuse it); a bare `anyOf` gains a null
285
+ * branch; anything else ($ref, allOf, oneOf, const…) moves into an anyOf
286
+ * beside `{ type: 'null' }`, its annotations staying on the node.
287
+ */
288
+ function admitNull(node) {
289
+ const composed = NULL_REFUSING_KEYWORDS.some((k) => k in node);
290
+ if (node.type !== undefined && !composed) {
291
+ const types = Array.isArray(node.type) ? node.type : [node.type];
292
+ if (!types.includes('null'))
293
+ node.type = typeof node.type === 'string' ? [node.type, 'null'] : [...types, 'null'];
294
+ if (Array.isArray(node.enum) && !node.enum.includes(null))
295
+ node.enum = [...node.enum, null];
296
+ return;
297
+ }
298
+ const constraints = Object.keys(node).filter((k) => !ANNOTATION_KEYWORDS.has(k));
299
+ if (constraints.length === 0)
300
+ return; // an unconstrained schema admits null already
301
+ if (constraints.length === 1 && constraints[0] === 'anyOf' && Array.isArray(node.anyOf)) {
302
+ if (!node.anyOf.some((s) => isPlainObject(s) && s.type === 'null'))
303
+ node.anyOf = [...node.anyOf, { type: 'null' }];
304
+ return;
305
+ }
306
+ // fromEntries defines each key, so an own `__proto__` from an untrusted
307
+ // (MCP) schema stays a key instead of setting the new object's prototype.
308
+ const inner = Object.fromEntries(constraints.map((key) => [key, node[key]]));
309
+ for (const key of constraints)
310
+ delete node[key];
311
+ node.anyOf = [inner, { type: 'null' }];
312
+ }
313
+ /**
314
+ * The strict form of one node: an object with properties lists them all as
315
+ * required and allows no others. An optional property becomes required as
316
+ * it is, not widened to null: a contract's zod schema would refuse the null.
317
+ */
318
+ function toStrictNode(node) {
319
+ if (!isPlainObject(node.properties))
320
+ return;
321
+ node.required = Object.keys(node.properties);
322
+ node.additionalProperties = false;
323
+ }
324
+ /**
325
+ * A copy of `schema` as lowercase JSON Schema, the contract's one dialect
326
+ * (`JsonSchema`), from either dialect. With `strict`, every object node
327
+ * that has properties, at any depth (inside `items`, `anyOf`, `$defs`, …),
328
+ * lists all of them as required and sets `additionalProperties: false`: the
329
+ * form OpenAI's and Anthropic's strict modes demand. An object without
330
+ * properties (a map) is left open, so a strict provider refuses it instead
331
+ * of receiving a field the model can never fill. Never mutates `schema`.
332
+ */
333
+ export function toContractJsonSchema(schema, options = {}) {
334
+ return contractSchema(schema, false, options.strict === true);
335
+ }
336
+ /**
337
+ * The parameters a zod schema declares, in the contract's dialect: its
338
+ * input side (zodInputJsonSchema) without the keywords the ADK path cannot
339
+ * carry (dropZodOnlyKeywords). defineTool's declaration() and
340
+ * contractToolDeclaration() both build from here, so a contract declares the
341
+ * same parameters on either runtime.
342
+ */
343
+ export function zodToolParameters(schema, options = {}) {
344
+ return contractSchema(zodInputJsonSchema(schema), true, options.strict === true);
345
+ }
346
+ function contractSchema(schema, fromZod, strict) {
347
+ return mapSchemaNodes(schema, (node) => toContractNode(node, fromZod, strict)) ?? { type: 'object', properties: {} };
348
+ }
349
+ /** A defineTool contract (lib/tools/toolContract.ts), told apart from an ADK tool, which has runAsync. */
350
+ function isToolContract(tool) {
351
+ const schema = tool.schema;
352
+ return !!schema && typeof schema.safeParse === 'function' && typeof tool.execute === 'function' && !('runAsync' in tool);
353
+ }
354
+ /**
355
+ * The declaration a model receives for one client-side tool, in the
356
+ * contract's shape (ToolDeclaration, lib/models/contract.ts).
357
+ *
358
+ * - An own Tool (lib/tools/tool.ts), defineTool's among them: its own
359
+ * `declaration()`.
360
+ * - A plain defineTool-shaped contract (name, description, zod schema,
361
+ * execute): built directly from its zod schema (zodToolParameters), never
362
+ * through Gemini's uppercase dialect. The input side is declared, so a
363
+ * field with a default is optional; the `default` keyword and a boolean
364
+ * `additionalProperties` are left out, as the ADK path leaves them out,
365
+ * and a record's value schema is kept, as the ADK path keeps it. The
366
+ * contract and the FunctionTool that toFunctionTool() makes of it declare
367
+ * the same parameters.
368
+ * - An ADK tool (FunctionTool, AgentTool, the memory tools, MCP tools): read
369
+ * from its own `_getDeclaration()` (ADR 0019), `parameters` or else
370
+ * `parametersJsonSchema`, and converted from Gemini's dialect once, here.
371
+ * - A plain object with `name` and `parameters` (tests, hand-built tools).
372
+ *
373
+ * With `strict`, the parameters take the strict form (toContractJsonSchema)
374
+ * and the declaration carries `strict: true`. Returns undefined for a tool
375
+ * that declares nothing (a server-side tool: see nativeToolOf, or an
376
+ * InstructionTool such as `preload_memory`, which only writes into the
377
+ * instruction, as itself or as its ADK tool) and for one with no name.
378
+ */
379
+ export function contractToolDeclaration(tool, options = {}) {
380
+ if (!isPlainObject(tool) || isInstructionTool(tool) || nativeToolMarkerOf(tool))
381
+ return undefined;
382
+ const strict = options.strict === true;
383
+ let name;
384
+ let description;
385
+ let parameters;
386
+ if (isTool(tool)) {
387
+ const decl = tool.declaration();
388
+ name = decl.name;
389
+ description = decl.description;
390
+ parameters = contractSchema(decl.parameters, false, strict);
391
+ }
392
+ else if (isToolContract(tool)) {
393
+ name = tool.name;
394
+ description = tool.description;
395
+ parameters = zodToolParameters(tool.schema, { strict });
396
+ }
397
+ else {
398
+ let decl;
399
+ if (typeof tool._getDeclaration === 'function') {
400
+ try {
401
+ decl = tool._getDeclaration() ?? undefined;
402
+ }
403
+ catch {
404
+ decl = undefined;
405
+ }
406
+ // A tool that implements _getDeclaration and returns nothing declares nothing.
407
+ if (!decl)
408
+ return undefined;
409
+ }
410
+ name = decl ? decl.name ?? tool.name : tool.name;
411
+ description = decl ? decl.description ?? tool.description : tool.description;
412
+ const declared = decl ? decl.parameters ?? decl.parametersJsonSchema : tool.parameters;
413
+ parameters = contractSchema(declared, false, strict);
414
+ }
415
+ if (!name || typeof name !== 'string')
416
+ return undefined;
417
+ return {
418
+ name,
419
+ description: typeof description === 'string' ? description : '',
420
+ parameters,
421
+ ...(strict ? { strict: true } : {}),
422
+ };
423
+ }
424
+ /**
425
+ * The NativeTool a tool object stands for, or undefined for any other tool.
426
+ * Recognised by marker, never by class or by shape, and every marker lives
427
+ * in the global symbol registry, so a second copy of a module (or of ADK)
428
+ * still matches (ADR 0062):
429
+ * - the engine's own marker (lib/tools/tool.ts, NATIVE_TOOL): a
430
+ * NativeToolMarker (lib/tools/nativeTools.ts) and the ADK sentinel made
431
+ * from it (lib/tools/webSearchTool.ts and its siblings);
432
+ * - Gemini code execution: ADK's built-in code executor, by ADK's marker
433
+ * (the agent's `codeExecutor`, from `code_execution: gemini`);
434
+ * - ADK's own GOOGLE_SEARCH and URL_CONTEXT, by ADK's marker for a tool
435
+ * the model runs, and their name.
436
+ * A client-side tool registered under one of those names carries no marker,
437
+ * so it stays a client-side tool, as does a tool that merely declares
438
+ * nothing. ADK's other in-model tools (Vertex AI Search, enterprise web
439
+ * search, Maps grounding, RAG retrieval) have no NativeTool: they are
440
+ * undefined here and declare nothing, so a caller building a request
441
+ * reports them as dropped.
442
+ */
443
+ export function nativeToolOf(tool) {
444
+ if (!tool || typeof tool !== 'object')
445
+ return undefined;
446
+ const own = nativeToolMarkerOf(tool);
447
+ if (own)
448
+ return own;
449
+ const t = tool;
450
+ if (t[ADK_BUILT_IN_CODE_EXECUTOR] === true)
451
+ return 'code_execution';
452
+ if (t[ADK_IN_MODEL_TOOL] !== true)
453
+ return undefined;
454
+ return ADK_IN_MODEL_NATIVE_TOOLS.find((n) => n === t.name);
455
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * lib/models/tracedGemini.ts — ADK's built-in Gemini, wrapped so Gemini calls
3
+ * emit the same per-request llm.request spans (tokens, latency) as every other
4
+ * provider, and get the shared transient-failure retries (lib/models/retry.ts).
5
+ *
6
+ * WHY its own module: two modules use it. lib/models/registry.ts registers it
7
+ * for every Gemini id on the ADK path and builds it in resolveModel, and the
8
+ * temporary AdkGeminiAdapter (lib/models/adkGeminiAdapter.ts) runs its calls
9
+ * through generateWithRetries. registry.ts also builds that adapter in
10
+ * resolveAdapter, so the class living in registry.ts would be an ESM import
11
+ * cycle. registry.ts re-exports it, so `TracedGemini` imports from there keep
12
+ * working.
13
+ */
14
+ import { Gemini } from '@google/adk';
15
+ import type { GeminiParams, LlmRequest, LlmResponse } from '@google/adk';
16
+ import type { ProviderEndpoint } from './endpoints.ts';
17
+ export declare class TracedGemini extends Gemini {
18
+ #private;
19
+ static readonly supportedModels: Array<string | RegExp>;
20
+ /**
21
+ * The Gemini platform (ADR 0023) applies to every construction, the
22
+ * LLMRegistry's included: on Vertex AI the client authenticates with
23
+ * Google Application Default Credentials for the configured project and
24
+ * location, and an AI Studio key (the environment's or a caller's) is not
25
+ * sent. `endpoint` overrides the environment's (the credentials plug point).
26
+ */
27
+ constructor(params?: GeminiParams & {
28
+ endpoint?: ProviderEndpoint;
29
+ });
30
+ generateContentAsync(llmRequest: LlmRequest, stream?: boolean, abortSignal?: AbortSignal): AsyncGenerator<LlmResponse, void>;
31
+ /**
32
+ * One call through ADK's Gemini with the shared retries, tagging whatever
33
+ * llm.request span is active (llm.web_search.native for Gemini grounding,
34
+ * llm.retries, llm.http_status). generateContentAsync opens that span
35
+ * around it; AdkGeminiAdapter (lib/models/adkGeminiAdapter.ts) runs it
36
+ * inside the span its own caller opens (ADR 0053). A failed call throws,
37
+ * as ADK's Gemini does. The abort signal is forwarded so a canceled turn stops the
38
+ * request in flight (ADK's Gemini puts it on the genai request config).
39
+ */
40
+ generateWithRetries(llmRequest: LlmRequest, stream?: boolean, abortSignal?: AbortSignal): AsyncGenerator<LlmResponse, void>;
41
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * lib/models/tracedGemini.ts — ADK's built-in Gemini, wrapped so Gemini calls
3
+ * emit the same per-request llm.request spans (tokens, latency) as every other
4
+ * provider, and get the shared transient-failure retries (lib/models/retry.ts).
5
+ *
6
+ * WHY its own module: two modules use it. lib/models/registry.ts registers it
7
+ * for every Gemini id on the ADK path and builds it in resolveModel, and the
8
+ * temporary AdkGeminiAdapter (lib/models/adkGeminiAdapter.ts) runs its calls
9
+ * through generateWithRetries. registry.ts also builds that adapter in
10
+ * resolveAdapter, so the class living in registry.ts would be an ESM import
11
+ * cycle. registry.ts re-exports it, so `TracedGemini` imports from there keep
12
+ * working.
13
+ */
14
+ import { Gemini } from '@google/adk';
15
+ import { endpointFromEnv, platformModel } from './endpoints.js';
16
+ import { llmRequestToModelRequest } from './genaiMapping.js';
17
+ import { errorStatus, retryUntilFirstYield } from './retry.js';
18
+ import { setLlmSpanAttribute, traceLlmGeneration } from '../observability/tracer.js';
19
+ import { currentTurnSignal } from '../runtime/turnControl.js';
20
+ export class TracedGemini extends Gemini {
21
+ // CRITICAL: reuse Gemini's exact regex instances. The LLMRegistry dict is
22
+ // keyed by the regex OBJECT — registering the same instances REPLACES the
23
+ // built-in Gemini entries instead of adding shadowed duplicates.
24
+ static supportedModels = Gemini.supportedModels;
25
+ /**
26
+ * The Gemini platform (ADR 0023) applies to every construction, the
27
+ * LLMRegistry's included: on Vertex AI the client authenticates with
28
+ * Google Application Default Credentials for the configured project and
29
+ * location, and an AI Studio key (the environment's or a caller's) is not
30
+ * sent. `endpoint` overrides the environment's (the credentials plug point).
31
+ */
32
+ constructor(params = {}) {
33
+ const { endpoint: given, ...rest } = params;
34
+ const e = given ?? endpointFromEnv('gemini');
35
+ const model = rest.model ? platformModel(e, rest.model) : rest.model;
36
+ super(e.platform === 'vertex'
37
+ ? { ...rest, model, apiKey: e.apiKey, vertexai: true, project: e.project, location: e.location }
38
+ : { ...rest, model, ...(e.apiKey && !rest.apiKey ? { apiKey: e.apiKey } : {}) });
39
+ this.#vertex = e.platform === 'vertex';
40
+ }
41
+ /** True when the client runs on Vertex AI (ADR 0023). */
42
+ #vertex;
43
+ async *generateContentAsync(llmRequest, stream, abortSignal) {
44
+ yield* traceLlmGeneration({
45
+ provider: 'gemini',
46
+ model: this.model,
47
+ request: () => llmRequestToModelRequest(llmRequest, { model: llmRequest.model || this.model, stream }),
48
+ }, this.generateWithRetries(llmRequest, stream, abortSignal ?? currentTurnSignal()));
49
+ }
50
+ /**
51
+ * One call through ADK's Gemini with the shared retries, tagging whatever
52
+ * llm.request span is active (llm.web_search.native for Gemini grounding,
53
+ * llm.retries, llm.http_status). generateContentAsync opens that span
54
+ * around it; AdkGeminiAdapter (lib/models/adkGeminiAdapter.ts) runs it
55
+ * inside the span its own caller opens (ADR 0053). A failed call throws,
56
+ * as ADK's Gemini does. The abort signal is forwarded so a canceled turn stops the
57
+ * request in flight (ADK's Gemini puts it on the genai request config).
58
+ */
59
+ async *generateWithRetries(llmRequest, stream, abortSignal) {
60
+ // The compiler asks every agent's config for includeServerSideToolInvocations
61
+ // (lib/compile.ts), which the Gemini API needs beside function
62
+ // declarations. @google/genai refuses it on Vertex AI before any request
63
+ // is sent ("only supported in Gemini Developer API mode"), so on Vertex
64
+ // it is left off, and the call goes out as it did before the flag.
65
+ const toolConfig = llmRequest.config?.toolConfig;
66
+ if (this.#vertex && toolConfig && 'includeServerSideToolInvocations' in toolConfig) {
67
+ const { includeServerSideToolInvocations: _vertexRefuses, ...kept } = toolConfig;
68
+ llmRequest.config = { ...llmRequest.config, toolConfig: kept };
69
+ }
70
+ const hasGrounding = (llmRequest.config?.tools ?? []).some((t) => t && (t.googleSearch || t.googleSearchRetrieval));
71
+ if (hasGrounding)
72
+ setLlmSpanAttribute('llm.web_search.native', true);
73
+ // Retries live here, not in genai's own httpOptions.retryOptions: that
74
+ // hook (p-retry 4, client-level only) ignores the abort signal — it
75
+ // sleeps and re-sends after a canceled turn — honours no Retry-After,
76
+ // does not retry Node's "fetch failed" resets, and replaces a 4xx's
77
+ // error body (e.g. "API key not valid") with a bare statusText. Wrapping
78
+ // the call keeps genai's ApiError intact and applies the same policy as
79
+ // every other adapter. A retry is only made while nothing has been
80
+ // yielded, so a stream that fails mid-reply is surfaced, not replayed.
81
+ // ADK's request preprocessing is idempotent, so re-sending the same
82
+ // llmRequest is safe. The registry class, resolveModel()'s per-request
83
+ // instances and AdkGeminiAdapter's are all TracedGemini, so all get this.
84
+ try {
85
+ yield* retryUntilFirstYield(() => super.generateContentAsync(llmRequest, stream, abortSignal), {
86
+ signal: abortSignal,
87
+ onRetry: ({ retries }) => setLlmSpanAttribute('llm.retries', retries),
88
+ });
89
+ }
90
+ catch (err) {
91
+ // genai's ApiError carries the status; put it where the ledger reads.
92
+ const status = errorStatus(err);
93
+ if (status !== undefined)
94
+ setLlmSpanAttribute('llm.http_status', status);
95
+ throw err;
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * lib/models/urls.ts — small URL helpers shared by the adapters, the
3
+ * gateway, the endpoints module and the embeddings provider.
4
+ *
5
+ * WHY: `url.replace(/\/+$/, '')` backtracks quadratically on a long run of
6
+ * slashes followed by another character (CodeQL js/polynomial-redos), and
7
+ * a base URL can come from an environment variable or a caller's option.
8
+ * This scans from the end once.
9
+ */
10
+ /** `url` without its trailing slashes, in one pass from the end. */
11
+ export declare function trimTrailingSlashes(url: string): string;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * lib/models/urls.ts — small URL helpers shared by the adapters, the
3
+ * gateway, the endpoints module and the embeddings provider.
4
+ *
5
+ * WHY: `url.replace(/\/+$/, '')` backtracks quadratically on a long run of
6
+ * slashes followed by another character (CodeQL js/polynomial-redos), and
7
+ * a base URL can come from an environment variable or a caller's option.
8
+ * This scans from the end once.
9
+ */
10
+ /** `url` without its trailing slashes, in one pass from the end. */
11
+ export function trimTrailingSlashes(url) {
12
+ let end = url.length;
13
+ while (end > 0 && url.charCodeAt(end - 1) === 47 /* '/' */)
14
+ end--;
15
+ return url.slice(0, end);
16
+ }
@@ -1,3 +1,24 @@
1
+ /**
2
+ * lib/net/redirects.ts — follow HTTP redirects one hop at a time, each hop
3
+ * held to a policy, for code that makes its own requests.
4
+ *
5
+ * WHY: fetch follows redirects by default, so a guard that vets only the URL
6
+ * a request starts at is bypassed by any server that answers 302: an allowed
7
+ * public API with an open redirect can send a call to 169.254.169.254, and
8
+ * fetch forwards every header but Authorization and Cookie (an API key in a
9
+ * custom header crosses origins). web_extract follows redirects manually for
10
+ * this reason, and so do the OpenAPI tools (lib/tools/openapi/call.ts) and
11
+ * the MCP client (lib/tools/mcpToolFactory.ts), through this module.
12
+ *
13
+ * HOW: `fetchWithRedirectPolicy(input, init, policy, fetch)` sends every
14
+ * request with `redirect: 'manual'`; each hop's URL goes to
15
+ * `policy.hopProblem` before it is fetched, a hop to another origin keeps
16
+ * only content-negotiation headers, and a chain longer than MAX_REDIRECTS is
17
+ * an error. 303, and 301/302 after a POST, continue as a GET without a body,
18
+ * as browsers do. The policy is a parameter of the call: nothing is
19
+ * installed on globalThis.fetch (ADR 0067 retired the context-scoped
20
+ * wrapper ADR 0036 put there while ADK made the OpenAPI requests).
21
+ */
1
22
  export declare const MAX_REDIRECTS = 5;
2
23
  export interface RedirectPolicy {
3
24
  /** Why the request may not follow a redirect to `url`, or null to follow it. */
@@ -9,6 +30,4 @@ type Fetch = typeof globalThis.fetch;
9
30
  * Throws when a hop is refused or the chain is too long.
10
31
  */
11
32
  export declare function fetchWithRedirectPolicy(input: string | URL | Request, init: RequestInit | undefined, policy: RedirectPolicy, fetchImpl: Fetch): Promise<Response>;
12
- /** Run `fn` with every globalThis.fetch it makes following redirects under `policy`. */
13
- export declare function withRedirectGuard<T>(policy: RedirectPolicy, fn: () => Promise<T>): Promise<T>;
14
33
  export {};