melchizedek-agents 0.18.0 → 0.20.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 (293) hide show
  1. package/.env.example +12 -0
  2. package/CHANGELOG.md +1001 -0
  3. package/DOCUMENTATION.md +252 -65
  4. package/QUICKSTART.md +9 -6
  5. package/README.md +42 -4
  6. package/config/agents/examples/harness.yaml +3 -1
  7. package/config/agents/examples/model_zoo.yaml +10 -9
  8. package/config/agents/syndicateSchema.yaml +53 -16
  9. package/config/agents/templates/account_memory.yaml +2 -6
  10. package/config/agents/templates/case_desk.yaml +3 -9
  11. package/config/agents/templates/draft_review.yaml +3 -9
  12. package/config/agents/templates/intake_extractor.yaml +1 -3
  13. package/config/agents/templates/research_brief.yaml +3 -9
  14. package/config/agents/templates/research_desk.yaml +3 -9
  15. package/config/agents/templates/review_panel.yaml +4 -12
  16. package/config/agents/templates/support_triage.yaml +4 -12
  17. package/config/agents/templates/systems_operator.yaml +2 -6
  18. package/db/hardening.sql +7 -1
  19. package/db/migrations/0013_tool_credentials.sql +260 -0
  20. package/dist/lib/a2a/app.d.ts +70 -6
  21. package/dist/lib/a2a/app.js +107 -2
  22. package/dist/lib/a2a/executor.d.ts +20 -3
  23. package/dist/lib/a2a/executor.js +66 -7
  24. package/dist/lib/a2a/remoteAgent.d.ts +24 -9
  25. package/dist/lib/a2a/remoteAgent.js +31 -14
  26. package/dist/lib/adkPeer.d.ts +63 -0
  27. package/dist/lib/adkPeer.js +240 -0
  28. package/dist/lib/compile.d.ts +159 -30
  29. package/dist/lib/compile.js +215 -134
  30. package/dist/lib/compileAdk.d.ts +32 -0
  31. package/dist/lib/compileAdk.js +142 -0
  32. package/dist/lib/compileNative.d.ts +111 -0
  33. package/dist/lib/compileNative.js +278 -0
  34. package/dist/lib/config.d.ts +6 -5
  35. package/dist/lib/config.js +6 -5
  36. package/dist/lib/doctor.d.ts +28 -1
  37. package/dist/lib/doctor.js +64 -2
  38. package/dist/lib/index.d.ts +6 -1
  39. package/dist/lib/index.js +7 -1
  40. package/dist/lib/loadSyndicate.d.ts +18 -0
  41. package/dist/lib/memory/erase.d.ts +5 -3
  42. package/dist/lib/memory/erase.js +5 -2
  43. package/dist/lib/memory/namespace.d.ts +8 -1
  44. package/dist/lib/memory/namespace.js +12 -6
  45. package/dist/lib/memory/providers.js +2 -1
  46. package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
  47. package/dist/lib/memory/supabaseMemoryService.js +42 -2
  48. package/dist/lib/model.d.ts +47 -0
  49. package/dist/lib/model.js +37 -0
  50. package/dist/lib/models/adapterResolver.d.ts +115 -0
  51. package/dist/lib/models/adapterResolver.js +137 -0
  52. package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
  53. package/dist/lib/models/adkGeminiAdapter.js +369 -0
  54. package/dist/lib/models/adkShim.d.ts +111 -0
  55. package/dist/lib/models/adkShim.js +136 -0
  56. package/dist/lib/models/capabilities.d.ts +3 -0
  57. package/dist/lib/models/capabilities.js +33 -23
  58. package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
  59. package/dist/lib/models/chatCompletionsAdapter.js +854 -0
  60. package/dist/lib/models/circuitBreaker.d.ts +42 -0
  61. package/dist/lib/models/circuitBreaker.js +70 -0
  62. package/dist/lib/models/claudeAdapter.d.ts +103 -0
  63. package/dist/lib/models/claudeAdapter.js +669 -0
  64. package/dist/lib/models/claudeLlm.d.ts +39 -62
  65. package/dist/lib/models/claudeLlm.js +45 -350
  66. package/dist/lib/models/claudeModels.d.ts +127 -0
  67. package/dist/lib/models/claudeModels.js +237 -0
  68. package/dist/lib/models/contract.d.ts +395 -0
  69. package/dist/lib/models/contract.js +39 -0
  70. package/dist/lib/models/endpoints.js +2 -1
  71. package/dist/lib/models/errorResponse.d.ts +50 -0
  72. package/dist/lib/models/errorResponse.js +48 -0
  73. package/dist/lib/models/fallback.d.ts +18 -11
  74. package/dist/lib/models/fallback.js +54 -43
  75. package/dist/lib/models/fallbackAdapter.d.ts +55 -0
  76. package/dist/lib/models/fallbackAdapter.js +62 -0
  77. package/dist/lib/models/gateway.js +2 -1
  78. package/dist/lib/models/gatewayAdapter.d.ts +51 -0
  79. package/dist/lib/models/gatewayAdapter.js +81 -0
  80. package/dist/lib/models/gatewayLlm.d.ts +12 -33
  81. package/dist/lib/models/gatewayLlm.js +16 -77
  82. package/dist/lib/models/geminiAdapter.d.ts +125 -0
  83. package/dist/lib/models/geminiAdapter.js +854 -0
  84. package/dist/lib/models/geminiState.d.ts +46 -0
  85. package/dist/lib/models/geminiState.js +51 -0
  86. package/dist/lib/models/genaiMapping.d.ts +283 -0
  87. package/dist/lib/models/genaiMapping.js +965 -0
  88. package/dist/lib/models/gptAdapter.d.ts +240 -0
  89. package/dist/lib/models/gptAdapter.js +804 -0
  90. package/dist/lib/models/gptLlm.d.ts +63 -123
  91. package/dist/lib/models/gptLlm.js +89 -603
  92. package/dist/lib/models/grokAdapter.d.ts +86 -0
  93. package/dist/lib/models/grokAdapter.js +142 -0
  94. package/dist/lib/models/grokLlm.d.ts +22 -45
  95. package/dist/lib/models/grokLlm.js +24 -69
  96. package/dist/lib/models/kimiAdapter.d.ts +157 -0
  97. package/dist/lib/models/kimiAdapter.js +203 -0
  98. package/dist/lib/models/kimiLlm.d.ts +15 -107
  99. package/dist/lib/models/kimiLlm.js +19 -155
  100. package/dist/lib/models/ollamaAdapter.d.ts +125 -0
  101. package/dist/lib/models/ollamaAdapter.js +155 -0
  102. package/dist/lib/models/ollamaLlm.d.ts +11 -107
  103. package/dist/lib/models/ollamaLlm.js +15 -153
  104. package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
  105. package/dist/lib/models/openAiCompatibleLlm.js +109 -671
  106. package/dist/lib/models/providerState.d.ts +63 -0
  107. package/dist/lib/models/providerState.js +61 -0
  108. package/dist/lib/models/reasoning.d.ts +26 -0
  109. package/dist/lib/models/reasoning.js +59 -0
  110. package/dist/lib/models/registry.d.ts +62 -35
  111. package/dist/lib/models/registry.js +100 -124
  112. package/dist/lib/models/retry.d.ts +2 -1
  113. package/dist/lib/models/retry.js +3 -2
  114. package/dist/lib/models/schemaNormalize.d.ts +98 -0
  115. package/dist/lib/models/schemaNormalize.js +321 -1
  116. package/dist/lib/models/tracedGemini.d.ts +41 -0
  117. package/dist/lib/models/tracedGemini.js +98 -0
  118. package/dist/lib/models/urls.d.ts +11 -0
  119. package/dist/lib/models/urls.js +16 -0
  120. package/dist/lib/net/redirects.d.ts +21 -2
  121. package/dist/lib/net/redirects.js +11 -34
  122. package/dist/lib/observability/audit.d.ts +5 -1
  123. package/dist/lib/observability/lineage.d.ts +14 -0
  124. package/dist/lib/observability/lineage.js +33 -0
  125. package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
  126. package/dist/lib/observability/supabaseSpanExporter.js +17 -9
  127. package/dist/lib/observability/tracer.d.ts +26 -8
  128. package/dist/lib/observability/tracer.js +43 -18
  129. package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
  130. package/dist/lib/runtime/adkMemoryBridge.js +68 -0
  131. package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
  132. package/dist/lib/runtime/adkSessionBridge.js +176 -0
  133. package/dist/lib/runtime/approvals.d.ts +5 -0
  134. package/dist/lib/runtime/approvals.js +39 -8
  135. package/dist/lib/runtime/credentials.d.ts +61 -0
  136. package/dist/lib/runtime/credentials.js +67 -0
  137. package/dist/lib/runtime/events.d.ts +229 -0
  138. package/dist/lib/runtime/events.js +274 -0
  139. package/dist/lib/runtime/logging.d.ts +32 -0
  140. package/dist/lib/runtime/logging.js +55 -0
  141. package/dist/lib/runtime/memoryService.d.ts +72 -0
  142. package/dist/lib/runtime/memoryService.js +25 -0
  143. package/dist/lib/runtime/native/agentLoop.d.ts +209 -0
  144. package/dist/lib/runtime/native/agentLoop.js +705 -0
  145. package/dist/lib/runtime/native/compaction.d.ts +103 -0
  146. package/dist/lib/runtime/native/compaction.js +191 -0
  147. package/dist/lib/runtime/native/delegate.d.ts +151 -0
  148. package/dist/lib/runtime/native/delegate.js +256 -0
  149. package/dist/lib/runtime/native/history.d.ts +83 -0
  150. package/dist/lib/runtime/native/history.js +312 -0
  151. package/dist/lib/runtime/native/interrupts.d.ts +129 -0
  152. package/dist/lib/runtime/native/interrupts.js +310 -0
  153. package/dist/lib/runtime/native/request.d.ts +215 -0
  154. package/dist/lib/runtime/native/request.js +585 -0
  155. package/dist/lib/runtime/native/selfCorrection.d.ts +164 -0
  156. package/dist/lib/runtime/native/selfCorrection.js +327 -0
  157. package/dist/lib/runtime/native/step.d.ts +156 -0
  158. package/dist/lib/runtime/native/step.js +263 -0
  159. package/dist/lib/runtime/native/taskMode.d.ts +64 -0
  160. package/dist/lib/runtime/native/taskMode.js +126 -0
  161. package/dist/lib/runtime/native/telemetry.d.ts +75 -0
  162. package/dist/lib/runtime/native/telemetry.js +299 -0
  163. package/dist/lib/runtime/native/tempState.d.ts +31 -0
  164. package/dist/lib/runtime/native/tempState.js +56 -0
  165. package/dist/lib/runtime/nativeTurn.d.ts +86 -0
  166. package/dist/lib/runtime/nativeTurn.js +107 -0
  167. package/dist/lib/runtime/questions.d.ts +29 -7
  168. package/dist/lib/runtime/questions.js +47 -32
  169. package/dist/lib/runtime/runtimeFlag.d.ts +38 -0
  170. package/dist/lib/runtime/runtimeFlag.js +58 -0
  171. package/dist/lib/runtime/sessions.d.ts +166 -0
  172. package/dist/lib/runtime/sessions.js +201 -0
  173. package/dist/lib/runtime/syndicateTurn.d.ts +46 -5
  174. package/dist/lib/runtime/syndicateTurn.js +233 -36
  175. package/dist/lib/runtime/valueDepth.d.ts +23 -0
  176. package/dist/lib/runtime/valueDepth.js +41 -0
  177. package/dist/lib/session/supabaseSessionService.d.ts +65 -18
  178. package/dist/lib/session/supabaseSessionService.js +148 -105
  179. package/dist/lib/session/transcript.d.ts +69 -23
  180. package/dist/lib/session/transcript.js +105 -29
  181. package/dist/lib/skills.js +33 -7
  182. package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
  183. package/dist/lib/storage/postgres/credentialStore.js +82 -0
  184. package/dist/lib/storage/postgres/index.d.ts +12 -0
  185. package/dist/lib/storage/postgres/index.js +6 -1
  186. package/dist/lib/storage/postgres/sessionService.d.ts +44 -16
  187. package/dist/lib/storage/postgres/sessionService.js +128 -66
  188. package/dist/lib/syndicateSchema.d.ts +49 -0
  189. package/dist/lib/syndicateSchema.js +99 -9
  190. package/dist/lib/toolRegistry.d.ts +26 -2
  191. package/dist/lib/toolRegistry.js +73 -17
  192. package/dist/lib/tools/adkTool.d.ts +81 -0
  193. package/dist/lib/tools/adkTool.js +268 -0
  194. package/dist/lib/tools/auth.d.ts +118 -0
  195. package/dist/lib/tools/auth.js +89 -0
  196. package/dist/lib/tools/collectionsSearchTool.d.ts +8 -7
  197. package/dist/lib/tools/collectionsSearchTool.js +12 -27
  198. package/dist/lib/tools/credentialCipher.d.ts +58 -0
  199. package/dist/lib/tools/credentialCipher.js +116 -0
  200. package/dist/lib/tools/credentialStore.d.ts +77 -0
  201. package/dist/lib/tools/credentialStore.js +246 -0
  202. package/dist/lib/tools/examples.d.ts +43 -0
  203. package/dist/lib/tools/examples.js +62 -0
  204. package/dist/lib/tools/generateImageTool.d.ts +15 -6
  205. package/dist/lib/tools/generateImageTool.js +19 -29
  206. package/dist/lib/tools/inspectImageTool.d.ts +9 -6
  207. package/dist/lib/tools/inspectImageTool.js +13 -17
  208. package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
  209. package/dist/lib/tools/mcpToolFactory.js +46 -47
  210. package/dist/lib/tools/memoryTools.d.ts +61 -0
  211. package/dist/lib/tools/memoryTools.js +119 -0
  212. package/dist/lib/tools/nativeTools.d.ts +38 -0
  213. package/dist/lib/tools/nativeTools.js +43 -0
  214. package/dist/lib/tools/oauthConsent.d.ts +181 -0
  215. package/dist/lib/tools/oauthConsent.js +330 -0
  216. package/dist/lib/tools/openapi/call.d.ts +108 -0
  217. package/dist/lib/tools/openapi/call.js +295 -0
  218. package/dist/lib/tools/openapi/parse.d.ts +147 -0
  219. package/dist/lib/tools/openapi/parse.js +570 -0
  220. package/dist/lib/tools/openapiTools.d.ts +25 -17
  221. package/dist/lib/tools/openapiTools.js +124 -121
  222. package/dist/lib/tools/scienceTools.d.ts +14 -14
  223. package/dist/lib/tools/skillToolset.d.ts +58 -87
  224. package/dist/lib/tools/skillToolset.js +61 -142
  225. package/dist/lib/tools/skills/env.d.ts +58 -0
  226. package/dist/lib/tools/skills/env.js +165 -0
  227. package/dist/lib/tools/skills/executor.d.ts +114 -0
  228. package/dist/lib/tools/skills/executor.js +331 -0
  229. package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
  230. package/dist/lib/tools/skills/frontmatter.js +215 -0
  231. package/dist/lib/tools/skills/loader.d.ts +91 -0
  232. package/dist/lib/tools/skills/loader.js +257 -0
  233. package/dist/lib/tools/skills/tools.d.ts +187 -0
  234. package/dist/lib/tools/skills/tools.js +386 -0
  235. package/dist/lib/tools/taskTools.d.ts +15 -15
  236. package/dist/lib/tools/tool.d.ts +354 -0
  237. package/dist/lib/tools/tool.js +304 -0
  238. package/dist/lib/tools/toolContract.d.ts +89 -33
  239. package/dist/lib/tools/toolContract.js +104 -82
  240. package/dist/lib/tools/urlContextTool.d.ts +4 -1
  241. package/dist/lib/tools/urlContextTool.js +6 -2
  242. package/dist/lib/tools/webExtractTool.d.ts +3 -3
  243. package/dist/lib/tools/webExtractTool.js +2 -1
  244. package/dist/lib/tools/webSearchTool.d.ts +7 -10
  245. package/dist/lib/tools/webSearchTool.js +15 -60
  246. package/dist/lib/tools/wikiTools.d.ts +19 -19
  247. package/dist/lib/tools/wikiTools.js +13 -9
  248. package/dist/lib/tools/xApiSearchTool.d.ts +3 -3
  249. package/dist/lib/tools/xApiSearchTool.js +2 -1
  250. package/dist/lib/tools/xSearchTool.d.ts +8 -10
  251. package/dist/lib/tools/xSearchTool.js +12 -63
  252. package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
  253. package/dist/lib/tools/xaiSearchParams.js +126 -0
  254. package/dist/lib/wiki/agentRun.d.ts +12 -0
  255. package/dist/lib/wiki/agentRun.js +74 -35
  256. package/dist/lib/wiki/entities.d.ts +12 -0
  257. package/dist/lib/wiki/entities.js +45 -3
  258. package/dist/lib/workflow/agentNode.d.ts +153 -0
  259. package/dist/lib/workflow/agentNode.js +365 -0
  260. package/dist/lib/workflow/graph.d.ts +178 -0
  261. package/dist/lib/workflow/graph.js +468 -0
  262. package/dist/lib/workflow/nodeEvents.d.ts +50 -0
  263. package/dist/lib/workflow/nodeEvents.js +101 -0
  264. package/dist/lib/workflow/pause.d.ts +122 -0
  265. package/dist/lib/workflow/pause.js +180 -0
  266. package/dist/lib/workflow/resume.d.ts +166 -0
  267. package/dist/lib/workflow/resume.js +441 -0
  268. package/dist/lib/workflow/route.d.ts +58 -0
  269. package/dist/lib/workflow/route.js +69 -0
  270. package/dist/lib/workflow/scheduler.d.ts +418 -0
  271. package/dist/lib/workflow/scheduler.js +756 -0
  272. package/dist/lib/workflow/toolNode.d.ts +127 -0
  273. package/dist/lib/workflow/toolNode.js +236 -0
  274. package/dist/lib/workflow/turn.d.ts +118 -0
  275. package/dist/lib/workflow/turn.js +216 -0
  276. package/dist/lib/workflow.d.ts +33 -11
  277. package/dist/lib/workflow.js +49 -27
  278. package/dist/lib/workflowConfig.d.ts +7 -2
  279. package/dist/lib/workflowConfig.js +7 -12
  280. package/dist/scripts/a2a_server.js +4 -3
  281. package/dist/scripts/assistant_worker.d.ts +2 -1
  282. package/dist/scripts/assistant_worker.js +9 -4
  283. package/dist/scripts/doctor.d.ts +3 -1
  284. package/dist/scripts/doctor.js +4 -2
  285. package/dist/scripts/syndicate_chat.js +19 -10
  286. package/package.json +9 -3
  287. package/skills/briefs/melchizedek-author.md +3 -2
  288. package/skills/briefs/melchizedek-models.md +13 -9
  289. package/skills/briefs/melchizedek.md +3 -3
  290. package/skills/melchizedek/SKILL.md +2 -1
  291. package/skills/melchizedek-author/SKILL.md +2 -1
  292. package/skills/melchizedek-author/assets/minimal.yaml +2 -0
  293. package/skills/melchizedek-models/SKILL.md +23 -6
@@ -0,0 +1,705 @@
1
+ /**
2
+ * lib/runtime/native/agentLoop.ts — the native loop for one agent: a model
3
+ * step, the step's tool calls, their results fed back, until the model
4
+ * stops calling tools or the turn stops it (ADR 0045, ADR 0066).
5
+ *
6
+ * WHY this file exists:
7
+ * On the ADK runtime this is LlmAgent.runAsyncImpl: run one step
8
+ * (lib/runtime/native/step.ts is the native step), run the answer's
9
+ * function calls (ADK's handleFunctionCallList), store the response
10
+ * event, and step again unless the last event is a final response. A
11
+ * session either runtime wrote must be one the other continues, so every
12
+ * event this loop stores is the one ADK stores for the same answer and the
13
+ * same tool results, ids and times aside. tests/nativeLoop.test.ts runs
14
+ * the boundary suite's single-agent cases both ways and compares.
15
+ *
16
+ * ONE STEP, AS ADK RUNS IT:
17
+ * 1. The model step (runModelStep). Its partial events are yielded as they
18
+ * arrive and stored nowhere, so drainAgentStream hands their text to
19
+ * onTextDelta exactly as it does on the ADK path. With a
20
+ * `fallback_model`, the step runs once per leaf adapter (ADR 0053): a
21
+ * provider-side failure before anything was produced is not stored,
22
+ * and the fallback answers the same request under its own model id, by
23
+ * ADK's FallbackLlm's rules and the shared circuit breaker (ADR 0044).
24
+ * 2. The answer's calls run in parallel, each with its own context (its
25
+ * own state delta and actions). Results are kept in call order:
26
+ * - a call naming no declared tool answers
27
+ * `{ error: "Function <name> is not found in the toolsDict." }`;
28
+ * - a tool that throws answers `{ error: "Error in tool '<name>': …" }`,
29
+ * the text ADK's FunctionTool writes (lib/tools/adkTool.ts);
30
+ * - with self-correction's tool side on (retries.tool_errors, default
31
+ * 3), those two answer with reflection guidance instead, counted per
32
+ * tool in call order (lib/runtime/native/selfCorrection.ts);
33
+ * - a tool that requires approval and has none asks for it, as
34
+ * FunctionTool's gate does: `{ error: APPROVAL_TEXTS.pending }`,
35
+ * `skipSummarization`, and the request under the call's id;
36
+ * - a long-running tool (ask_user) that returns nothing answers
37
+ * nothing; its actions, when it set any, make an event of their own;
38
+ * - a result that is not an object is wrapped `{ result }`, an array
39
+ * `{ results }`, as ADK wraps them;
40
+ * - a result nested deeper than MAX_VALUE_DEPTH levels answers
41
+ * `{ error: TOO_DEEP_RESULT }` instead: every later reader of the
42
+ * session would overflow its stack on it (lib/runtime/valueDepth.ts,
43
+ * ADR 0101). ADK keeps it.
44
+ * One call's response is stored as its own event; several are merged
45
+ * into one, parts in call order and actions merged (ADK's
46
+ * mergeParallelFunctionResponseEvents). ADK runs the calls one after
47
+ * another and lets a call read the state an earlier call of the same
48
+ * step wrote; here each call reads the state as the step left it.
49
+ * 3. An approval request ends the run: the response is NOT stored; in its
50
+ * place the event ADK stores, a call to `adk_request_confirmation`
51
+ * (args: the original call and the confirmation; an `adk-` id listed in
52
+ * longRunningToolIds), carrying the response's actions. The answer
53
+ * resumes it: before each step, lib/runtime/native/interrupts.ts runs
54
+ * the pinned call the latest user message approves or refuses, as ADK's
55
+ * request-confirmation processor does, and its response is stored
56
+ * before the request is built.
57
+ * 4. The agent's outputKey is written into each final event's stateDelta
58
+ * before it is stored (ADK's maybeSaveOutputToState, case for case).
59
+ * The `temp:` keys of every event stored are kept for the rest of the
60
+ * run, beside the session and never in it, and each later step and
61
+ * call reads them (lib/runtime/native/tempState.ts).
62
+ * 5. The loop goes on unless the step's last event is final (ADK's
63
+ * isFinalResponse), the turn stopped the step, or the step stored
64
+ * nothing. A model call that waits on a person (ask_user) is final: its
65
+ * model event lists the call in longRunningToolIds, and the run ends
66
+ * with no response to it, the call pending. The answer resumes it: the
67
+ * turn runner stores the person's next message as the call's function
68
+ * response, and the next run's first step reads the call and its answer
69
+ * from the history (history.ts), as ADK's content processor does. No
70
+ * request processor acts on it (ADR 0079).
71
+ *
72
+ * DELEGATION: a call to a subagent tool runs the subagent as its own child
73
+ * loop, as ADK's AgentTool runs it (lib/runtime/native/delegate.ts, ADR 0074).
74
+ *
75
+ * SELF-CORRECTION (ADR 0034, ADR 0075) is ADK's reflect-and-retry plugins,
76
+ * ported to lib/runtime/native/selfCorrection.ts: the step declares the
77
+ * reflection tool and passes each response through its model side, and
78
+ * each call's error or answer goes through its tool side. On by default,
79
+ * as runSyndicateTurn installs the plugins by default.
80
+ *
81
+ * COMPACTION (ADR 0033): with the agent's `context:`, each step may first
82
+ * store ADK's compacted event, written by the summary model
83
+ * (lib/runtime/native/compaction.ts). It is yielded like any stored event
84
+ * and is not the run's `lastEvent`.
85
+ *
86
+ * TASK MODE (ADR 0033, ADR 0081): finish_task is an own tool the request
87
+ * declares; a run marked `taskNode` ends on its successful answer
88
+ * (lib/runtime/native/taskMode.ts).
89
+ *
90
+ * WORKFLOW NODES (ADR 0090): a run for a workflow agent node passes
91
+ * `nodeStamp`, which writes the node's output and path on each event before
92
+ * it is stored (lib/workflow/agentNode.ts).
93
+ *
94
+ * CONSENT (WS6-3b, ADR 0085): with `credentials`, each call's
95
+ * `accessToken(provider)` reads the user's grant (ADR 0072). With `consent`,
96
+ * a call whose provider the user has not granted asks for it
97
+ * (`requestCredential`, or `accessToken` by itself): the call answers
98
+ * CONSENT_TEXTS.pending, ADK's `adk_request_credential` event is stored
99
+ * before the response (ADK's generateAuthEvent), and the run ends paused on
100
+ * it. The flow's secrets stay in the consent step (lib/tools/oauthConsent.ts).
101
+ * Before each step, a credential request the latest user message answers
102
+ * runs its paused call again (interrupts.ts grantedCalls, ADK's auth
103
+ * preprocessor), ahead of the approval resume.
104
+ *
105
+ * NOT HERE (later tickets): transfer_to_agent (no compiled syndicate sets
106
+ * subAgents). The run's spans (agent.invoke, model.call,
107
+ * tool.execute) are lib/runtime/native/telemetry.ts.
108
+ *
109
+ * ADK stays out of this file: an ADK tool an agent still lists during the
110
+ * dual period (a registry FunctionTool, the skills toolset's tools) is run
111
+ * through its runAsync with a context shaped like ADK's, by shape.
112
+ */
113
+ import { randomUUID } from 'node:crypto';
114
+ import { circuitOpen, recordFailure, recordSuccess } from '../../models/circuitBreaker.js';
115
+ import { providerForModel } from '../../models/providerMap.js';
116
+ import { resolveAdapter } from '../../models/registry.js';
117
+ import { toContractJsonSchema } from '../../models/schemaNormalize.js';
118
+ import { z } from 'zod';
119
+ import { ToolCredentialError, toolAccessToken } from '../../tools/auth.js';
120
+ import { APPROVAL_TEXTS, CONSENT_TEXTS, isTool } from '../../tools/tool.js';
121
+ import { createEventActions, createTurnEvent, getFunctionCalls, getFunctionResponses, isFinal } from '../events.js';
122
+ import { MAX_VALUE_DEPTH, nestedDeeperThan } from '../valueDepth.js';
123
+ import { currentTurnSignal } from '../turnControl.js';
124
+ import { compactBeforeStep } from './compaction.js';
125
+ import { ADK_CALL_ID_PREFIX } from './history.js';
126
+ import { runSubagent, runWorkflowSubagent, subagentOf, workflowSubagentOf } from './delegate.js';
127
+ import { approvedCalls, grantedCalls } from './interrupts.js';
128
+ import { SelfCorrection } from './selfCorrection.js';
129
+ import { eitherSignal, runModelStep, stopOf } from './step.js';
130
+ import { taskNodeRun } from './taskMode.js';
131
+ import { createRunTempState, withStateOverlay } from './tempState.js';
132
+ import { traceAgentInvocation, traceModelCall, traceToolCall } from './telemetry.js';
133
+ /** ADK's own ceiling on model calls in one run (RunConfig.maxLlmCalls); the turn's max_steps is lower. */
134
+ const MAX_LLM_CALLS = 500;
135
+ // ── Events as ADK makes them ─────────────────────────────────────────────────
136
+ const REQUEST_CONFIRMATION = 'adk_request_confirmation';
137
+ const REQUEST_CREDENTIAL = 'adk_request_credential';
138
+ const isEmptyRecord = (r) => !r || Object.keys(r).length === 0;
139
+ /** Sets an own property even for a key such as `__proto__`. */
140
+ function setOwn(target, key, value) {
141
+ Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
142
+ }
143
+ /** ADK's isDefaultEventActions. */
144
+ function isDefaultActions(a) {
145
+ return (isEmptyRecord(a.stateDelta) &&
146
+ isEmptyRecord(a.artifactDelta) &&
147
+ isEmptyRecord(a.requestedAuthConfigs) &&
148
+ isEmptyRecord(a.requestedToolConfirmations) &&
149
+ a.skipSummarization === undefined &&
150
+ a.transferToAgent === undefined &&
151
+ a.escalate === undefined);
152
+ }
153
+ /** Copies `from`'s own keys onto `to` as own keys: Object.assign would re-parent `to` for a key `__proto__` (a model-chosen call id). */
154
+ function assignOwn(to, from) {
155
+ for (const [k, v] of Object.entries(from))
156
+ setOwn(to, k, v);
157
+ }
158
+ /** ADK's mergeEventActions: dictionaries merged in order, the later flag winning. */
159
+ function mergeActions(sources) {
160
+ const out = createEventActions();
161
+ for (const s of sources) {
162
+ if (s.stateDelta)
163
+ assignOwn(out.stateDelta, s.stateDelta);
164
+ if (s.artifactDelta)
165
+ assignOwn(out.artifactDelta, s.artifactDelta);
166
+ if (s.requestedAuthConfigs)
167
+ assignOwn(out.requestedAuthConfigs, s.requestedAuthConfigs);
168
+ if (s.requestedToolConfirmations)
169
+ assignOwn(out.requestedToolConfirmations, s.requestedToolConfirmations);
170
+ if (s.skipSummarization !== undefined)
171
+ out.skipSummarization = s.skipSummarization;
172
+ if (s.transferToAgent !== undefined)
173
+ out.transferToAgent = s.transferToAgent;
174
+ if (s.escalate !== undefined)
175
+ out.escalate = s.escalate;
176
+ }
177
+ return out;
178
+ }
179
+ // ── outputKey (ADK's maybeSaveOutputToState) ─────────────────────────────────
180
+ const validators = new WeakMap();
181
+ /** The output schema as a validator, as ADK reads it (z.fromJSONSchema); null when it cannot be read. */
182
+ function validatorFor(schema) {
183
+ let v = validators.get(schema);
184
+ if (v !== undefined)
185
+ return v;
186
+ try {
187
+ v = z.fromJSONSchema(toContractJsonSchema(schema));
188
+ }
189
+ catch {
190
+ v = null;
191
+ }
192
+ validators.set(schema, v);
193
+ return v;
194
+ }
195
+ /**
196
+ * Writes the agent's final answer into `event`'s stateDelta under its
197
+ * outputKey: the text of its parts; with an output schema, the parsed and
198
+ * validated JSON (the text itself when it does not parse; the parsed value
199
+ * when it does not validate).
200
+ */
201
+ export function saveOutput(agent, event) {
202
+ if (event.author !== agent.name || !agent.outputKey || !isFinal(event))
203
+ return;
204
+ const parts = event.content?.parts;
205
+ if (!parts?.length)
206
+ return;
207
+ const text = parts.map((p) => (p.text ? p.text : '')).join('');
208
+ const delta = (event.actions.stateDelta ??= {});
209
+ let result = text;
210
+ if (agent.outputSchema) {
211
+ if (!text.trim())
212
+ return;
213
+ let parsed;
214
+ try {
215
+ parsed = JSON.parse(text);
216
+ }
217
+ catch {
218
+ setOwn(delta, agent.outputKey, text);
219
+ return;
220
+ }
221
+ try {
222
+ const validator = validatorFor(agent.outputSchema);
223
+ result = validator ? validator.parse(parsed) : parsed;
224
+ }
225
+ catch {
226
+ result = parsed;
227
+ }
228
+ }
229
+ setOwn(delta, agent.outputKey, result);
230
+ }
231
+ function callContext(scope, functionCallId, confirmation) {
232
+ const { agent, ctx, stateBase } = scope;
233
+ const session = ctx.session;
234
+ const actions = createEventActions();
235
+ const delta = actions.stateDelta;
236
+ const state = {
237
+ get: (key, fallback) => (Object.hasOwn(delta, key) ? delta[key] : Object.hasOwn(stateBase, key) ? stateBase[key] : fallback),
238
+ set: (key, value) => setOwn(delta, key, value),
239
+ has: (key) => Object.hasOwn(delta, key) || Object.hasOwn(stateBase, key),
240
+ };
241
+ const memory = ctx.memory;
242
+ // ADK's Context.requestCredential, with the flow held server-side (ADR 0085): the request, keyed by the call's id.
243
+ const consent = ctx.consent;
244
+ const requestCredential = consent && functionCallId
245
+ ? async (provider) => {
246
+ const request = await consent.begin({ appName: session.appName, userId: session.userId, sessionId: session.id, functionCallId, provider });
247
+ // An own key, as every delta: a call id such as `__proto__` names a request, never a prototype.
248
+ setOwn(actions.requestedAuthConfigs, functionCallId, request.authConfig);
249
+ }
250
+ : undefined;
251
+ const credentials = ctx.credentials;
252
+ const readToken = credentials ? toolAccessToken(credentials, session.appName, session.userId, scope.signal) : undefined;
253
+ // A grant the user has not given (or that can no longer be renewed) is asked for, when the provider has a consent step.
254
+ const accessToken = readToken
255
+ ? async (provider) => {
256
+ try {
257
+ return await readToken(provider);
258
+ }
259
+ catch (error) {
260
+ const ask = error instanceof ToolCredentialError && (error.code === 'not_connected' || error.code === 'expired' || error.code === 'refresh_failed');
261
+ if (ask && requestCredential && consent?.has(provider))
262
+ await requestCredential(provider);
263
+ throw error;
264
+ }
265
+ }
266
+ : undefined;
267
+ return {
268
+ invocationId: ctx.invocationId,
269
+ agentName: agent.name,
270
+ functionCallId,
271
+ userId: session.userId,
272
+ appName: session.appName,
273
+ sessionId: session.id,
274
+ state,
275
+ get stateDelta() {
276
+ return delta;
277
+ },
278
+ actions,
279
+ // ADK's Context.requestConfirmation: the request, keyed by the call's id.
280
+ requestConfirmation: ({ hint, payload } = {}) => {
281
+ if (!functionCallId)
282
+ throw new Error('functionCallId is not set.');
283
+ setOwn(actions.requestedToolConfirmations, functionCallId, { hint: hint ?? '', confirmed: false, payload });
284
+ },
285
+ // The person's answer, when this call is the pinned call an approval resumes (interrupts.ts).
286
+ confirmation,
287
+ toolConfirmation: confirmation,
288
+ signal: scope.signal,
289
+ abortSignal: scope.signal,
290
+ ...(ctx.userContent ? { userContent: ctx.userContent } : {}),
291
+ ...(memory
292
+ ? { searchMemory: (query) => memory.search({ appName: session.appName, userId: session.userId, query }) }
293
+ : {}),
294
+ ...(accessToken ? { accessToken } : {}),
295
+ ...(requestCredential ? { requestCredential } : {}),
296
+ invocationContext: {
297
+ invocationId: ctx.invocationId,
298
+ agent: { name: agent.name },
299
+ branch: ctx.branch,
300
+ session,
301
+ userContent: ctx.userContent,
302
+ },
303
+ };
304
+ }
305
+ /** What a tool's result becomes when it nests past MAX_VALUE_DEPTH (ADR 0101). */
306
+ export const TOO_DEEP_RESULT = (toolName) => `The result of tool '${toolName}' nested deeper than ${MAX_VALUE_DEPTH} levels and was not kept.`;
307
+ /** ADK's normalization of a tool's result into a function response. */
308
+ function asResponse(value) {
309
+ if (typeof value !== 'object' || value === null)
310
+ return { result: value };
311
+ if (Array.isArray(value))
312
+ return { results: value };
313
+ return value;
314
+ }
315
+ function isAdkShaped(tool) {
316
+ return !!tool && typeof tool === 'object' && typeof tool.runAsync === 'function';
317
+ }
318
+ /** An own Tool's call, as FunctionTool runs it on the ADK path: its approval gate, then execute, a throw named for the tool. */
319
+ async function runOwnTool(tool, args, context) {
320
+ try {
321
+ if (tool.requiresApproval === true) {
322
+ if (!context.confirmation) {
323
+ context.requestConfirmation({ hint: APPROVAL_TEXTS.hint(tool.name) });
324
+ context.actions.skipSummarization = true;
325
+ return { error: APPROVAL_TEXTS.pending };
326
+ }
327
+ if (!context.confirmation.confirmed)
328
+ return { error: APPROVAL_TEXTS.rejected };
329
+ }
330
+ return await tool.execute(args, context);
331
+ }
332
+ catch (error) {
333
+ const message = error instanceof Error ? error.message : String(error);
334
+ throw new Error(`Error in tool '${tool.name}': ${message}`);
335
+ }
336
+ }
337
+ async function runCall(scope, call, tools, correction, confirmation) {
338
+ const context = callContext(scope, call.id || undefined, confirmation);
339
+ const name = call.name ?? '';
340
+ const tool = name && tools.has(name) ? tools.get(name) : undefined;
341
+ const callable = isTool(tool) || isAdkShaped(tool);
342
+ if (!callable) {
343
+ const toolName = name || '<unnamed>';
344
+ const notFound = `Function ${toolName} is not found in the toolsDict.`;
345
+ const guided = await correction?.failed(toolName, call.args ?? {}, new Error(notFound));
346
+ const part = {
347
+ functionResponse: { name: toolName, response: guided ?? { error: notFound }, id: context.functionCallId },
348
+ };
349
+ return { part, actions: context.actions };
350
+ }
351
+ const args = call.args ?? {};
352
+ const toolName = tool.name;
353
+ const longRunning = isTool(tool) ? tool.longRunning === true : tool.isLongRunning === true;
354
+ // Delegation (WS2-6): a subagent runs as its own child loop, a nested workflow as its own walk (ADR 0098), before the generic path (delegate.ts).
355
+ const subagent = subagentOf(tool);
356
+ const workflow = subagent ? undefined : workflowSubagentOf(tool);
357
+ const delegation = { ctx: scope.ctx, stateBase: scope.stateBase, signal: scope.signal, runLoop: runAgentLoop, queue: scope };
358
+ let response;
359
+ let failure;
360
+ try {
361
+ response = subagent
362
+ ? await runSubagent(subagent, args, context, delegation)
363
+ : workflow
364
+ ? await runWorkflowSubagent(workflow, args, context, delegation)
365
+ : isTool(tool) ? await runOwnTool(tool, args, context) : await tool.runAsync({ args, toolContext: context });
366
+ }
367
+ catch (e) {
368
+ failure = e instanceof Error ? e.message : e;
369
+ // Self-correction answers a thrown Error with reflection guidance in its place; a call waiting on a grant is not a failure.
370
+ const guided = isEmptyRecord(context.actions.requestedAuthConfigs) ? await correction?.failed(toolName, args, e) : undefined;
371
+ if (guided)
372
+ [response, failure] = [guided, undefined];
373
+ }
374
+ // Consent hook (WS6-3b, ADR 0085): a call that asked for a grant answers that it waits, whatever it returned or threw.
375
+ const asked = Object.values(context.actions.requestedAuthConfigs ?? {});
376
+ if (asked.length > 0) {
377
+ const provider = String(asked[0]?.credentialKey ?? '');
378
+ return { part: { functionResponse: { id: context.functionCallId, name: toolName, response: { result: CONSENT_TEXTS.pending(provider) } } }, actions: context.actions };
379
+ }
380
+ if (failure === undefined)
381
+ await correction?.answered(toolName, response);
382
+ // As ADK: a long-running call with no response answers nothing, even when it threw.
383
+ if (longRunning && (response === null || response === undefined)) {
384
+ return isDefaultActions(context.actions) ? undefined : { actions: context.actions };
385
+ }
386
+ const answer = failure ? { error: failure } : response === null || response === undefined ? { result: response } : asResponse(response);
387
+ // A result nested past MAX_VALUE_DEPTH would overflow every later reader of the session (lib/runtime/valueDepth.ts, ADR 0101): a note stands in for it.
388
+ const kept = nestedDeeperThan(answer) ? { error: TOO_DEEP_RESULT(toolName) } : answer;
389
+ return { part: { functionResponse: { id: context.functionCallId, name: toolName, response: kept } }, actions: context.actions };
390
+ }
391
+ /**
392
+ * The calls run, in parallel, as the one response event ADK stores for them
393
+ * (not yet stored); undefined when none answered. `confirmations` holds the
394
+ * person's answer for a pinned call an approval resumes, by call id.
395
+ */
396
+ async function runCalls(scope, calls, tools, confirmations) {
397
+ const order = scope.selfCorrection?.forCalls(scope.ctx.invocationId, calls.length);
398
+ const outcomes = (await Promise.all(calls.map((call, i) => traceToolCall(call, tools.get(call.name ?? ''), () => runCall(scope, call, tools, order?.call(i), call.id ? confirmations?.get(call.id) : undefined)).finally(() => order?.release(i))))).filter((o) => !!o);
399
+ if (outcomes.length === 0)
400
+ return undefined;
401
+ const base = { invocationId: scope.ctx.invocationId, author: scope.agent.name };
402
+ const contentOf = (parts) => ({ role: 'user', parts });
403
+ if (outcomes.length === 1) {
404
+ const [only] = outcomes;
405
+ return createTurnEvent({
406
+ ...base,
407
+ ...(only.part ? { content: contentOf([only.part]) } : {}),
408
+ actions: only.actions,
409
+ branch: scope.ctx.branch,
410
+ });
411
+ }
412
+ return createTurnEvent({
413
+ ...base,
414
+ branch: scope.ctx.branch,
415
+ content: contentOf(outcomes.flatMap((o) => (o.part ? [o.part] : []))),
416
+ actions: mergeActions(outcomes.map((o) => o.actions)),
417
+ });
418
+ }
419
+ /** ADK's generateAuthEvent: a credential request per call that asked for one. */
420
+ function authEvent(scope, response) {
421
+ const configs = response.actions.requestedAuthConfigs;
422
+ if (isEmptyRecord(configs))
423
+ return undefined;
424
+ const parts = Object.entries(configs).map(([callId, authConfig]) => ({
425
+ functionCall: { name: REQUEST_CREDENTIAL, args: { function_call_id: callId, auth_config: authConfig }, id: `${ADK_CALL_ID_PREFIX}${randomUUID()}` },
426
+ }));
427
+ return createTurnEvent({
428
+ invocationId: scope.ctx.invocationId,
429
+ author: scope.agent.name,
430
+ branch: scope.ctx.branch,
431
+ content: { parts, role: response.content?.role ?? 'user' },
432
+ longRunningToolIds: parts.map((p) => p.functionCall?.id),
433
+ });
434
+ }
435
+ /** ADK's generateRequestConfirmationEvent: an adk_request_confirmation call per gated call, carrying the response's actions. */
436
+ function confirmationEvent(scope, modelEvent, response) {
437
+ const requested = response.actions.requestedToolConfirmations;
438
+ if (isEmptyRecord(requested))
439
+ return undefined;
440
+ const calls = getFunctionCalls(modelEvent);
441
+ const parts = [];
442
+ for (const [callId, toolConfirmation] of Object.entries(requested)) {
443
+ const originalFunctionCall = calls.find((c) => c.id === callId);
444
+ if (!originalFunctionCall)
445
+ continue;
446
+ parts.push({ functionCall: { name: REQUEST_CONFIRMATION, args: { originalFunctionCall, toolConfirmation }, id: `${ADK_CALL_ID_PREFIX}${randomUUID()}` } });
447
+ }
448
+ return createTurnEvent({
449
+ invocationId: scope.ctx.invocationId,
450
+ author: scope.agent.name,
451
+ branch: scope.ctx.branch,
452
+ content: { parts, role: response.content?.role ?? 'user' },
453
+ actions: response.actions,
454
+ longRunningToolIds: parts.map((p) => p.functionCall?.id),
455
+ });
456
+ }
457
+ /**
458
+ * ADK's request-confirmation processor, before a step: the pinned calls the
459
+ * latest user message approves or refuses run with the answer in their
460
+ * context, and their response is returned to be stored (interrupts.ts). Undefined when
461
+ * there is none; 'stopped' when the turn stopped while they ran (nothing is
462
+ * stored). Throws IntentMismatchError when an answer does not bind.
463
+ */
464
+ async function resumeApprovals(agent, ctx, stateBase, selfCorrection) {
465
+ const signal = eitherSignal(ctx.signal, currentTurnSignal());
466
+ const scope = { agent, ctx, stateBase, selfCorrection, ...(signal ? { signal } : {}) };
467
+ const approved = await approvedCalls(agent, ctx, (id) => callContext(scope, id));
468
+ if (!approved)
469
+ return undefined;
470
+ const response = await runCalls(scope, approved.calls, approved.tools, approved.confirmations);
471
+ if (signal?.aborted)
472
+ return 'stopped';
473
+ return response;
474
+ }
475
+ /**
476
+ * ADK's auth preprocessor, before a step (ADR 0085): the paused calls whose
477
+ * credential requests the latest user message answers run again, now with
478
+ * the grant in the store (interrupts.ts grantedCalls). Returns their
479
+ * response and the credential request a call raised again, if any, to be
480
+ * stored; undefined when there is none; 'stopped' when the turn stopped
481
+ * while they ran.
482
+ */
483
+ async function resumeGrants(agent, ctx, stateBase, selfCorrection) {
484
+ const granted = await grantedCalls(agent, ctx);
485
+ if (!granted)
486
+ return undefined;
487
+ const signal = eitherSignal(ctx.signal, currentTurnSignal());
488
+ const scope = { agent, ctx, stateBase, selfCorrection, ...(signal ? { signal } : {}) };
489
+ const response = await runCalls(scope, granted.calls, granted.tools);
490
+ if (signal?.aborted)
491
+ return 'stopped';
492
+ if (!response)
493
+ return undefined;
494
+ const auth = authEvent(scope, response);
495
+ return { response, ...(auth ? { auth } : {}) };
496
+ }
497
+ // ── The model step, with partials and the fallback ───────────────────────────
498
+ /** Runs a step, yielding its partial events as they arrive; returns the step's result. */
499
+ async function* withPartials(start) {
500
+ const queue = [];
501
+ let wake;
502
+ let done = false;
503
+ const settle = () => {
504
+ done = true;
505
+ wake?.();
506
+ };
507
+ const run = start((event) => {
508
+ queue.push(event);
509
+ wake?.();
510
+ });
511
+ run.then(settle, settle);
512
+ for (;;) {
513
+ while (queue.length > 0)
514
+ yield queue.shift();
515
+ if (done)
516
+ break;
517
+ await new Promise((resolve) => (wake = resolve));
518
+ wake = undefined;
519
+ }
520
+ return await run;
521
+ }
522
+ const hasParts = (event) => (event.content?.parts?.length ?? 0) > 0;
523
+ /**
524
+ * One model step for the agent: its own model, and with a fallback_model
525
+ * the fallback by ADK's FallbackLlm's rules (ADR 0044), each a leaf adapter
526
+ * called by its own step (ADR 0053).
527
+ */
528
+ async function* modelStep(agent, ctx, stepOptions) {
529
+ const adapterFor = ctx.adapterFor ?? resolveAdapter;
530
+ const fallbackId = agent.fallbackModel;
531
+ if (!fallbackId) {
532
+ return yield* withPartials((onPartial) => runModelStep({ ...stepOptions, adapter: adapterFor(agent.model), onPartial }));
533
+ }
534
+ const log = ctx.log ?? ((m) => console.warn(m));
535
+ const onFallback = (why) => {
536
+ log(`[models] ${why}; answering from ${fallbackId}.`);
537
+ return withPartials((onPartial) => runModelStep({ ...stepOptions, adapter: adapterFor(fallbackId), model: fallbackId, onPartial }));
538
+ };
539
+ const provider = providerForModel(agent.model);
540
+ if (circuitOpen(provider))
541
+ return yield* onFallback(`${provider} circuit is open`);
542
+ let answered = false;
543
+ const first = yield* withPartials((onPartial) => runModelStep({
544
+ ...stepOptions,
545
+ adapter: adapterFor(agent.model),
546
+ onPartial: (event) => {
547
+ if (hasParts(event))
548
+ answered = true;
549
+ onPartial(event);
550
+ },
551
+ // FallbackLlm: a retryable failure counts against the provider, and redirects when nothing was produced.
552
+ redirect: (final, produced) => {
553
+ if (!final.error?.retryable)
554
+ return false;
555
+ recordFailure(provider);
556
+ return !produced && final.parts.length === 0;
557
+ },
558
+ }));
559
+ if (first.redirected) {
560
+ return yield* onFallback(`${agent.model} failed (${first.redirected.code}: ${first.redirected.message.slice(0, 120)})`);
561
+ }
562
+ if (first.event && hasParts(first.event))
563
+ answered = true;
564
+ if (answered && !first.error && !first.stopped)
565
+ recordSuccess(provider);
566
+ return first;
567
+ }
568
+ // ── The loop ─────────────────────────────────────────────────────────────────
569
+ /**
570
+ * Runs `agent` on the session in `ctx` until its answer is final, yielding
571
+ * every event in order: partials as they arrive (never stored), then each
572
+ * event as the store returned it. Returns how the run ended.
573
+ *
574
+ * Never throws for a failed model call or a failing tool: the first is the
575
+ * last event, stored with its error, the second the call's error response.
576
+ * An adapter that throws an Error ends the step on ADK's error event for
577
+ * it (lib/runtime/native/step.ts). Throws where the ADK runtime throws: a
578
+ * request that cannot be built, a store that throws.
579
+ */
580
+ export function runAgentLoop(agent, ctx) {
581
+ // Telemetry hook (WS2-11, lib/runtime/native/telemetry.ts): the run is an agent.invoke span.
582
+ return traceAgentInvocation(agent, ctx, () => agentLoop(agent, ctx));
583
+ }
584
+ async function* agentLoop(agent, ctx) {
585
+ const { session, sessions } = ctx;
586
+ // The run's temp: keys, read from each event before the store drops them (lib/runtime/native/tempState.ts).
587
+ const runTemp = createRunTempState();
588
+ // Task-mode hook (WS3-5): a task node's run ends on finish_task's successful answer.
589
+ const task = ctx.taskNode && agent.mode === 'task' ? taskNodeRun(agent) : undefined;
590
+ const beforeStore = (event) => {
591
+ saveOutput(agent, event);
592
+ task?.beforeStore(event);
593
+ ctx.nodeStamp?.(event);
594
+ runTemp.record(event);
595
+ };
596
+ const store = async (event) => {
597
+ beforeStore(event);
598
+ return sessions.append(session, event);
599
+ };
600
+ let steps = 0;
601
+ let lastEvent;
602
+ const selfCorrection = ctx.selfCorrection ?? new SelfCorrection();
603
+ const correction = selfCorrection.forModel(agent.name, ctx.invocationId);
604
+ for (;;) {
605
+ // Consent hook (WS6-3b, interrupts.ts): a granted credential request runs its paused call again before the step, as ADK's auth preprocessor
606
+ // does, ahead of the approval resume as ADK orders its processors. A call that asks again pauses the run on its new request.
607
+ const regranted = await resumeGrants(agent, ctx, withStateOverlay(session.state, runTemp.values()), selfCorrection);
608
+ if (regranted === 'stopped')
609
+ return { reason: 'stopped', steps, lastEvent, stop: stopOf() };
610
+ if (regranted) {
611
+ if (regranted.auth) {
612
+ lastEvent = await store(regranted.auth);
613
+ yield lastEvent;
614
+ }
615
+ const pausedOn = regranted.auth ? [...(lastEvent?.longRunningToolIds ?? [])] : [];
616
+ lastEvent = await store(regranted.response);
617
+ yield lastEvent;
618
+ if (pausedOn.length > 0)
619
+ return { reason: 'paused', steps, lastEvent, pending: pausedOn };
620
+ }
621
+ // Interrupts hook (WS2-7a, interrupts.ts): an answered approval runs its pinned call before the step, as ADK's request-confirmation processor does; it runs
622
+ // first, then compaction (ADK inserts its compactor before the contents processor), both before the step budget. ADK's request-input processor sits
623
+ // between them and resumes node-tool calls only, which no native agent lists; an answered ask_user needs none (interrupts.ts, ADR 0079).
624
+ const resumed = await resumeApprovals(agent, ctx, withStateOverlay(session.state, runTemp.values()), selfCorrection);
625
+ if (resumed === 'stopped')
626
+ return { reason: 'stopped', steps, lastEvent, stop: stopOf() };
627
+ if (resumed) {
628
+ lastEvent = await store(resumed);
629
+ yield lastEvent;
630
+ }
631
+ // Compaction hook (WS2-9, lib/runtime/native/compaction.ts): with `context:`, the summary is stored before the step reads the history,
632
+ // before the step budget, as ADK's request processors run before its call count.
633
+ const compacted = await compactBeforeStep(agent, {
634
+ session,
635
+ adapterFor: ctx.adapterFor ?? resolveAdapter,
636
+ ...(ctx.branch !== undefined ? { branch: ctx.branch } : {}),
637
+ ...(ctx.isolationScope !== undefined ? { isolationScope: ctx.isolationScope } : {}),
638
+ ...(currentTurnSignal() ? { signal: currentTurnSignal() } : {}),
639
+ });
640
+ if (compacted) {
641
+ // As ADK's Runner: a turn that stopped during the summary stores nothing and makes no step.
642
+ if (ctx.signal?.aborted || currentTurnSignal()?.aborted)
643
+ return { reason: 'stopped', steps, lastEvent, stop: stopOf() };
644
+ // A workflow node's stamp (ADR 0093): ADK's node runner stamps the summary as it does every event its agent yields, and outside
645
+ // task mode its maybeSetOutput gives it the summary as output. The outputKey and task hooks never see it, as on ADK.
646
+ ctx.nodeStamp?.(compacted);
647
+ yield await sessions.append(session, compacted);
648
+ }
649
+ if (steps >= MAX_LLM_CALLS) {
650
+ return { reason: 'stopped', steps, lastEvent, stop: { code: 'STEP_LIMIT', message: `Max number of llm calls limit of ${MAX_LLM_CALLS} exceeded` } };
651
+ }
652
+ steps += 1;
653
+ // Telemetry hook: each step is a model.call span.
654
+ const step = yield* traceModelCall(agent, ctx, (traced) => modelStep(agent, traced, { ...traced, agent, beforeAppend: beforeStore, stateOverlay: runTemp.values(), ...(correction ? { correction } : {}) }));
655
+ if (step.stopped)
656
+ return { reason: 'stopped', steps, lastEvent, stop: step.stopped };
657
+ const modelEvent = step.event;
658
+ if (!modelEvent)
659
+ return { reason: 'empty', steps, lastEvent };
660
+ yield modelEvent;
661
+ lastEvent = modelEvent;
662
+ let stepEnd = modelEvent;
663
+ const hadCalls = getFunctionCalls(modelEvent).length > 0;
664
+ if (hadCalls) {
665
+ const scope = { agent, ctx, stateBase: withStateOverlay(session.state, runTemp.values()), selfCorrection, ...(step.request.signal ? { signal: step.request.signal } : {}) };
666
+ const response = await runCalls(scope, getFunctionCalls(modelEvent), step.tools);
667
+ // As ADK: a turn that stopped while the calls ran (a long subagent run, say) stores no response.
668
+ if (scope.signal?.aborted)
669
+ return { reason: 'stopped', steps, lastEvent, stop: stopOf() };
670
+ if (response) {
671
+ const auth = authEvent(scope, response);
672
+ if (auth) {
673
+ lastEvent = await store(auth);
674
+ yield lastEvent;
675
+ }
676
+ const authIds = auth ? [...(lastEvent.longRunningToolIds ?? [])] : [];
677
+ const confirmation = confirmationEvent(scope, modelEvent, response);
678
+ if (confirmation) {
679
+ lastEvent = await store(confirmation);
680
+ yield lastEvent;
681
+ return { reason: 'paused', steps, lastEvent, pending: [...(lastEvent.longRunningToolIds ?? [])] };
682
+ }
683
+ lastEvent = await store(response);
684
+ yield lastEvent;
685
+ // As ADK: the response asking for auth is final, and the run waits on its credential request (ADR 0085).
686
+ if (authIds.length > 0)
687
+ return { reason: 'paused', steps, lastEvent, pending: authIds };
688
+ stepEnd = lastEvent;
689
+ if (task?.finished)
690
+ return { reason: 'final', steps, lastEvent, output: task.output };
691
+ }
692
+ }
693
+ // ADK's runAsyncImpl: stop on a final event, unless it is an empty metadata event after tool calls.
694
+ const emptyMetadata = stepEnd.author === agent.name && !stepEnd.partial && !hasParts(stepEnd) && isDefaultActions(stepEnd.actions);
695
+ if (isFinal(stepEnd) && !(emptyMetadata && hadCalls)) {
696
+ if (stepEnd.errorCode && stepEnd.errorCode !== 'STOP')
697
+ return { reason: 'error', steps, lastEvent };
698
+ const answered = new Set(getFunctionResponses(stepEnd).map((r) => r.id));
699
+ const pending = (modelEvent.longRunningToolIds ?? []).filter((id) => !answered.has(id));
700
+ if (pending.length > 0)
701
+ return { reason: 'paused', steps, lastEvent, pending };
702
+ return { reason: 'final', steps, lastEvent };
703
+ }
704
+ }
705
+ }