melchizedek-agents 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/.env.example +12 -0
  2. package/CHANGELOG.md +825 -0
  3. package/DOCUMENTATION.md +217 -59
  4. package/README.md +42 -1
  5. package/config/agents/examples/harness.yaml +3 -1
  6. package/config/agents/examples/model_zoo.yaml +10 -9
  7. package/config/agents/syndicateSchema.yaml +50 -16
  8. package/config/agents/templates/account_memory.yaml +2 -6
  9. package/config/agents/templates/case_desk.yaml +3 -9
  10. package/config/agents/templates/draft_review.yaml +3 -9
  11. package/config/agents/templates/intake_extractor.yaml +1 -3
  12. package/config/agents/templates/research_brief.yaml +3 -9
  13. package/config/agents/templates/research_desk.yaml +3 -9
  14. package/config/agents/templates/review_panel.yaml +4 -12
  15. package/config/agents/templates/support_triage.yaml +4 -12
  16. package/config/agents/templates/systems_operator.yaml +2 -6
  17. package/db/hardening.sql +7 -1
  18. package/db/migrations/0013_tool_credentials.sql +260 -0
  19. package/dist/lib/a2a/app.d.ts +70 -6
  20. package/dist/lib/a2a/app.js +107 -2
  21. package/dist/lib/a2a/executor.d.ts +20 -3
  22. package/dist/lib/a2a/executor.js +66 -7
  23. package/dist/lib/a2a/remoteAgent.d.ts +24 -9
  24. package/dist/lib/a2a/remoteAgent.js +31 -14
  25. package/dist/lib/compile.d.ts +121 -30
  26. package/dist/lib/compile.js +137 -124
  27. package/dist/lib/compileAdk.d.ts +29 -0
  28. package/dist/lib/compileAdk.js +132 -0
  29. package/dist/lib/compileNative.d.ts +85 -0
  30. package/dist/lib/compileNative.js +222 -0
  31. package/dist/lib/config.d.ts +6 -5
  32. package/dist/lib/config.js +6 -5
  33. package/dist/lib/doctor.js +1 -0
  34. package/dist/lib/index.d.ts +2 -1
  35. package/dist/lib/index.js +2 -1
  36. package/dist/lib/loadSyndicate.d.ts +18 -0
  37. package/dist/lib/memory/erase.d.ts +5 -3
  38. package/dist/lib/memory/erase.js +5 -2
  39. package/dist/lib/memory/namespace.d.ts +8 -1
  40. package/dist/lib/memory/namespace.js +12 -6
  41. package/dist/lib/memory/providers.js +2 -1
  42. package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
  43. package/dist/lib/memory/supabaseMemoryService.js +42 -2
  44. package/dist/lib/model.d.ts +47 -0
  45. package/dist/lib/model.js +37 -0
  46. package/dist/lib/models/adapterResolver.d.ts +113 -0
  47. package/dist/lib/models/adapterResolver.js +136 -0
  48. package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
  49. package/dist/lib/models/adkGeminiAdapter.js +367 -0
  50. package/dist/lib/models/adkShim.d.ts +111 -0
  51. package/dist/lib/models/adkShim.js +136 -0
  52. package/dist/lib/models/capabilities.d.ts +3 -0
  53. package/dist/lib/models/capabilities.js +20 -13
  54. package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
  55. package/dist/lib/models/chatCompletionsAdapter.js +854 -0
  56. package/dist/lib/models/circuitBreaker.d.ts +42 -0
  57. package/dist/lib/models/circuitBreaker.js +70 -0
  58. package/dist/lib/models/claudeAdapter.d.ts +103 -0
  59. package/dist/lib/models/claudeAdapter.js +669 -0
  60. package/dist/lib/models/claudeLlm.d.ts +39 -62
  61. package/dist/lib/models/claudeLlm.js +44 -349
  62. package/dist/lib/models/claudeModels.d.ts +127 -0
  63. package/dist/lib/models/claudeModels.js +237 -0
  64. package/dist/lib/models/contract.d.ts +395 -0
  65. package/dist/lib/models/contract.js +39 -0
  66. package/dist/lib/models/endpoints.js +2 -1
  67. package/dist/lib/models/errorResponse.d.ts +50 -0
  68. package/dist/lib/models/errorResponse.js +48 -0
  69. package/dist/lib/models/fallback.d.ts +17 -10
  70. package/dist/lib/models/fallback.js +53 -42
  71. package/dist/lib/models/fallbackAdapter.d.ts +55 -0
  72. package/dist/lib/models/fallbackAdapter.js +62 -0
  73. package/dist/lib/models/gateway.js +2 -1
  74. package/dist/lib/models/gatewayAdapter.d.ts +51 -0
  75. package/dist/lib/models/gatewayAdapter.js +81 -0
  76. package/dist/lib/models/gatewayLlm.d.ts +12 -33
  77. package/dist/lib/models/gatewayLlm.js +14 -75
  78. package/dist/lib/models/geminiAdapter.d.ts +123 -0
  79. package/dist/lib/models/geminiAdapter.js +831 -0
  80. package/dist/lib/models/geminiState.d.ts +25 -0
  81. package/dist/lib/models/geminiState.js +25 -0
  82. package/dist/lib/models/genaiMapping.d.ts +263 -0
  83. package/dist/lib/models/genaiMapping.js +862 -0
  84. package/dist/lib/models/gptAdapter.d.ts +240 -0
  85. package/dist/lib/models/gptAdapter.js +804 -0
  86. package/dist/lib/models/gptLlm.d.ts +63 -123
  87. package/dist/lib/models/gptLlm.js +88 -602
  88. package/dist/lib/models/grokAdapter.d.ts +86 -0
  89. package/dist/lib/models/grokAdapter.js +142 -0
  90. package/dist/lib/models/grokLlm.d.ts +22 -45
  91. package/dist/lib/models/grokLlm.js +22 -67
  92. package/dist/lib/models/kimiAdapter.d.ts +157 -0
  93. package/dist/lib/models/kimiAdapter.js +203 -0
  94. package/dist/lib/models/kimiLlm.d.ts +15 -107
  95. package/dist/lib/models/kimiLlm.js +17 -153
  96. package/dist/lib/models/ollamaAdapter.d.ts +125 -0
  97. package/dist/lib/models/ollamaAdapter.js +155 -0
  98. package/dist/lib/models/ollamaLlm.d.ts +11 -107
  99. package/dist/lib/models/ollamaLlm.js +13 -151
  100. package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
  101. package/dist/lib/models/openAiCompatibleLlm.js +109 -671
  102. package/dist/lib/models/providerState.d.ts +63 -0
  103. package/dist/lib/models/providerState.js +61 -0
  104. package/dist/lib/models/reasoning.d.ts +26 -0
  105. package/dist/lib/models/reasoning.js +59 -0
  106. package/dist/lib/models/registry.d.ts +58 -35
  107. package/dist/lib/models/registry.js +89 -118
  108. package/dist/lib/models/retry.d.ts +2 -1
  109. package/dist/lib/models/retry.js +3 -2
  110. package/dist/lib/models/schemaNormalize.d.ts +98 -0
  111. package/dist/lib/models/schemaNormalize.js +321 -1
  112. package/dist/lib/models/tracedGemini.d.ts +41 -0
  113. package/dist/lib/models/tracedGemini.js +98 -0
  114. package/dist/lib/models/urls.d.ts +11 -0
  115. package/dist/lib/models/urls.js +16 -0
  116. package/dist/lib/net/redirects.d.ts +21 -2
  117. package/dist/lib/net/redirects.js +11 -34
  118. package/dist/lib/observability/audit.d.ts +5 -1
  119. package/dist/lib/observability/lineage.d.ts +14 -0
  120. package/dist/lib/observability/lineage.js +33 -0
  121. package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
  122. package/dist/lib/observability/supabaseSpanExporter.js +17 -9
  123. package/dist/lib/observability/tracer.d.ts +26 -8
  124. package/dist/lib/observability/tracer.js +43 -18
  125. package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
  126. package/dist/lib/runtime/adkMemoryBridge.js +68 -0
  127. package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
  128. package/dist/lib/runtime/adkSessionBridge.js +176 -0
  129. package/dist/lib/runtime/credentials.d.ts +59 -0
  130. package/dist/lib/runtime/credentials.js +62 -0
  131. package/dist/lib/runtime/events.d.ts +229 -0
  132. package/dist/lib/runtime/events.js +274 -0
  133. package/dist/lib/runtime/logging.d.ts +32 -0
  134. package/dist/lib/runtime/logging.js +55 -0
  135. package/dist/lib/runtime/memoryService.d.ts +72 -0
  136. package/dist/lib/runtime/memoryService.js +25 -0
  137. package/dist/lib/runtime/native/agentLoop.d.ts +203 -0
  138. package/dist/lib/runtime/native/agentLoop.js +687 -0
  139. package/dist/lib/runtime/native/compaction.d.ts +103 -0
  140. package/dist/lib/runtime/native/compaction.js +191 -0
  141. package/dist/lib/runtime/native/delegate.d.ts +98 -0
  142. package/dist/lib/runtime/native/delegate.js +173 -0
  143. package/dist/lib/runtime/native/history.d.ts +83 -0
  144. package/dist/lib/runtime/native/history.js +312 -0
  145. package/dist/lib/runtime/native/interrupts.d.ts +126 -0
  146. package/dist/lib/runtime/native/interrupts.js +291 -0
  147. package/dist/lib/runtime/native/request.d.ts +215 -0
  148. package/dist/lib/runtime/native/request.js +585 -0
  149. package/dist/lib/runtime/native/selfCorrection.d.ts +106 -0
  150. package/dist/lib/runtime/native/selfCorrection.js +253 -0
  151. package/dist/lib/runtime/native/step.d.ts +148 -0
  152. package/dist/lib/runtime/native/step.js +239 -0
  153. package/dist/lib/runtime/native/taskMode.d.ts +64 -0
  154. package/dist/lib/runtime/native/taskMode.js +126 -0
  155. package/dist/lib/runtime/native/telemetry.d.ts +75 -0
  156. package/dist/lib/runtime/native/telemetry.js +299 -0
  157. package/dist/lib/runtime/native/tempState.d.ts +31 -0
  158. package/dist/lib/runtime/native/tempState.js +56 -0
  159. package/dist/lib/runtime/nativeTurn.d.ts +86 -0
  160. package/dist/lib/runtime/nativeTurn.js +107 -0
  161. package/dist/lib/runtime/questions.d.ts +29 -7
  162. package/dist/lib/runtime/questions.js +47 -32
  163. package/dist/lib/runtime/runtimeFlag.d.ts +30 -0
  164. package/dist/lib/runtime/runtimeFlag.js +50 -0
  165. package/dist/lib/runtime/sessions.d.ts +166 -0
  166. package/dist/lib/runtime/sessions.js +201 -0
  167. package/dist/lib/runtime/syndicateTurn.d.ts +41 -5
  168. package/dist/lib/runtime/syndicateTurn.js +202 -32
  169. package/dist/lib/session/supabaseSessionService.d.ts +64 -17
  170. package/dist/lib/session/supabaseSessionService.js +148 -105
  171. package/dist/lib/session/transcript.d.ts +68 -22
  172. package/dist/lib/session/transcript.js +102 -28
  173. package/dist/lib/skills.js +33 -7
  174. package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
  175. package/dist/lib/storage/postgres/credentialStore.js +82 -0
  176. package/dist/lib/storage/postgres/index.d.ts +12 -0
  177. package/dist/lib/storage/postgres/index.js +6 -1
  178. package/dist/lib/storage/postgres/sessionService.d.ts +43 -15
  179. package/dist/lib/storage/postgres/sessionService.js +128 -66
  180. package/dist/lib/syndicateSchema.d.ts +47 -0
  181. package/dist/lib/syndicateSchema.js +72 -3
  182. package/dist/lib/toolRegistry.d.ts +15 -2
  183. package/dist/lib/toolRegistry.js +33 -17
  184. package/dist/lib/tools/adkTool.d.ts +81 -0
  185. package/dist/lib/tools/adkTool.js +268 -0
  186. package/dist/lib/tools/auth.d.ts +118 -0
  187. package/dist/lib/tools/auth.js +89 -0
  188. package/dist/lib/tools/collectionsSearchTool.d.ts +7 -6
  189. package/dist/lib/tools/collectionsSearchTool.js +11 -26
  190. package/dist/lib/tools/credentialCipher.d.ts +58 -0
  191. package/dist/lib/tools/credentialCipher.js +116 -0
  192. package/dist/lib/tools/credentialStore.d.ts +77 -0
  193. package/dist/lib/tools/credentialStore.js +246 -0
  194. package/dist/lib/tools/examples.d.ts +43 -0
  195. package/dist/lib/tools/examples.js +62 -0
  196. package/dist/lib/tools/generateImageTool.d.ts +15 -6
  197. package/dist/lib/tools/generateImageTool.js +19 -29
  198. package/dist/lib/tools/inspectImageTool.d.ts +9 -6
  199. package/dist/lib/tools/inspectImageTool.js +13 -17
  200. package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
  201. package/dist/lib/tools/mcpToolFactory.js +46 -47
  202. package/dist/lib/tools/memoryTools.d.ts +61 -0
  203. package/dist/lib/tools/memoryTools.js +119 -0
  204. package/dist/lib/tools/nativeTools.d.ts +38 -0
  205. package/dist/lib/tools/nativeTools.js +43 -0
  206. package/dist/lib/tools/oauthConsent.d.ts +181 -0
  207. package/dist/lib/tools/oauthConsent.js +330 -0
  208. package/dist/lib/tools/openapi/call.d.ts +108 -0
  209. package/dist/lib/tools/openapi/call.js +295 -0
  210. package/dist/lib/tools/openapi/parse.d.ts +147 -0
  211. package/dist/lib/tools/openapi/parse.js +570 -0
  212. package/dist/lib/tools/openapiTools.d.ts +25 -17
  213. package/dist/lib/tools/openapiTools.js +124 -121
  214. package/dist/lib/tools/scienceTools.d.ts +14 -14
  215. package/dist/lib/tools/skillToolset.d.ts +58 -87
  216. package/dist/lib/tools/skillToolset.js +61 -142
  217. package/dist/lib/tools/skills/env.d.ts +58 -0
  218. package/dist/lib/tools/skills/env.js +165 -0
  219. package/dist/lib/tools/skills/executor.d.ts +114 -0
  220. package/dist/lib/tools/skills/executor.js +331 -0
  221. package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
  222. package/dist/lib/tools/skills/frontmatter.js +215 -0
  223. package/dist/lib/tools/skills/loader.d.ts +91 -0
  224. package/dist/lib/tools/skills/loader.js +257 -0
  225. package/dist/lib/tools/skills/tools.d.ts +187 -0
  226. package/dist/lib/tools/skills/tools.js +386 -0
  227. package/dist/lib/tools/taskTools.d.ts +15 -15
  228. package/dist/lib/tools/tool.d.ts +354 -0
  229. package/dist/lib/tools/tool.js +304 -0
  230. package/dist/lib/tools/toolContract.d.ts +89 -33
  231. package/dist/lib/tools/toolContract.js +104 -82
  232. package/dist/lib/tools/urlContextTool.d.ts +3 -0
  233. package/dist/lib/tools/urlContextTool.js +5 -1
  234. package/dist/lib/tools/webExtractTool.d.ts +2 -2
  235. package/dist/lib/tools/webExtractTool.js +2 -1
  236. package/dist/lib/tools/webSearchTool.d.ts +6 -9
  237. package/dist/lib/tools/webSearchTool.js +14 -59
  238. package/dist/lib/tools/wikiTools.d.ts +19 -19
  239. package/dist/lib/tools/wikiTools.js +13 -9
  240. package/dist/lib/tools/xApiSearchTool.d.ts +2 -2
  241. package/dist/lib/tools/xApiSearchTool.js +2 -1
  242. package/dist/lib/tools/xSearchTool.d.ts +7 -9
  243. package/dist/lib/tools/xSearchTool.js +11 -62
  244. package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
  245. package/dist/lib/tools/xaiSearchParams.js +126 -0
  246. package/dist/lib/wiki/agentRun.d.ts +12 -0
  247. package/dist/lib/wiki/agentRun.js +72 -34
  248. package/dist/lib/wiki/entities.d.ts +12 -0
  249. package/dist/lib/wiki/entities.js +45 -3
  250. package/dist/lib/workflow/agentNode.d.ts +136 -0
  251. package/dist/lib/workflow/agentNode.js +274 -0
  252. package/dist/lib/workflow/graph.d.ts +177 -0
  253. package/dist/lib/workflow/graph.js +458 -0
  254. package/dist/lib/workflow/nodeEvents.d.ts +50 -0
  255. package/dist/lib/workflow/nodeEvents.js +101 -0
  256. package/dist/lib/workflow/pause.d.ts +122 -0
  257. package/dist/lib/workflow/pause.js +180 -0
  258. package/dist/lib/workflow/resume.d.ts +165 -0
  259. package/dist/lib/workflow/resume.js +440 -0
  260. package/dist/lib/workflow/route.d.ts +58 -0
  261. package/dist/lib/workflow/route.js +69 -0
  262. package/dist/lib/workflow/scheduler.d.ts +415 -0
  263. package/dist/lib/workflow/scheduler.js +743 -0
  264. package/dist/lib/workflow/toolNode.d.ts +127 -0
  265. package/dist/lib/workflow/toolNode.js +236 -0
  266. package/dist/lib/workflow/turn.d.ts +118 -0
  267. package/dist/lib/workflow/turn.js +216 -0
  268. package/dist/lib/workflowConfig.d.ts +3 -2
  269. package/dist/lib/workflowConfig.js +3 -12
  270. package/dist/scripts/a2a_server.js +4 -3
  271. package/dist/scripts/assistant_worker.d.ts +2 -1
  272. package/dist/scripts/assistant_worker.js +9 -4
  273. package/dist/scripts/syndicate_chat.js +19 -10
  274. package/package.json +4 -1
  275. package/skills/briefs/melchizedek-author.md +3 -2
  276. package/skills/briefs/melchizedek-models.md +13 -9
  277. package/skills/briefs/melchizedek.md +3 -3
  278. package/skills/melchizedek/SKILL.md +2 -1
  279. package/skills/melchizedek-author/SKILL.md +2 -1
  280. package/skills/melchizedek-author/assets/minimal.yaml +2 -0
  281. package/skills/melchizedek-models/SKILL.md +23 -6
@@ -0,0 +1,216 @@
1
+ /**
2
+ * lib/workflow/turn.ts — one turn of a workflow syndicate on the native
3
+ * runtime: the message stored, the graph walked by the engine's scheduler,
4
+ * and every event the walk stores yielded in the order it was stored, the
5
+ * node inputs aside, as ADK's Runner yields them (ADR 0095).
6
+ *
7
+ * WHY this file exists:
8
+ * runSyndicateTurn (lib/runtime/syndicateTurn.ts) runs a workflow on ADK
9
+ * by handing ADK's Workflow to its Runner and draining the events the
10
+ * Runner yields (drainAgentStream). On the native runtime it hands this
11
+ * generator to the same drain, under the same root span, so the progress
12
+ * lines, onProgress calls, node errors, the paused input and the ledger
13
+ * rows come out of one reader on both runtimes. Everything a node does is
14
+ * the WS4 modules': the scheduler (scheduler.ts), agent nodes on the
15
+ * native loop (agentNode.ts), tool nodes (toolNode.ts), ask_user nodes and
16
+ * the workflow's pause record (pause.ts), and the resume rebuilt from the
17
+ * session (resume.ts). This file wires them for one turn, as ADK's Runner
18
+ * and Workflow wire theirs:
19
+ *
20
+ * 1. THE MESSAGE. Stored as the user's event under a new `e-` invocation
21
+ * id, before the walk, as the Runner stores it (runNativeAgent does the
22
+ * same for one agent). A turn whose signal aborted first stores nothing.
23
+ * 2. THE START. workflowResume rebuilds every node's prior runs and the
24
+ * answers from the session, as ADK's rehydration does on every message:
25
+ * with nothing paused every node runs fresh. The walk's input is the
26
+ * message's text, not its content, as ADK hands its root workflow, and
27
+ * that text is what the workflow's pause record keeps. A pause only ADK
28
+ * can resume (UnsupportedWorkflowResumeError) fails the turn; it never
29
+ * walks afresh and asks the person again.
30
+ * 3. THE WALK. runWorkflowGraph with the runners chained (ask_user, tool,
31
+ * then agentNodeRuntime for agent nodes and map items), under the
32
+ * turn's signal. Every event a runner stores goes through the agent
33
+ * node runtime's one queue, in walk order: node inputs, the agents'
34
+ * events, tool and ask_user events, route steps, joins and maps, and
35
+ * the node-error event ADK writes for a node that gave up
36
+ * (nodeErrorEvent, on the scheduler's `workflow` node_error). A node's
37
+ * input turn is stored and not yielded, as ADK's Runner never yields
38
+ * it; a node agent's partial (streamed) event is yielded and not
39
+ * stored, as the Runner yields it.
40
+ * 4. THE END. A paused walk stores the workflow's own record
41
+ * (workflowPauseEvent) after every node's event. A walk the turn
42
+ * stopped (InvocationAbortedError, or any failure once the signal
43
+ * fired) ends quietly, as ADK's Runner ends an aborted run: the turn
44
+ * runner reads the stop reason from its control. A node that gave up
45
+ * rethrows its error once every event is stored and yielded, as ADK's
46
+ * Runner throws it.
47
+ * 5. THE SPANS. `workflow.invoke <name>` around the walk, `node.execute
48
+ * <name>` around each node run (the scheduler's traceNode hook), and
49
+ * `tool.execute <name>` around a tool node's call
50
+ * (lib/runtime/native/telemetry.ts). An agent node's `agent.invoke`
51
+ * opens inside its node's span, so each model call is attributed to
52
+ * its node's agent in the ledger.
53
+ *
54
+ * WHAT IT REFUSES before any model call: refuseUnrunnableNodes throws, for
55
+ * a tool node, ADK's compile-time refusals (an unregistered tool, a
56
+ * long-running one). A pause raised inside an agent node is refused by
57
+ * runAgentNode by name.
58
+ *
59
+ * It imports nothing from ADK: the caller passes the tool lookup (the
60
+ * registry builds ADK's tools) and the store as the engine's interface.
61
+ * The message, the answers and every stored event are data the walk
62
+ * carries, never instructions this module acts on.
63
+ */
64
+ import { randomUUID } from 'node:crypto';
65
+ import { createTurnEvent } from '../runtime/events.js';
66
+ import { traceNodeExecution, traceToolNodeCall, traceWorkflowInvocation } from '../runtime/native/telemetry.js';
67
+ import { agentNodeRuntime } from './agentNode.js';
68
+ import { askUserNodeRunner, workflowPauseEvent } from './pause.js';
69
+ import { workflowResume } from './resume.js';
70
+ import { InvocationAbortedError, nodeErrorEvent, runWorkflowGraph } from './scheduler.js';
71
+ import { resolveToolNode, toolNodeRunner } from './toolNode.js';
72
+ /**
73
+ * Throws, before any model call, what ADK's compileWorkflow throws for a
74
+ * tool node: an unregistered tool, or a long-running one (ADR 0091).
75
+ */
76
+ export function refuseUnrunnableNodes(graph, resolveTool) {
77
+ for (const node of graph.nodes.values())
78
+ if (node.kind === 'tool')
79
+ resolveToolNode(node, { resolveTool });
80
+ }
81
+ const isAborted = (error) => error instanceof InvocationAbortedError || error?.name === 'InvocationAbortedError';
82
+ /**
83
+ * Runs one turn of the workflow: stores the message, walks the graph (a
84
+ * resume when the session holds a paused walk), and yields every event the
85
+ * walk stores, in the order stored, but the node inputs (ADK's Runner
86
+ * yields none of them). Returns how the walk ended, or
87
+ * undefined when the signal fired before the message was stored. Throws
88
+ * the error a node gave up with (after its events), ADK's message for a
89
+ * reply that answers nothing, and UnsupportedWorkflowResumeError.
90
+ */
91
+ export async function* runNativeWorkflow(params) {
92
+ const { graph, sessions, appName, userId, sessionId, signal } = params;
93
+ const session = await sessions.get({ appName, userId, sessionId });
94
+ if (!session)
95
+ throw new Error(`Session not found: ${sessionId} (appName=${appName}, userId=${userId})`);
96
+ if (signal?.aborted)
97
+ return undefined;
98
+ if (params.userParts.length === 0)
99
+ throw new Error('No parts in the newMessage.');
100
+ // 1. The message, as the Runner stores it.
101
+ const invocationId = `e-${randomUUID()}`;
102
+ const userContent = { role: 'user', parts: params.userParts };
103
+ await sessions.append(session, createTurnEvent({ invocationId, author: 'user', content: userContent }));
104
+ if (signal?.aborted)
105
+ return { stopped: true };
106
+ // 2. The start: the message's text as the input, and every node's prior runs.
107
+ const start = workflowResume({ events: session.events, invocationId, userContent, workflowPath: graph.name });
108
+ // 3. The walk, its events handed on in the order they are stored.
109
+ const queue = [];
110
+ let wake;
111
+ const runtime = agentNodeRuntime({
112
+ agents: params.agents,
113
+ session,
114
+ sessions,
115
+ invocationId,
116
+ userContent,
117
+ loop: {
118
+ adapterFor: params.adapterFor,
119
+ stream: params.stream ?? false,
120
+ ...(params.memory ? { memory: params.memory } : {}),
121
+ ...(params.log ? { log: params.log } : {}),
122
+ ...(params.selfCorrection ? { selfCorrection: params.selfCorrection } : {}),
123
+ ...(params.credentials ? { credentials: params.credentials } : {}),
124
+ },
125
+ onEvent: (event) => {
126
+ // A node's input turn is appended straight to the session, as ADK's runLlmAgentAsNode appends it: stored, never yielded.
127
+ if (event.author === 'user')
128
+ return;
129
+ queue.push(event);
130
+ wake?.();
131
+ },
132
+ // A node agent's streamed text, yielded as ADK's Runner yields its partial events.
133
+ onPartial: (event) => {
134
+ queue.push(event);
135
+ wake?.();
136
+ },
137
+ });
138
+ // Every event handed over is queued a microtask later (agentNodeRuntime.store); `stored` waits for all of them.
139
+ const handed = new Set();
140
+ const store = (event) => {
141
+ const queued = runtime.store(event).catch(() => { }); // a store error surfaces from settled()
142
+ handed.add(queued);
143
+ void queued.finally(() => handed.delete(queued));
144
+ };
145
+ const stored = async () => {
146
+ while (handed.size > 0)
147
+ await Promise.all([...handed]);
148
+ await runtime.settled();
149
+ };
150
+ const runNode = askUserNodeRunner({ invocationId, onEvent: store }, toolNodeRunner({
151
+ invocationId,
152
+ appName,
153
+ userId,
154
+ sessionId,
155
+ userContent,
156
+ resolveTool: params.resolveTool,
157
+ state: () => session.state,
158
+ ...(params.memory ? { memory: params.memory } : {}),
159
+ ...(params.credentials ? { credentials: params.credentials } : {}),
160
+ onEvent: store,
161
+ traceCall: traceToolNodeCall,
162
+ }, runtime.runNode));
163
+ const onEvent = (event) => {
164
+ runtime.onEvent(event);
165
+ // ADK's reportNodeError: the workflow's own event for a node that gave up.
166
+ if (event.type === 'node_error' && event.source === 'workflow')
167
+ store(nodeErrorEvent(event, invocationId));
168
+ };
169
+ let end;
170
+ let failure;
171
+ let finished = false;
172
+ const walk = traceWorkflowInvocation({ name: graph.name, path: graph.name }, { sessionId, invocationId }, async () => {
173
+ try {
174
+ const run = await runWorkflowGraph(graph, {
175
+ input: start.input,
176
+ resume: start.resume,
177
+ runNode,
178
+ onEvent,
179
+ traceNode: traceNodeExecution,
180
+ ...(signal ? { signal } : {}),
181
+ });
182
+ // 4. A paused walk: the workflow's own record, after every node's event.
183
+ if (run.interruptIds.length > 0)
184
+ store(workflowPauseEvent({ name: graph.name, invocationId, input: start.input, interruptIds: run.interruptIds }));
185
+ await stored();
186
+ return run;
187
+ }
188
+ catch (error) {
189
+ await stored().catch(() => { });
190
+ throw error;
191
+ }
192
+ }).then((run) => {
193
+ end = { run };
194
+ }, (error) => {
195
+ // ADK's Runner ends an aborted run without an error; the turn's control holds why.
196
+ if (isAborted(error) || signal?.aborted)
197
+ end = { stopped: true };
198
+ else
199
+ failure = { error };
200
+ }).finally(() => {
201
+ finished = true;
202
+ wake?.();
203
+ });
204
+ for (;;) {
205
+ while (queue.length > 0)
206
+ yield queue.shift();
207
+ if (finished)
208
+ break;
209
+ await new Promise((resolve) => (wake = resolve));
210
+ wake = undefined;
211
+ }
212
+ await walk;
213
+ if (failure)
214
+ throw failure.error;
215
+ return end;
216
+ }
@@ -69,9 +69,10 @@ export declare function nodeSettings(node: WorkflowNodeYaml | undefined): {
69
69
  /**
70
70
  * The route an output names: the `routeKey` property of an object (a parsed
71
71
  * JSON output), else the trimmed text. Never undefined, so an edge with a
72
- * `default` key always has something to fall back from.
72
+ * `default` key always has something to fall back from. One function for
73
+ * both runtimes; it lives in lib/workflow/route.ts.
73
74
  */
74
- export declare function routeOf(output: unknown, routeKey?: string): string;
75
+ export { routeOf } from './workflow/route.ts';
75
76
  /** A pause raised by an `ask_user` node, read from the run's events (`adk_request_input`). */
76
77
  export interface PendingInput {
77
78
  /** The interrupt id. A plain-text next message answers it. */
@@ -57,19 +57,10 @@ export function nodeSettings(node) {
57
57
  /**
58
58
  * The route an output names: the `routeKey` property of an object (a parsed
59
59
  * JSON output), else the trimmed text. Never undefined, so an edge with a
60
- * `default` key always has something to fall back from.
60
+ * `default` key always has something to fall back from. One function for
61
+ * both runtimes; it lives in lib/workflow/route.ts.
61
62
  */
62
- export function routeOf(output, routeKey = 'route') {
63
- if (output && typeof output === 'object' && !Array.isArray(output)) {
64
- const value = output[routeKey];
65
- if (value === undefined || value === null)
66
- return '';
67
- return String(value).trim();
68
- }
69
- if (typeof output === 'string')
70
- return output.trim();
71
- return output === undefined || output === null ? '' : String(output).trim();
72
- }
63
+ export { routeOf } from './workflow/route.js';
73
64
  /** ADK's function-call name for a workflow input request. */
74
65
  export const INPUT_REQUEST = 'adk_request_input';
75
66
  /** The request an `adk_request_input` call carries, or undefined for another call. */
@@ -79,7 +79,6 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
79
79
  import { randomBytes } from 'node:crypto';
80
80
  import { realpathSync, writeFileSync } from 'node:fs';
81
81
  import { pathToFileURL } from 'node:url';
82
- import { setLogLevel, LogLevel } from '@google/adk';
83
82
  import { createA2AApp } from '../lib/a2a/app.js';
84
83
  import { callerTokens, firstOf, hashCallerToken, jwtIdentity, parseCallers, sharedSecret, trustedHeader, } from '../lib/a2a/identity.js';
85
84
  import { budgets, memoryUsageStore, parseBudgets, postgresUsageStore, supabaseUsageStore } from '../lib/a2a/policy.js';
@@ -90,6 +89,7 @@ import { dbSchema } from '../lib/storage/schema.js';
90
89
  import { postgresStorage } from '../lib/storage/postgres/index.js';
91
90
  import { isPlaceholderValue, loadEnv } from '../lib/loadEnv.js';
92
91
  import { flushTracing } from '../lib/observability/tracer.js';
92
+ import { setLogLevel } from '../lib/runtime/logging.js';
93
93
  // Re-exported for existing importers (tests/failNarration.test.ts).
94
94
  export { describeTurnError } from '../lib/a2a/executor.js';
95
95
  /** Minimum bearer-secret length below which the server warns. */
@@ -138,8 +138,9 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
138
138
  // the root span carries the full user message and answer.
139
139
  if (process.env.OTEL_CONSOLE_SPANS === undefined)
140
140
  process.env.OTEL_CONSOLE_SPANS = 'false';
141
- // ADK's INFO/DEBUG logs carry raw event JSON; keep them quiet.
142
- setLogLevel(LogLevel.WARN);
141
+ // The engine's level, which ADK's logger follows: ADK's INFO/DEBUG logs
142
+ // carry raw event JSON, so keep them quiet.
143
+ setLogLevel('warn');
143
144
  const publicUrl = process.env.PUBLIC_URL?.trim() || undefined;
144
145
  const port = envInt('PORT', 4000);
145
146
  // ── Authentication posture ─────────────────────────────────────────────────
@@ -13,7 +13,8 @@
13
13
  * and writes the result (or the error) back to the store, where
14
14
  * `task_get` reads it in a later conversation.
15
15
  *
16
- * A job runs through lib/runtime/syndicateTurn.ts, the same runtime the
16
+ * A job runs through lib/runtime/syndicateTurn.ts (on the runtime
17
+ * MELCHIZEDEK_RUNTIME names, ADK by default), the same runtime the
17
18
  * A2A server, the REPL and the observatory use, so it runs the exact agent
18
19
  * the conversation would have called directly — with the same step cap —
19
20
  * and the job timeout ABORTS the run (the model call in flight included)
@@ -13,7 +13,8 @@
13
13
  * and writes the result (or the error) back to the store, where
14
14
  * `task_get` reads it in a later conversation.
15
15
  *
16
- * A job runs through lib/runtime/syndicateTurn.ts, the same runtime the
16
+ * A job runs through lib/runtime/syndicateTurn.ts (on the runtime
17
+ * MELCHIZEDEK_RUNTIME names, ADK by default), the same runtime the
17
18
  * A2A server, the REPL and the observatory use, so it runs the exact agent
18
19
  * the conversation would have called directly — with the same step cap —
19
20
  * and the job timeout ABORTS the run (the model call in flight included)
@@ -31,17 +32,19 @@
31
32
  * and must match the chat process's value. Run ONE worker per store.
32
33
  */
33
34
  import { randomUUID } from 'node:crypto';
34
- import { InMemorySessionService, LogLevel, setLogLevel } from '@google/adk';
35
35
  import { loadEnv } from '../lib/loadEnv.js';
36
36
  import { loadSyndicate } from '../lib/loadSyndicate.js';
37
37
  import { runSyndicateTurn } from '../lib/runtime/syndicateTurn.js';
38
+ import { asAdkSessionService } from '../lib/runtime/adkSessionBridge.js';
39
+ import { setLogLevel } from '../lib/runtime/logging.js';
40
+ import { InProcessSessionService } from '../lib/runtime/sessions.js';
38
41
  import { PROVIDERS, providerForModel, providerKeyPresent, registerAvailableProviders, } from '../lib/models/registry.js';
39
42
  import { getTaskBackend, setTaskBackend, taskStorePath } from '../lib/tools/taskTools.js';
40
43
  import { postgresStorage } from '../lib/storage/postgres/index.js';
41
44
  import { dbSchema } from '../lib/storage/schema.js';
42
45
  import { hostname } from 'node:os';
43
46
  loadEnv(import.meta.url);
44
- setLogLevel(LogLevel.WARN);
47
+ setLogLevel('warn');
45
48
  registerAvailableProviders();
46
49
  // ── CLI ──────────────────────────────────────────────────────────────────────
47
50
  const argv = process.argv.slice(2).filter((a) => a !== '--');
@@ -111,7 +114,9 @@ async function runJob(job) {
111
114
  appName: 'assistant-worker',
112
115
  userId: 'local-user',
113
116
  sessionId: randomUUID(),
114
- sessionService: new InMemorySessionService(),
117
+ // A fresh in-process store per job, as the turn runner's signature
118
+ // names it (ADR 0080). The runtime follows MELCHIZEDEK_RUNTIME.
119
+ sessionService: asAdkSessionService(new InProcessSessionService()),
115
120
  compile: { log, onUnknownTool: (n) => log(`unknown tool '${n}' skipped`) },
116
121
  signal: current.signal,
117
122
  deadlineMs: JOB_TIMEOUT_MS,
@@ -5,7 +5,11 @@ import { join } from 'node:path';
5
5
  import { loadSyndicate, parseCliBindings } from '../lib/loadSyndicate.js';
6
6
  import { isDispatchSyndicate } from '../lib/dispatch.js';
7
7
  import { approvalResponsePart, describeApproval, describeInput, ingestTurnMemory, runSyndicateTurn } from '../lib/runtime/syndicateTurn.js';
8
- import { InMemorySessionService, getFunctionCalls, getFunctionResponses, setLogLevel, LogLevel, } from '@google/adk';
8
+ import { getFunctionCalls, getFunctionResponses } from '../lib/runtime/events.js';
9
+ import { InProcessSessionService } from '../lib/runtime/sessions.js';
10
+ import { asAdkSessionService, asSessionService } from '../lib/runtime/adkSessionBridge.js';
11
+ import { asAdkMemoryService } from '../lib/runtime/adkMemoryBridge.js';
12
+ import { setLogLevel } from '../lib/runtime/logging.js';
9
13
  import { randomUUID } from 'node:crypto';
10
14
  import { loadEnv } from '../lib/loadEnv.js';
11
15
  // ── LLM Provider Registration ─────────────────────────────────────────────────
@@ -18,8 +22,8 @@ import { registerAvailableProviders, providerForModel, providerKeyPresent, PROVI
18
22
  // ── Persistence Factory ───────────────────────────────────────────────────────
19
23
  // Supabase-specific initialization lives in supabaseProvider.ts.
20
24
  import { hasSupabaseCredentials, createSupabaseServices, } from '../lib/persistence/supabaseProvider.js';
21
- // Silence ADK verbose INFO logging natively
22
- setLogLevel(LogLevel.WARN);
25
+ // The engine's level, which ADK's logger follows: no INFO chatter in the chat.
26
+ setLogLevel('warn');
23
27
  const c = {
24
28
  reset: '\x1b[0m',
25
29
  bold: '\x1b[1m',
@@ -175,7 +179,10 @@ async function main() {
175
179
  // The same services the server would use for this syndicate's
176
180
  // memory_system: Supabase sessions (and the memory service for
177
181
  // long-term) when configured, process memory otherwise.
178
- let sessionService = new InMemorySessionService();
182
+ // The engine's stores (ADR 0080): the in-process one, or Supabase's,
183
+ // which has both faces. The turn runner takes ADK's face (its signature
184
+ // is fixed), and follows MELCHIZEDEK_RUNTIME for the runtime.
185
+ let sessionService = new InProcessSessionService();
179
186
  let memoryService;
180
187
  if (persistence.sessionService === 'supabase') {
181
188
  const services = await createSupabaseServices({
@@ -188,7 +195,9 @@ async function main() {
188
195
  memoryService = services.memoryService;
189
196
  }
190
197
  const appName = config.syndicate_name || 'melchizedek-syndicate';
191
- await sessionService.createSession({ appName, userId: SESSION_USER_ID, sessionId: SESSION_ID, state: {} });
198
+ await asSessionService(sessionService).create({ appName, userId: SESSION_USER_ID, sessionId: SESSION_ID, state: {} });
199
+ const turnSessions = asAdkSessionService(sessionService);
200
+ const turnMemory = memoryService ? asAdkMemoryService(memoryService) : undefined;
192
201
  banner(config, persistence, mergedBindings, SESSION_ID);
193
202
  // Plan-dispatch, nested yaml_reference syndicates, guards and the
194
203
  // max_steps cap run here exactly as the A2A server runs them: both call
@@ -227,8 +236,8 @@ async function main() {
227
236
  appName,
228
237
  userId: SESSION_USER_ID,
229
238
  sessionId: SESSION_ID,
230
- sessionService,
231
- memoryService,
239
+ sessionService: turnSessions,
240
+ memoryService: turnMemory,
232
241
  compile: {
233
242
  onUnknownTool: (name) => console.warn(`${c.yellow}⚠ Unknown tool: '${name}' — skipping.${c.reset}`),
234
243
  log: (message) => console.log(`${c.dim} ${message}${c.reset}`),
@@ -276,12 +285,12 @@ async function main() {
276
285
  }
277
286
  }
278
287
  const ingest = async () => {
279
- if (!memoryService)
288
+ if (!turnMemory)
280
289
  return;
281
290
  try {
282
291
  const ingested = await ingestTurnMemory({
283
- memoryService,
284
- sessionService,
292
+ memoryService: turnMemory,
293
+ sessionService: turnSessions,
285
294
  appName,
286
295
  userId: SESSION_USER_ID,
287
296
  sessionId: SESSION_ID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "melchizedek-agents",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "A multi-model, multi-agent orchestration framework on Google ADK — YAML-defined syndicates, Supabase session persistence, pgvector long-term memory. Companion repo to the lyceumagents.com curriculum.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,6 +35,7 @@
35
35
  "./storage/postgres": "./dist/lib/storage/postgres/index.js",
36
36
  "./grounding": "./dist/lib/grounding.js",
37
37
  "./config": "./dist/lib/config.js",
38
+ "./model": "./dist/lib/model.js",
38
39
  "./models/*": "./dist/lib/models/*.js",
39
40
  "./tools": "./dist/lib/toolRegistry.js",
40
41
  "./tools/*": "./dist/lib/tools/*.js",
@@ -102,6 +103,7 @@
102
103
  "assistant:worker": "node --disable-warning=DEP0040 --experimental-strip-types scripts/assistant_worker.ts",
103
104
  "demo:models": "node --disable-warning=DEP0040 --experimental-strip-types scripts/demo_model_optionality.ts",
104
105
  "demo:direct": "node --disable-warning=DEP0040 --experimental-strip-types scripts/direct_call.ts",
106
+ "parity": "node --disable-warning=DEP0040 --experimental-strip-types scripts/parity_check.ts",
105
107
  "mcp:demo": "node --disable-warning=DEP0040 --experimental-strip-types scripts/demo_mcp_server.ts",
106
108
  "mcp:wiki": "node --disable-warning=DEP0040 --experimental-strip-types scripts/wiki/mcp_server.ts",
107
109
  "mcp:science": "node --disable-warning=DEP0040 --experimental-strip-types scripts/science_mcp_server.ts",
@@ -111,6 +113,7 @@
111
113
  "schema:gen": "node --disable-warning=DEP0040 --experimental-strip-types scripts/gen_syndicate_schema.ts",
112
114
  "start:a2a": "node --disable-warning=DEP0040 --experimental-strip-types scripts/a2a_server.ts",
113
115
  "test": "node --disable-warning=DEP0040 --experimental-strip-types --test tests/**/*.test.ts",
116
+ "fixtures:sessions:check": "node --disable-warning=DEP0040 --experimental-strip-types tests/fixtures/sessions/generate.ts --check",
114
117
  "init": "node --disable-warning=DEP0040 --experimental-strip-types scripts/init.ts",
115
118
  "db": "node --disable-warning=DEP0040 --experimental-strip-types scripts/db.ts",
116
119
  "registry": "node --disable-warning=DEP0040 --experimental-strip-types scripts/registry.ts",
@@ -38,7 +38,8 @@ Agent block keys (orchestrator and each subagent):
38
38
  - `code_execution: gemini` lets a Gemini agent write and run Python in Google's sandbox (nothing runs on your machine). `context: { compact_after_tokens: 60000 }` on a delegate orchestrator summarizes a long conversation instead of overflowing it. `mode: task` on a workflow node makes its output its `finish_task` arguments.
39
39
  - `openapi:` a list of OpenAPI 3 spec files (relative to the syndicate file) whose operations become the agent's tools; only GET operations unless `operations` names others; `auth` reads `bearer_env` or `api_key: {env, in, name}` from the environment. Read `weather.yaml` for the worked example.
40
40
  - `mcp_server_url:` on a subagent: tools discovered from that MCP server at start (the melchizedek-serve skill).
41
- - `generateContentConfig:` per agent: `temperature`, `maxOutputTokens`, `responseMimeType: "application/json"` for structured output, `thinkingConfig: { thinkingLevel: MEDIUM, includeThoughts: false }` on Gemini models that think. Thinking tokens count against `maxOutputTokens`, so a thinking agent with long output needs a high ceiling (the Scribe uses 24576).
41
+ - `reasoning:` how hard the agent reasons, on any provider: `none`, `low`, `medium`, `high`, or `{ budget_tokens: 4096 }`. The compiler sends each provider the field it reads, so the setting survives a change to the `model:` line; unset keeps the provider's default. The templates in `config/agents/templates/` give a gathering or routing agent `none` and a synthesizing agent `low` or `medium`. What each level becomes per provider and per Claude model generation belongs to the melchizedek-models skill.
42
+ - `generateContentConfig:` per agent: `temperature`, `maxOutputTokens`, `responseMimeType: "application/json"` for structured output. Thinking tokens count against `maxOutputTokens`, so a reasoning agent with long output needs a high ceiling (the Scribe uses 24576). `thinkingConfig` and `reasoningEffort` inside this block are the older, provider-specific spelling of `reasoning:`: some starter-pack examples still use them and they still load, but an agent that sets either beside `reasoning:` fails to load. New agents use `reasoning:`.
42
43
  - `outputSchema:` a JSON schema (types in upper case: `OBJECT`, `STRING`, `INTEGER`, `BOOLEAN`, `ARRAY`) the agent's reply must satisfy.
43
44
  - Subagents receive one string argument, `query`, and see nothing else of the conversation. The orchestrator's instruction must say what to pass them, in full, every time.
44
45
 
@@ -60,5 +61,5 @@ Validate and run:
60
61
  Editing a syndicate that is already serving:
61
62
  - `melchizedek-serve` compiles a syndicate when it is first requested and caches it for the life of the process; an edited file changes nothing until the server restarts. A deployment that boots agents from the Supabase agent registry (`registry:<id>`) serves the registry row, not the file, until the row is republished.
62
63
 
63
- IDENTIFIERS (verbatim): config/agents/, syndicateSchema.yaml, assets/minimal.yaml, syndicate_name, memory_system, internal-only, session-only, long-term, variables:, {{current_date}}, memory_extraction_rules:, dispatch:, default_route, route_overrides, workflow:, edges, nodes, ask_user, join, map, START, pipeline.yaml, orchestrator:, subagents:, description, model, instruction, tools:, mcp_server_url:, generateContentConfig:, outputSchema:, responseMimeType, thinkingConfig, maxOutputTokens, # tier:, npm test, npx melchizedek-doctor, npx melchizedek-chat --syndicate, loadSyndicate, gemini-3.8-flash, gemini-3.5-flash-lite, critic.yaml, scribe.yaml, tutor.yaml, patient_advocate.yaml
64
+ IDENTIFIERS (verbatim): config/agents/, syndicateSchema.yaml, assets/minimal.yaml, syndicate_name, memory_system, internal-only, session-only, long-term, variables:, {{current_date}}, memory_extraction_rules:, dispatch:, default_route, route_overrides, workflow:, edges, nodes, ask_user, join, map, START, pipeline.yaml, orchestrator:, subagents:, description, model, instruction, tools:, mcp_server_url:, reasoning:, budget_tokens, config/agents/templates/, generateContentConfig:, outputSchema:, responseMimeType, thinkingConfig, reasoningEffort, maxOutputTokens, # tier:, npm test, npx melchizedek-doctor, npx melchizedek-chat --syndicate, loadSyndicate, gemini-3.8-flash, gemini-3.5-flash-lite, critic.yaml, scribe.yaml, tutor.yaml, patient_advocate.yaml
64
65
  LIMITS: the Node one-liner goes in a fenced bash block exactly as given. Body under 170 lines.
@@ -6,19 +6,19 @@ STRUCTURE (exact):
6
6
  Frontmatter, verbatim:
7
7
  ---
8
8
  name: melchizedek-models
9
- description: "Choose and wire a model for a Melchizedek agent: how a model id routes to Gemini, Claude, GPT, Grok, or local Ollama, which environment variable each needs, the gateway fallback, the doctor, and per-agent generation settings. Use when the user changes a model line, adds a provider key, sees Model not found or a gateway error, or asks which keys a syndicate needs."
9
+ description: "Choose and wire a model for a Melchizedek agent: how a model id routes to Gemini, Claude, GPT, Grok, Kimi, or local Ollama, which environment variable each needs, the gateway fallback, the doctor, and per-agent settings such as how hard an agent reasons. Use when the user changes a model line, adds a provider key, wants an agent to think more or less, sees Model not found or a gateway error, or asks which keys a syndicate needs."
10
10
  ---
11
11
  Then `##` sections in this order: "How a model id routes", "Which key unlocks what", "Run with no key at all", "One key for every cloud provider", "Settings per agent", "Mixing providers in one syndicate", "Reading the errors".
12
12
 
13
13
  FACTS:
14
14
  How a model id routes:
15
- - The `model:` string's prefix names the provider: `claude-*` to Anthropic, `gpt-*` and `o<digit>*` to OpenAI, `grok-*` to xAI, `ollama/<model>` to a local Ollama, and everything else to Gemini (the ADK-native default).
15
+ - The `model:` string's prefix names the provider: `claude-*` to Anthropic, `gpt-*` and `o<digit>*` to OpenAI, `grok-*` to xAI, `kimi-*` to Moonshot AI (Kimi), `ollama/<model>` to a local Ollama, and everything else to Gemini (the ADK-native default).
16
16
  - There is no allowlist in the engine: any id a provider currently serves works as written. A new model is a one-line YAML change.
17
- - Ids verified in this deployment: `gemini-3.8-flash` (production), `gemini-3.5-flash-lite` (subagents, cost), `claude-sonnet-4-6`, `claude-opus-4-6`, `claude-haiku-4-5-20251001`, `gpt-5-mini`, `gpt-5`, `grok-4.7`, `ollama/qwen3:8b`.
17
+ - Ids verified in this deployment: `gemini-3.8-flash` (production), `gemini-3.5-flash-lite` (subagents, cost), `claude-sonnet-4-6`, `claude-opus-4-6`, `claude-haiku-4-5-20251001`, `gpt-5-mini`, `gpt-5`, `grok-4.7`, `kimi-k3`, `ollama/qwen3:8b`.
18
18
  - `gemini-2.5-flash` returns a 400 about tool call context circulation with the server-side tool flag the framework sets; use `gemini-3.8-flash` or newer.
19
19
  - Subagents inherit the orchestrator's `model` when they set none.
20
20
  Which key unlocks what:
21
- - `GOOGLE_GENAI_API_KEY` for Gemini (also the embedding model behind long-term memory); `ANTHROPIC_API_KEY`; `OPENAI_API_KEY`; `XAI_API_KEY`; no key for `ollama/*`.
21
+ - `GOOGLE_GENAI_API_KEY` for Gemini (also the embedding model behind long-term memory); `ANTHROPIC_API_KEY`; `OPENAI_API_KEY`; `XAI_API_KEY`; `MOONSHOT_API_KEY` for Kimi; no key for `ollama/*`.
22
22
  - A provider registers only when its key is present; a missing key logs the provider as disabled, and an agent on that provider fails with `Model not found`.
23
23
  - `npx melchizedek-doctor` (clone: `npm run doctor`) reads every syndicate, resolves each agent's model under the current `.env`, and prints per agent: model, provider, which server-side search tools the path keeps or drops, and whether it is funded; per syndicate a verdict; and the variables that would unlock the most. `--json`, `--check` (exit 1 when any syndicate is blocked). Read-only, no key value printed.
24
24
  - The `# tier:` first line of every starter-pack file states its cost class (`keyless`, one provider name, or `multi-provider`); the doctor checks the claim against the models.
@@ -32,16 +32,20 @@ One key for every cloud provider (the gateway fallback):
32
32
  - Dials: `MODEL_GATEWAY_MODEL_MAP=<your id>=<the gateway's id>` renames an id the gateway rejects; `MODEL_GATEWAY_BASE_URL` points at a self-hosted proxy.
33
33
  - Telemetry attributes the call to the upstream provider and records the transport separately.
34
34
  Settings per agent:
35
- - `generateContentConfig:` on any agent: `temperature` (where the provider still exposes it), `maxOutputTokens`, `thinkingConfig: { thinkingLevel: MEDIUM, includeThoughts: false }` on Gemini models that think. Thinking tokens count against `maxOutputTokens`.
36
- - The framework's own pattern: data-gathering subagents on a lite model with a tight output cap; synthesis on the stronger model with a thinking level and room to reason.
35
+ - `reasoning:` on the agent block, beside `model:`, sets how hard the agent reasons: `none`, `low`, `medium`, `high`, or `{ budget_tokens: 4096 }`. It reads the same on every provider; unset keeps the provider's default. The compiler sends each provider the field it reads: Gemini 3 and later get a thinking level (`none` is `MINIMAL`, then `LOW`, `MEDIUM`, `HIGH`); Gemini 2.x, and Claude 4.6 or older, get a thinking budget of 0, 2048, 8192 or 16384 tokens; every later Claude model gets adaptive thinking at an effort of `low`, `medium` or `high`, and `none` is that model's own off switch, sent at `low` effort; every other provider, and the gateway, gets an effort word, and where the provider lacks that word, its nearest setting above.
36
+ - A `budget_tokens` value goes as written to Gemini and to Claude 4.6 or older, and as the smallest level that covers it everywhere else, later Claude models included. The effort word is always sent too, because the gateway can serve any cloud id; a direct provider ignores the field it does not read. Changing the `model:` line keeps the setting. Unset, a later Claude model keeps its own default: it thinks, except Opus 4.7 and 4.8, which think only when asked.
37
+ - Four provider limits: on Claude 4.6 or older, the adapter raises a budget under 1024 to 1024, the least Anthropic takes; on every Claude model, the Anthropic SDK refuses a non-streaming request whose output ceiling passes about 21000 tokens, and the adapter sets that ceiling to at least the thinking budget plus 2048, so a `budget_tokens` above about 19000 fails a non-streaming turn, while the levels stay under it; on Claude Opus 5 and 5.5, Fable and Mythos, `none` is adaptive thinking at `low` effort, so a little thinking remains, because Opus 5.5, Fable and Mythos cannot turn thinking off and Opus 5's off switch can write a tool call as text that never runs; Gemini 2.5 Pro rejects a budget of 0, so `none` fails on it.
38
+ - `generateContentConfig:` on any agent: `temperature` (where the provider still exposes it; never sent to Claude), `maxOutputTokens`. Thinking tokens count against `maxOutputTokens`, so a reasoning agent with long output needs room; the Claude adapter raises its own ceiling to the thinking budget plus 2048 tokens, on every Claude model. A level counts as its budget (2048, 8192 or 16384), so `high`, or a later Claude model that thinks by default, gets at least 18432.
39
+ - `thinkingConfig` and `reasoningEffort` inside `generateContentConfig:` are the older, provider-specific spelling of `reasoning:`. They still load, but each reaches only the providers that read it (a `thinkingLevel` written for Gemini does nothing on Claude). An agent that sets either beside `reasoning:` fails to load. `thinkingConfig` stays only for `includeThoughts: true`, which streams Gemini's thought trace and has no `reasoning:` form; such an agent sets its level inside `thinkingConfig` too (`thinkingLevel`) and leaves `reasoning:` out.
40
+ - The framework's own pattern: data-gathering subagents on a lite model with `reasoning: none` and a tight output cap; synthesis on the stronger model with `reasoning: low` or higher and room to reason. The templates in `config/agents/templates/` follow it.
37
41
  Mixing providers in one syndicate:
38
42
  - Any agent in a graph can run on a different provider; `model_zoo.yaml` declares one lightweight agent per provider, and in a clone `npm run demo:models` sends one prompt through each.
39
43
  - In code, `registerAvailableProviders()` from `melchizedek-agents` registers every provider whose key is present so a plain ADK `LlmAgent` can take any of these ids; in a clone `npm run demo:direct -- --model ollama/qwen3:8b hello` runs one agent with no YAML.
40
44
  Reading the errors:
41
- - `Model not found` for a `claude-*`, `gpt-*` or `grok-*` id: the provider's key is unset. Set it, or set the gateway pair.
45
+ - `Model not found` for a `claude-*`, `gpt-*`, `grok-*` or `kimi-*` id: the provider's key is unset. Set it, or set the gateway pair.
42
46
  - `GATEWAY_HTTP_ERROR ... 404/400`: the gateway rejected the mapped id; fix the name with `MODEL_GATEWAY_MODEL_MAP`.
43
47
  - `GATEWAY_KEY_MISSING`: `MODEL_GATEWAY` is set without `MODEL_GATEWAY_API_KEY`.
44
48
  - `OLLAMA_UNREACHABLE`: Ollama is not running (`ollama serve`) or the model is not pulled (`ollama list`).
45
49
 
46
- IDENTIFIERS (verbatim): model:, claude-*, gpt-*, grok-*, ollama/, gemini-3.8-flash, gemini-3.5-flash-lite, gemini-2.5-flash, ollama/qwen3:8b, GOOGLE_GENAI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, npx melchizedek-doctor, --json, --check, # tier:, ollama pull qwen3:8b, http://localhost:11434/v1, MODEL_GATEWAY, MODEL_GATEWAY_API_KEY, MODEL_GATEWAY_MODEL_MAP, MODEL_GATEWAY_BASE_URL, X-API-Key, generateContentConfig:, maxOutputTokens, thinkingConfig, model_zoo.yaml, npm run demo:models, registerAvailableProviders, GATEWAY_HTTP_ERROR, GATEWAY_KEY_MISSING, OLLAMA_UNREACHABLE, Model not found
47
- LIMITS: the routing prefixes may be a short table. Body under 160 lines.
50
+ IDENTIFIERS (verbatim): model:, claude-*, gpt-*, grok-*, kimi-*, ollama/, gemini-3.8-flash, gemini-3.5-flash-lite, gemini-2.5-flash, kimi-k3, ollama/qwen3:8b, GOOGLE_GENAI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, MOONSHOT_API_KEY, npx melchizedek-doctor, --json, --check, # tier:, ollama pull qwen3:8b, http://localhost:11434/v1, MODEL_GATEWAY, MODEL_GATEWAY_API_KEY, MODEL_GATEWAY_MODEL_MAP, MODEL_GATEWAY_BASE_URL, X-API-Key, reasoning:, budget_tokens, generateContentConfig:, maxOutputTokens, thinkingConfig, reasoningEffort, thinkingLevel, includeThoughts, config/agents/templates/, model_zoo.yaml, npm run demo:models, registerAvailableProviders, GATEWAY_HTTP_ERROR, GATEWAY_KEY_MISSING, OLLAMA_UNREACHABLE, Model not found
51
+ LIMITS: the routing prefixes and what `reasoning:` becomes per provider may each be a short table. Body under 160 lines.
@@ -43,7 +43,7 @@ The starter pack (file → name → what it does → tier / extra requirement):
43
43
  Check the keys:
44
44
  - `npx melchizedek-doctor` (clone: `npm run doctor`) reads every syndicate the loader can see, resolves each agent's model to a provider under the current `.env`, and prints one table: agent, model, provider, which server-side tools the path keeps or drops, and whether the path is funded, with one verdict per syndicate and the variables that would unlock the most. Read-only; nothing sent, nothing written, no key value printed.
45
45
  - `--json` for machine-readable output; `--check` exits 1 when any syndicate is blocked.
46
- - Which key: GOOGLE_GENAI_API_KEY for gemini-* ids, ANTHROPIC_API_KEY for claude-*, OPENAI_API_KEY for gpt-*, XAI_API_KEY for grok-*, none for ollama/* (Ollama must be running, model pulled). Details and the gateway fallback: the melchizedek-models skill.
46
+ - Which key: GOOGLE_GENAI_API_KEY for gemini-* ids, ANTHROPIC_API_KEY for claude-*, OPENAI_API_KEY for gpt-*, XAI_API_KEY for grok-*, MOONSHOT_API_KEY for kimi-*, none for ollama/* (Ollama must be running, model pulled). Details and the gateway fallback: the melchizedek-models skill.
47
47
 
48
48
  Run one:
49
49
  - Interactive: `npx melchizedek-chat --syndicate <name>` (clone: `npm run chat:syndicate -- --syndicate <name>`; every example also has an alias such as `npm run syndicate:tutor`). A banner shows the syndicate, orchestrator, subagents, and whether sessions and memory are in Supabase or in memory. Type `exit` to end the session; that is also when a long-term syndicate writes its memory.
@@ -60,7 +60,7 @@ Delegate a task from this agent:
60
60
 
61
61
  When something fails:
62
62
  - `Gemini API Key is not configured`: `.env` missing or GOOGLE_GENAI_API_KEY unset.
63
- - `Model not found` for a claude-*, gpt-* or grok-* id: that provider's key is not set, so the provider was not registered; the doctor names the variable.
63
+ - `Model not found` for a claude-*, gpt-*, grok-* or kimi-* id: that provider's key is not set, so the provider was not registered; the doctor names the variable.
64
64
  - `OLLAMA_UNREACHABLE` for ollama/* ids: Ollama is not running, or the model is not pulled (`ollama list`).
65
65
  - `[400] Tool call context circulation is not enabled`: the agent's `model:` is too old for agent transfer; use `gemini-3.8-flash` or newer.
66
66
  - `Refusing to connect to private/loopback MCP host`: the SSRF guard; for a local MCP server set `ALLOW_PRIVATE_MCP=true` in `.env`.
@@ -68,5 +68,5 @@ When something fails:
68
68
 
69
69
  The other skills: melchizedek-author (design or edit a syndicate file), melchizedek-serve (A2A server and MCP in both directions), melchizedek-memory (Supabase sessions and long-term memory), melchizedek-models (model ids, providers, keys, the gateway), melchizedek-scribe (write documents from a brief with the Scribe).
70
70
 
71
- IDENTIFIERS (verbatim): config/agents/, config/agents/examples/, node_modules/melchizedek-agents/config/agents/examples/, MELCHIZEDEK_AGENTS_DIR, npx melchizedek-doctor, --json, --check, npx melchizedek-chat --syndicate, CHAT_STREAMING=false, --bind, --bindings, {{current_date}}, GOOGLE_GENAI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, ALLOW_PRIVATE_MCP=true, X_BEARER_TOKEN, scribe.yaml, tutor.yaml, augustin.yaml, syndicate.yaml
71
+ IDENTIFIERS (verbatim): config/agents/, config/agents/examples/, node_modules/melchizedek-agents/config/agents/examples/, MELCHIZEDEK_AGENTS_DIR, npx melchizedek-doctor, --json, --check, npx melchizedek-chat --syndicate, CHAT_STREAMING=false, --bind, --bindings, {{current_date}}, GOOGLE_GENAI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, MOONSHOT_API_KEY, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, ALLOW_PRIVATE_MCP=true, X_BEARER_TOKEN, scribe.yaml, tutor.yaml, augustin.yaml, syndicate.yaml
72
72
  LIMITS: the starter-pack section is a bulleted list, one line per file, file name in backticks first. Body under 170 lines.
@@ -78,6 +78,7 @@ Provide keys in `.env` or in the environment:
78
78
  - `ANTHROPIC_API_KEY` for claude-* ids.
79
79
  - `OPENAI_API_KEY` for gpt-* ids.
80
80
  - `XAI_API_KEY` for grok-* ids.
81
+ - `MOONSHOT_API_KEY` for kimi-* ids.
81
82
  - No key for ollama/* ids; start Ollama and pull the model.
82
83
 
83
84
  The `melchizedek-models` skill covers provider details and the gateway fallback.
@@ -147,7 +148,7 @@ CHAT_STREAMING=false npx melchizedek-chat --syndicate scribe -- "$(cat brief.md)
147
148
  ## When something fails
148
149
 
149
150
  - `Gemini API Key is not configured`: `.env` is missing or `GOOGLE_GENAI_API_KEY` is unset.
150
- - `Model not found` for a claude-*, gpt-*, or grok-* id: you did not set that provider's key, so the framework did not register the provider; run `npx melchizedek-doctor` to see the missing variable.
151
+ - `Model not found` for a claude-*, gpt-*, grok-*, or kimi-* id: you did not set that provider's key, so the framework did not register the provider; run `npx melchizedek-doctor` to see the missing variable.
151
152
  - `OLLAMA_UNREACHABLE` for ollama/* ids: Ollama is not running, or you have not pulled the target model (`ollama list`).
152
153
  - `[400] Tool call context circulation is not enabled`: the agent uses a `model:` too old for agent transfer; update the agent to `gemini-3.8-flash` or newer.
153
154
  - `Refusing to connect to private/loopback MCP host`: the SSRF guard blocked the connection; for a local MCP server, set `ALLOW_PRIVATE_MCP=true` in `.env`.
@@ -45,7 +45,8 @@ Agent block keys for the orchestrator and each subagent:
45
45
  - `code_execution: gemini` lets a Gemini agent write and run Python in Google's sandbox (nothing runs on your machine). `context: { compact_after_tokens: 60000 }` on a delegate orchestrator summarizes a long conversation instead of overflowing it. `mode: task` on a workflow node makes its output its `finish_task` arguments.
46
46
  - `openapi:` a list of OpenAPI 3 spec files (relative to the syndicate file) whose operations become the agent's tools; only GET operations unless `operations` names others; `auth` reads `bearer_env` or `api_key: {env, in, name}` from the environment. Read `weather.yaml` for the worked example.
47
47
  - `mcp_server_url:`: URL on a subagent to discover MCP tools at start. The melchizedek-serve skill covers MCP servers.
48
- - `generateContentConfig:`: per-agent options: `temperature`, `maxOutputTokens`, `responseMimeType: "application/json"`, and `thinkingConfig: { thinkingLevel: MEDIUM, includeThoughts: false }` on Gemini models that think. Thinking tokens count against `maxOutputTokens`. The Scribe uses 24576 for `maxOutputTokens`.
48
+ - `reasoning:`: how hard the agent reasons, on any provider: `none`, `low`, `medium`, `high`, or `{ budget_tokens: 4096 }`. The compiler sends each provider the field it reads, so the setting survives a change to the `model:` line. Leave it unset to keep the provider's default. The templates in `config/agents/templates/` give a gathering or routing agent `none` and a synthesizing agent `low` or `medium`. The melchizedek-models skill covers what each level becomes per provider and per Claude model generation.
49
+ - `generateContentConfig:`: per-agent options: `temperature`, `maxOutputTokens`, `responseMimeType: "application/json"`. Thinking tokens count against `maxOutputTokens`, so a reasoning agent with long output needs a high ceiling. The Scribe uses 24576 for `maxOutputTokens`. `thinkingConfig` and `reasoningEffort` inside this block are the older, provider-specific spelling of `reasoning:`. Some starter-pack examples still use them and they still load, but an agent that sets either beside `reasoning:` fails to load. Write new agents with `reasoning:`.
49
50
  - `outputSchema:`: a JSON schema using uppercase types (`OBJECT`, `STRING`, `INTEGER`, `BOOLEAN`, `ARRAY`) that the agent reply must satisfy.
50
51
 
51
52
  Subagents receive one string argument, `query`, and see nothing else of the conversation. The orchestrator instruction must state what to pass them in full every time.
@@ -34,6 +34,7 @@ orchestrator:
34
34
  3. <Compose the reply from what came back.>
35
35
  </execution_framework>
36
36
  </prompt_instructions>
37
+ reasoning: low # none | low | medium | high, on any provider (melchizedek-models)
37
38
  generateContentConfig:
38
39
  maxOutputTokens: 4096
39
40
 
@@ -47,6 +48,7 @@ subagents:
47
48
  instruction: |
48
49
  You are Worker. <The one job, its inputs, and the exact shape of the
49
50
  report you return.>
51
+ reasoning: none # a gathering agent; raise it when the job is judgment
50
52
  generateContentConfig:
51
53
  maxOutputTokens: 2048
52
54
  # For structured output put the schema HERE, on a leaf with no tools —