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,441 @@
1
+ /**
2
+ * lib/workflow/resume.ts — a paused workflow resumes on the engine's own
3
+ * scheduler, as ADK 2.2 resumes it (ADR 0094).
4
+ *
5
+ * A walk that paused on an `ask_user` node (lib/workflow/pause.ts, ADR 0092)
6
+ * ended the turn `input-required`. The next message answers it. ADK keeps no
7
+ * workflow state between the two turns: it rebuilds every node's state from
8
+ * the session's events (workflow/utils/rehydration_utils.js) and walks the
9
+ * graph again from START. This module is that rebuild, with no ADK import,
10
+ * function for function:
11
+ *
12
+ * 1. THE RUN'S EVENTS (eventsForCurrentRun). The events of the current
13
+ * invocation, plus those of every paused run immediately before it, so
14
+ * a run paused twice resumes with both halves.
15
+ * 2. THE ANSWERS. A plain-text message answers the one open interrupt
16
+ * (run_node_as_invocation.js resumeInputsFromPlainText), and a
17
+ * function response named for an interrupt answers it explicitly
18
+ * (resolvedInterruptResponses: `{ result: x }` unwrapped, a structured
19
+ * reply checked against the request's `response_schema`, a reply to an
20
+ * unknown or already-answered interrupt refused with ADK's message).
21
+ * Together they are ADK's `ctx.resumeInputs`.
22
+ * 3. THE NODE STATES (reconstructNodeRuns). Per direct child of the
23
+ * workflow, its runs in order: the output, route and branch an event
24
+ * carried, the interrupts it raised, the answers to them, and the
25
+ * input recorded in `actions.agentState.input` (pause.ts writes it).
26
+ * 4. THE WALK'S INPUT (extractNodeInput): the message's text, else the
27
+ * message itself, as ADK hands its root workflow.
28
+ *
29
+ * `workflowResume` puts the four together; the scheduler
30
+ * (lib/workflow/scheduler.ts, `RunWorkflowOptions.resume`) then treats a
31
+ * node whose prior run has an output and no open interrupt as done
32
+ * (`isFastForwardable`), reruns a paused node that reruns on resume
33
+ * (`rerunsOnResume`) on its recorded input with the answers in
34
+ * `NodeRun.resumeInputs`, and walks on. An ask_user node rerun with an
35
+ * answer outputs `{ reply, input }` (pause.ts), as the FunctionNode
36
+ * lib/workflow.ts compiles does.
37
+ *
38
+ * TRUST: the answers are the person's words and the stored events are the
39
+ * session's; both are data the walk carries, never instructions this module
40
+ * acts on. A function call in an event the user authored raises nothing:
41
+ * only a node's event opens an interrupt. No regular expression reads input.
42
+ */
43
+ import { z } from 'zod';
44
+ import { APPROVAL_REQUEST } from '../runtime/approvals.js';
45
+ import { CREDENTIAL_REQUEST } from '../runtime/credentials.js';
46
+ import { INPUT_REQUEST } from '../workflowConfig.js';
47
+ import { RESPONSE_SCHEMA_ARG } from './pause.js';
48
+ /** The key ADK unwraps a bare reply from: `{ result: <value> }`. */
49
+ const RESULT_KEY = 'result';
50
+ const partsOf = (event) => event.content?.parts ?? [];
51
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
52
+ const stringOr = (value) => (typeof value === 'string' ? value : undefined);
53
+ // ── 1. The run's events ──────────────────────────────────────────────────────
54
+ /**
55
+ * ADK's raisedInterrupt (requiresUserInput): the event carries an input,
56
+ * credential or confirmation request with an interrupt id.
57
+ */
58
+ export function raisedInterrupt(event) {
59
+ for (const part of partsOf(event)) {
60
+ const call = part.functionCall;
61
+ if (!call?.name)
62
+ continue;
63
+ if (call.name !== INPUT_REQUEST && call.name !== CREDENTIAL_REQUEST && call.name !== APPROVAL_REQUEST)
64
+ continue;
65
+ const args = call.args ?? {};
66
+ const credential = call.name === CREDENTIAL_REQUEST;
67
+ // ADK camel-cases a credential request's keys before it reads functionCallId.
68
+ const id = call.id ?? stringOr(args.interruptId) ?? stringOr(args.functionCallId) ?? (credential ? stringOr(args.function_call_id) : undefined);
69
+ if (id)
70
+ return true;
71
+ }
72
+ return false;
73
+ }
74
+ /**
75
+ * ADK's eventsForCurrentRun: the events from the start of the current
76
+ * invocation, extended back over every run just before it that paused (an
77
+ * event without an invocation id belongs to the one before it; events
78
+ * without a node path neither start nor pause a run).
79
+ */
80
+ export function eventsForCurrentRun(events, currentInvocationId) {
81
+ const priorRuns = [];
82
+ let currentStart = events.length;
83
+ let lastInvocationId;
84
+ for (let i = 0; i < events.length; i++) {
85
+ const event = events[i];
86
+ const invocationId = event.invocationId || lastInvocationId;
87
+ if (event.invocationId)
88
+ lastInvocationId = event.invocationId;
89
+ if (invocationId === currentInvocationId) {
90
+ if (currentStart === events.length)
91
+ currentStart = i;
92
+ continue;
93
+ }
94
+ if (!event.nodeInfo?.path)
95
+ continue;
96
+ let entry = priorRuns[priorRuns.length - 1];
97
+ if (!entry || entry.invocationId !== invocationId) {
98
+ entry = { start: i, invocationId, paused: false };
99
+ priorRuns.push(entry);
100
+ }
101
+ if (raisedInterrupt(event))
102
+ entry.paused = true;
103
+ }
104
+ let boundary = currentStart;
105
+ for (let i = priorRuns.length - 1; i >= 0 && priorRuns[i].paused; i--)
106
+ boundary = priorRuns[i].start;
107
+ return events.slice(boundary);
108
+ }
109
+ // ── 2. The answers ───────────────────────────────────────────────────────────
110
+ /** ADK's responseSchemasByInterruptId: the schema each input request declared. */
111
+ export function responseSchemasByInterruptId(events) {
112
+ const schemas = new Map();
113
+ for (const event of events) {
114
+ for (const part of partsOf(event)) {
115
+ const call = part.functionCall;
116
+ if (call?.name !== INPUT_REQUEST || !call.id)
117
+ continue;
118
+ const schema = call.args?.[RESPONSE_SCHEMA_ARG];
119
+ if (schema)
120
+ schemas.set(call.id, schema);
121
+ }
122
+ }
123
+ return schemas;
124
+ }
125
+ /** ADK's acceptsString: whether a JSON Schema admits a string. */
126
+ function acceptsString(schema) {
127
+ if (!isRecord(schema))
128
+ return false;
129
+ const type = schema.type;
130
+ if (type === 'string')
131
+ return true;
132
+ if (Array.isArray(type) && type.includes('string'))
133
+ return true;
134
+ for (const key of ['anyOf', 'oneOf']) {
135
+ const branches = schema[key];
136
+ if (Array.isArray(branches) && branches.some(acceptsString))
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ /** ADK's unwrapResponse: `{ result: x }` is x, a JSON string parsed unless the schema takes a string. */
142
+ export function unwrapResponse(response, responseSchema) {
143
+ if (isRecord(response) && Object.keys(response).length === 1 && RESULT_KEY in response) {
144
+ const value = response[RESULT_KEY];
145
+ if (typeof value !== 'string' || acceptsString(responseSchema))
146
+ return value;
147
+ try {
148
+ return JSON.parse(value);
149
+ }
150
+ catch {
151
+ return value;
152
+ }
153
+ }
154
+ return response;
155
+ }
156
+ /**
157
+ * ADK's interruptResponseMismatch: a structured reply (an object) that the
158
+ * request's schema refuses, as ADK's message; a scalar reply, or a schema
159
+ * zod cannot read, is not checked.
160
+ */
161
+ export function interruptResponseMismatch(interruptId, value, jsonSchema) {
162
+ if (typeof value !== 'object' || value === null)
163
+ return undefined;
164
+ if (jsonSchema === null || typeof jsonSchema !== 'object')
165
+ return undefined;
166
+ let validator;
167
+ try {
168
+ validator = z.fromJSONSchema(jsonSchema);
169
+ }
170
+ catch {
171
+ return undefined;
172
+ }
173
+ const result = validator.safeParse(value);
174
+ if (result.success)
175
+ return undefined;
176
+ const issues = result.error.issues
177
+ .map((issue) => `${issue.message}${issue.path.length ? ` at '${issue.path.join('.')}'` : ''}`)
178
+ .join('; ');
179
+ return `The reply to interrupt '${interruptId}' does not match the responseSchema it declared: ${issues}. A structured reply must either match that schema, or wrap a bare value as {result: <value>}; a plain-text reply is accepted as-is and is not checked. The interrupt is still waiting, so you can answer it again.`;
180
+ }
181
+ const waitingList = (open) => open.size ? `Still waiting: ${[...open].map((id) => `'${id}'`).join(', ')}.` : 'This run has no interrupt waiting for an answer.';
182
+ function lastUserEvent(events) {
183
+ for (let i = events.length - 1; i >= 0; i--)
184
+ if (events[i].author === 'user')
185
+ return events[i];
186
+ return undefined;
187
+ }
188
+ /**
189
+ * ADK's resolvedInterruptResponses: every interrupt a function response in
190
+ * a user event answered, with the answer unwrapped. The newest user turn is
191
+ * held to account: a reply there to an interrupt that is not open, or one
192
+ * its schema refuses, throws ADK's message; an older one is skipped.
193
+ */
194
+ export function resolvedInterruptResponses(events) {
195
+ const responseSchemas = responseSchemasByInterruptId(events);
196
+ const newestUserTurn = lastUserEvent(events);
197
+ const raised = new Set();
198
+ const open = new Set();
199
+ const resolved = new Map();
200
+ for (const event of events) {
201
+ if (event.author === 'user' && event.content?.parts) {
202
+ for (const part of event.content.parts) {
203
+ const response = part.functionResponse;
204
+ if (!response?.id)
205
+ continue;
206
+ if (!open.has(response.id)) {
207
+ if (response.name === INPUT_REQUEST && event === newestUserTurn) {
208
+ throw new Error(raised.has(response.id)
209
+ ? `Interrupt '${response.id}' has already been answered, so this reply resolves nothing. Sending it again does not change the earlier answer, and it would otherwise be served as a new message, restarting the node that asked. ${waitingList(open)}`
210
+ : `The reply carries interrupt id '${response.id}', which does not match any interrupt this run raised. ${waitingList(open)} A reply must use the id from the 'adk_request_input' function call it answers. Nothing was resolved, so any waiting interrupt can still be answered.`);
211
+ }
212
+ continue;
213
+ }
214
+ const schema = responseSchemas.get(response.id);
215
+ const value = unwrapResponse(response.response, schema);
216
+ const mismatch = interruptResponseMismatch(response.id, value, schema);
217
+ if (mismatch) {
218
+ if (event === newestUserTurn)
219
+ throw new Error(mismatch);
220
+ continue;
221
+ }
222
+ open.delete(response.id);
223
+ resolved.set(response.id, value);
224
+ }
225
+ continue;
226
+ }
227
+ for (const id of event.longRunningToolIds ?? []) {
228
+ raised.add(id);
229
+ open.add(id);
230
+ }
231
+ }
232
+ return resolved;
233
+ }
234
+ // ── 3. The node states ───────────────────────────────────────────────────────
235
+ /** ADK's nodeNameFromPath: the last segment of a path, without its `@<run>` suffix. */
236
+ export function nodeNameFromPath(path) {
237
+ let start = 0;
238
+ for (let i = 0; i < path.length; i++)
239
+ if (path[i] === '.' || path[i] === '/')
240
+ start = i + 1;
241
+ const leaf = path.slice(start);
242
+ const at = leaf.indexOf('@');
243
+ return at === -1 ? leaf : leaf.slice(0, at);
244
+ }
245
+ /** ADK's directChildName: the child of `parentPath` an event's path names, or undefined for any other path. */
246
+ export function directChildName(path, parentPath) {
247
+ const prefix = `${parentPath}.`;
248
+ if (!path.startsWith(prefix))
249
+ return undefined;
250
+ const rest = path.slice(prefix.length);
251
+ if (rest.includes('.'))
252
+ return undefined;
253
+ const at = rest.indexOf('@');
254
+ return at === -1 ? rest : rest.slice(0, at);
255
+ }
256
+ const isRunClosed = (run) => run.output !== undefined || run.route !== undefined || run.interruptIds.size > 0;
257
+ function keyFn(parentPath) {
258
+ if (parentPath)
259
+ return (event) => (event.nodeInfo?.path ? directChildName(event.nodeInfo.path, parentPath) : undefined);
260
+ return (event) => (event.nodeInfo?.path ? nodeNameFromPath(event.nodeInfo.path) : event.author);
261
+ }
262
+ /** ADK's reconstructRuns: each key's runs, a run closing on an output, a route or an interrupt. */
263
+ function reconstructRuns(events, keyFor) {
264
+ const nodes = new Map();
265
+ const interruptOwner = new Map();
266
+ const resolved = resolvedInterruptResponses(events);
267
+ const currentRun = (name) => {
268
+ let runs = nodes.get(name);
269
+ if (!runs) {
270
+ runs = [];
271
+ nodes.set(name, runs);
272
+ }
273
+ const open = runs[runs.length - 1];
274
+ if (open && !isRunClosed(open))
275
+ return open;
276
+ const run = { interruptIds: new Set(), resolvedResponses: new Map() };
277
+ runs.push(run);
278
+ return run;
279
+ };
280
+ for (const event of events) {
281
+ if (event.author === 'user' && event.content?.parts) {
282
+ for (const part of event.content.parts) {
283
+ const id = part.functionResponse?.id;
284
+ if (id && interruptOwner.has(id) && resolved.has(id))
285
+ interruptOwner.get(id).resolvedResponses.set(id, resolved.get(id));
286
+ }
287
+ continue;
288
+ }
289
+ const key = keyFor(event);
290
+ if (!key)
291
+ continue;
292
+ const run = currentRun(key);
293
+ if (event.output !== undefined) {
294
+ run.output = event.output;
295
+ run.branch = event.branch;
296
+ }
297
+ if (event.route !== undefined)
298
+ run.route = event.route;
299
+ for (const id of event.longRunningToolIds ?? []) {
300
+ run.interruptIds.add(id);
301
+ interruptOwner.set(id, run);
302
+ }
303
+ const agentState = event.actions?.agentState;
304
+ if (isRecord(agentState) && 'input' in agentState)
305
+ run.input = agentState.input;
306
+ }
307
+ return nodes;
308
+ }
309
+ /** ADK's reconstructNodeRuns: per direct child of `parentPath` (or per node name at the root), its runs in order. */
310
+ export function reconstructNodeRuns(events, parentPath) {
311
+ return reconstructRuns(events, keyFn(parentPath));
312
+ }
313
+ /** ADK's reconstructNodeStates: each key's runs merged into one state. */
314
+ export function reconstructNodeStates(events, parentPath) {
315
+ const merged = new Map();
316
+ for (const [key, runs] of reconstructRuns(events, keyFn(parentPath))) {
317
+ const node = { interruptIds: new Set(), resolvedResponses: new Map() };
318
+ for (const run of runs) {
319
+ if (run.output !== undefined) {
320
+ node.output = run.output;
321
+ node.branch = run.branch;
322
+ }
323
+ if (run.route !== undefined)
324
+ node.route = run.route;
325
+ if (run.input !== undefined)
326
+ node.input = run.input;
327
+ for (const id of run.interruptIds)
328
+ node.interruptIds.add(id);
329
+ for (const [id, value] of run.resolvedResponses)
330
+ node.resolvedResponses.set(id, value);
331
+ }
332
+ merged.set(key, node);
333
+ }
334
+ return merged;
335
+ }
336
+ /** ADK's isFastForwardable: the run produced an output or a route, and every interrupt it raised is answered. */
337
+ export function isFastForwardable(run) {
338
+ if (run.output === undefined && run.route === undefined)
339
+ return false;
340
+ for (const id of run.interruptIds)
341
+ if (!run.resolvedResponses.has(id))
342
+ return false;
343
+ return true;
344
+ }
345
+ /**
346
+ * ADK's rerunOnResume, by node kind as lib/workflow.ts compiles them: an
347
+ * agent (LlmAgent), an ask_user node (its FunctionNode sets it) and a map
348
+ * (ParallelWorker) run again on resume; a tool node, a join and a route
349
+ * step (BaseNode's default) do not, and resolve to their answers instead.
350
+ */
351
+ export function rerunsOnResume(node) {
352
+ return node.kind === 'agent' || node.kind === 'ask_user' || node.kind === 'map';
353
+ }
354
+ // ── 4. The walk's input, and the plain-text answer ───────────────────────────
355
+ /** ADK's extractNodeInput: the message's text parts joined, else the message itself. */
356
+ export function workflowNodeInput(content) {
357
+ if (!content)
358
+ return undefined;
359
+ const texts = (content.parts ?? []).filter((p) => typeof p.text === 'string');
360
+ if (texts.length > 0)
361
+ return texts.map((p) => p.text).join('');
362
+ return content;
363
+ }
364
+ /**
365
+ * ADK's resumeInputsFromPlainText: a message of text parts only answers the
366
+ * one interrupt left open in the run's events; with none or several open,
367
+ * it answers nothing (it is the walk's new input).
368
+ */
369
+ export function resumeInputsFromPlainText(content, events, invocationId) {
370
+ const parts = content?.parts ?? [];
371
+ if (!(parts.length > 0 && parts.every((p) => typeof p.text === 'string')))
372
+ return {};
373
+ const text = parts.map((p) => p.text).join('');
374
+ const pending = new Set();
375
+ for (const state of reconstructNodeStates(eventsForCurrentRun(events, invocationId)).values()) {
376
+ for (const id of state.interruptIds)
377
+ if (!state.resolvedResponses.has(id))
378
+ pending.add(id);
379
+ }
380
+ if (pending.size !== 1)
381
+ return {};
382
+ const [id] = pending;
383
+ return { [id]: text };
384
+ }
385
+ // ── What the native walk cannot resume yet ───────────────────────────────────
386
+ /**
387
+ * A pause the native walk cannot resume: one raised inside an agent node
388
+ * other than an approval (an `ask_user` tool call, a credential request) or
389
+ * inside a map item. ADK resumes those inside the node (the agent's own
390
+ * history, the ParallelWorker's item); the native agent node pauses only on
391
+ * an approval (lib/workflow/agentNode.ts, ADR 0098), so it cannot pick the
392
+ * others up. Resuming the walk anyway would run the node again from its
393
+ * input and ask again, so the resume refuses by name instead.
394
+ */
395
+ export class UnsupportedWorkflowResumeError extends Error {
396
+ /** The node path the pause was raised at. */
397
+ nodePath;
398
+ constructor(nodePath, why) {
399
+ super(`Cannot resume the workflow on the native runtime: the pause at '${nodePath}' was raised ${why}, which only an ask_user node's pause or an agent node's approval supports here.`);
400
+ this.name = 'UnsupportedWorkflowResumeError';
401
+ this.nodePath = nodePath;
402
+ }
403
+ }
404
+ /**
405
+ * Throws UnsupportedWorkflowResumeError for the first interrupt in the run's
406
+ * events that is neither an ask_user node's input request nor an agent
407
+ * node's approval request at a direct child of the workflow (the workflow's
408
+ * own record aside).
409
+ */
410
+ export function assertResumable(runEvents, workflowPath) {
411
+ for (const event of runEvents) {
412
+ if (event.author === 'user' || !(event.longRunningToolIds?.length))
413
+ continue;
414
+ const path = event.nodeInfo?.path ?? '';
415
+ if (path === workflowPath)
416
+ continue;
417
+ if (directChildName(path, workflowPath) === undefined)
418
+ throw new UnsupportedWorkflowResumeError(path || String(event.author ?? ''), 'inside a nested node (a map item)');
419
+ const resumable = partsOf(event).some((p) => p.functionCall?.name === INPUT_REQUEST || p.functionCall?.name === APPROVAL_REQUEST);
420
+ if (!resumable)
421
+ throw new UnsupportedWorkflowResumeError(path, 'inside an agent node');
422
+ }
423
+ }
424
+ /**
425
+ * Everything a walk needs to resume as ADK's does: the new message as the
426
+ * walk's input, the prior runs of each node of the workflow, and the
427
+ * answers. On a session with nothing paused, every node runs fresh (its
428
+ * prior runs belong to finished invocations, which the run's events leave
429
+ * out). Throws ADK's message for a reply that answers nothing open, and
430
+ * UnsupportedWorkflowResumeError for a pause only ADK can resume.
431
+ */
432
+ export function workflowResume(request) {
433
+ const resumeInputs = resumeInputsFromPlainText(request.userContent, request.events, request.invocationId);
434
+ const runEvents = eventsForCurrentRun(request.events, request.invocationId);
435
+ assertResumable(runEvents, request.workflowPath);
436
+ const priorRuns = reconstructNodeRuns(runEvents, request.workflowPath || undefined);
437
+ // ADK's applyResumeInputs: the explicit answers, over the plain-text one.
438
+ for (const [id, value] of resolvedInterruptResponses(runEvents))
439
+ resumeInputs[id] = value;
440
+ return { input: workflowNodeInput(request.userContent), resume: { priorRuns, resumeInputs } };
441
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * lib/workflow/route.ts — the route a workflow node's output takes, and the
3
+ * event a route step stores (ADR 0030, ADR 0090).
4
+ *
5
+ * ADK's TypeScript port never derives a route from an agent's output, so a
6
+ * routing map after a node gets a hidden step, `<Node>__route`, that reads
7
+ * the route from the output and re-emits the output with it (lib/workflow.ts
8
+ * compiles it as a FunctionNode; lib/workflow/scheduler.ts runs it itself).
9
+ * The rule is one function both runtimes call, `routeOf`:
10
+ *
11
+ * - an object output (an agent with an output schema, a join, a tool's
12
+ * result): its `route_key` property, `route` by default, trimmed, `''`
13
+ * when the property is absent or null;
14
+ * - a string output: the text, trimmed. A JSON object an agent WITHOUT an
15
+ * output schema writes is text, as ADK keeps it (run_llm_agent_as_node's
16
+ * maybeSetOutput parses only with a schema), so it routes on its whole
17
+ * text, which usually takes the `default` edge;
18
+ * - anything else: its string form, trimmed; `''` for undefined or null.
19
+ *
20
+ * The scheduler then matches the route against the keys in ADK's spelling
21
+ * and takes the `default` edge when no key matched (scheduler.ts nextNodes),
22
+ * so the default catches every route no key names, `''` included.
23
+ *
24
+ * `routeStepEvent` is the event ADK's route step stores, so a session the
25
+ * native walk writes holds what ADK's holds: authored by the step, the
26
+ * output and the route, the step's node path, no content.
27
+ *
28
+ * No ADK import: the ADK path (lib/workflow.ts) imports `routeOf` from here
29
+ * through lib/workflowConfig.ts.
30
+ */
31
+ import type { TurnEvent } from '../runtime/events.ts';
32
+ /** The route_key a routing map reads when the node sets none. */
33
+ export declare const DEFAULT_ROUTE_PROPERTY = "route";
34
+ /**
35
+ * The route a node's output takes: the `routeKey` property of an object
36
+ * output, else the trimmed text, else `''`. Linear in the output's size; no
37
+ * regular expression.
38
+ */
39
+ export declare function routeOf(output: unknown, routeKey?: string): string;
40
+ /** Where a route step ran: the scheduler's node_end for it. */
41
+ export interface RouteStepRun {
42
+ /** The step's name, `<Node>__route`. */
43
+ name: string;
44
+ /** Its node path, `<workflow>.<Node>__route`. */
45
+ path: string;
46
+ branch: string | undefined;
47
+ invocationId: string;
48
+ /** The output it re-emits: its predecessor's. */
49
+ output: unknown;
50
+ /** The route it emitted (routeOf). */
51
+ route: unknown;
52
+ }
53
+ /**
54
+ * The event ADK stores for a route step: its FunctionNode's createEvent
55
+ * (author, invocation, branch, output, route), stamped by the node runner
56
+ * with the step's path and `outputFor` (node_runner.js enrichEvent).
57
+ */
58
+ export declare function routeStepEvent(run: RouteStepRun): TurnEvent;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * lib/workflow/route.ts — the route a workflow node's output takes, and the
3
+ * event a route step stores (ADR 0030, ADR 0090).
4
+ *
5
+ * ADK's TypeScript port never derives a route from an agent's output, so a
6
+ * routing map after a node gets a hidden step, `<Node>__route`, that reads
7
+ * the route from the output and re-emits the output with it (lib/workflow.ts
8
+ * compiles it as a FunctionNode; lib/workflow/scheduler.ts runs it itself).
9
+ * The rule is one function both runtimes call, `routeOf`:
10
+ *
11
+ * - an object output (an agent with an output schema, a join, a tool's
12
+ * result): its `route_key` property, `route` by default, trimmed, `''`
13
+ * when the property is absent or null;
14
+ * - a string output: the text, trimmed. A JSON object an agent WITHOUT an
15
+ * output schema writes is text, as ADK keeps it (run_llm_agent_as_node's
16
+ * maybeSetOutput parses only with a schema), so it routes on its whole
17
+ * text, which usually takes the `default` edge;
18
+ * - anything else: its string form, trimmed; `''` for undefined or null.
19
+ *
20
+ * The scheduler then matches the route against the keys in ADK's spelling
21
+ * and takes the `default` edge when no key matched (scheduler.ts nextNodes),
22
+ * so the default catches every route no key names, `''` included.
23
+ *
24
+ * `routeStepEvent` is the event ADK's route step stores, so a session the
25
+ * native walk writes holds what ADK's holds: authored by the step, the
26
+ * output and the route, the step's node path, no content.
27
+ *
28
+ * No ADK import: the ADK path (lib/workflow.ts) imports `routeOf` from here
29
+ * through lib/workflowConfig.ts.
30
+ */
31
+ import { createTurnEvent } from '../runtime/events.js';
32
+ /** The route_key a routing map reads when the node sets none. */
33
+ export const DEFAULT_ROUTE_PROPERTY = 'route';
34
+ /**
35
+ * The route a node's output takes: the `routeKey` property of an object
36
+ * output, else the trimmed text, else `''`. Linear in the output's size; no
37
+ * regular expression.
38
+ */
39
+ export function routeOf(output, routeKey = DEFAULT_ROUTE_PROPERTY) {
40
+ if (output && typeof output === 'object' && !Array.isArray(output)) {
41
+ const value = output[routeKey];
42
+ if (value === undefined || value === null)
43
+ return '';
44
+ return String(value).trim();
45
+ }
46
+ if (typeof output === 'string')
47
+ return output.trim();
48
+ return output === undefined || output === null ? '' : String(output).trim();
49
+ }
50
+ /**
51
+ * The event ADK stores for a route step: its FunctionNode's createEvent
52
+ * (author, invocation, branch, output, route), stamped by the node runner
53
+ * with the step's path and `outputFor` (node_runner.js enrichEvent).
54
+ */
55
+ export function routeStepEvent(run) {
56
+ const event = createTurnEvent({
57
+ author: run.name,
58
+ invocationId: run.invocationId,
59
+ ...(run.branch !== undefined ? { branch: run.branch } : {}),
60
+ });
61
+ if (run.output !== undefined)
62
+ event.output = run.output;
63
+ if (run.route !== undefined)
64
+ event.route = run.route;
65
+ // What enrichNodeEvent (lib/workflow/toolNode.ts) writes on this event, kept here so this module stays a leaf:
66
+ // lib/workflowConfig.ts re-exports routeOf, and the schema and the ADK path import it.
67
+ event.nodeInfo = { path: run.path, ...(run.output !== undefined ? { outputFor: [run.path] } : {}) };
68
+ return event;
69
+ }