melchizedek-agents 0.19.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 (106) hide show
  1. package/CHANGELOG.md +252 -76
  2. package/DOCUMENTATION.md +44 -15
  3. package/QUICKSTART.md +9 -6
  4. package/README.md +6 -9
  5. package/config/agents/syndicateSchema.yaml +3 -0
  6. package/dist/lib/adkPeer.d.ts +63 -0
  7. package/dist/lib/adkPeer.js +240 -0
  8. package/dist/lib/compile.d.ts +40 -2
  9. package/dist/lib/compile.js +80 -12
  10. package/dist/lib/compileAdk.d.ts +7 -4
  11. package/dist/lib/compileAdk.js +22 -12
  12. package/dist/lib/compileNative.d.ts +28 -2
  13. package/dist/lib/compileNative.js +60 -4
  14. package/dist/lib/doctor.d.ts +28 -1
  15. package/dist/lib/doctor.js +63 -2
  16. package/dist/lib/index.d.ts +4 -0
  17. package/dist/lib/index.js +5 -0
  18. package/dist/lib/models/adapterResolver.d.ts +13 -11
  19. package/dist/lib/models/adapterResolver.js +11 -10
  20. package/dist/lib/models/adkGeminiAdapter.js +2 -0
  21. package/dist/lib/models/adkShim.d.ts +1 -1
  22. package/dist/lib/models/adkShim.js +1 -1
  23. package/dist/lib/models/capabilities.js +13 -10
  24. package/dist/lib/models/claudeLlm.js +2 -2
  25. package/dist/lib/models/fallback.d.ts +1 -1
  26. package/dist/lib/models/fallback.js +1 -1
  27. package/dist/lib/models/gatewayLlm.js +2 -2
  28. package/dist/lib/models/geminiAdapter.d.ts +6 -4
  29. package/dist/lib/models/geminiAdapter.js +28 -5
  30. package/dist/lib/models/geminiState.d.ts +21 -0
  31. package/dist/lib/models/geminiState.js +26 -0
  32. package/dist/lib/models/genaiMapping.d.ts +24 -4
  33. package/dist/lib/models/genaiMapping.js +109 -6
  34. package/dist/lib/models/gptLlm.js +2 -2
  35. package/dist/lib/models/grokLlm.js +2 -2
  36. package/dist/lib/models/kimiLlm.js +2 -2
  37. package/dist/lib/models/ollamaLlm.js +2 -2
  38. package/dist/lib/models/registry.d.ts +5 -1
  39. package/dist/lib/models/registry.js +17 -12
  40. package/dist/lib/models/tracedGemini.d.ts +1 -1
  41. package/dist/lib/models/tracedGemini.js +1 -1
  42. package/dist/lib/runtime/adkSessionBridge.d.ts +1 -1
  43. package/dist/lib/runtime/adkSessionBridge.js +1 -1
  44. package/dist/lib/runtime/approvals.d.ts +5 -0
  45. package/dist/lib/runtime/approvals.js +39 -8
  46. package/dist/lib/runtime/credentials.d.ts +3 -1
  47. package/dist/lib/runtime/credentials.js +6 -1
  48. package/dist/lib/runtime/native/agentLoop.d.ts +7 -1
  49. package/dist/lib/runtime/native/agentLoop.js +31 -13
  50. package/dist/lib/runtime/native/delegate.d.ts +53 -0
  51. package/dist/lib/runtime/native/delegate.js +91 -8
  52. package/dist/lib/runtime/native/interrupts.d.ts +6 -3
  53. package/dist/lib/runtime/native/interrupts.js +23 -4
  54. package/dist/lib/runtime/native/request.d.ts +1 -1
  55. package/dist/lib/runtime/native/request.js +1 -1
  56. package/dist/lib/runtime/native/selfCorrection.d.ts +64 -6
  57. package/dist/lib/runtime/native/selfCorrection.js +82 -8
  58. package/dist/lib/runtime/native/step.d.ts +14 -6
  59. package/dist/lib/runtime/native/step.js +34 -10
  60. package/dist/lib/runtime/runtimeFlag.d.ts +13 -5
  61. package/dist/lib/runtime/runtimeFlag.js +14 -6
  62. package/dist/lib/runtime/syndicateTurn.d.ts +15 -10
  63. package/dist/lib/runtime/syndicateTurn.js +59 -32
  64. package/dist/lib/runtime/valueDepth.d.ts +23 -0
  65. package/dist/lib/runtime/valueDepth.js +41 -0
  66. package/dist/lib/session/supabaseSessionService.d.ts +1 -1
  67. package/dist/lib/session/supabaseSessionService.js +1 -1
  68. package/dist/lib/session/transcript.d.ts +1 -1
  69. package/dist/lib/session/transcript.js +5 -3
  70. package/dist/lib/storage/postgres/sessionService.d.ts +1 -1
  71. package/dist/lib/storage/postgres/sessionService.js +1 -1
  72. package/dist/lib/syndicateSchema.d.ts +2 -0
  73. package/dist/lib/syndicateSchema.js +27 -6
  74. package/dist/lib/toolRegistry.d.ts +12 -1
  75. package/dist/lib/toolRegistry.js +42 -2
  76. package/dist/lib/tools/adkTool.d.ts +1 -1
  77. package/dist/lib/tools/adkTool.js +1 -1
  78. package/dist/lib/tools/collectionsSearchTool.d.ts +1 -1
  79. package/dist/lib/tools/collectionsSearchTool.js +1 -1
  80. package/dist/lib/tools/generateImageTool.d.ts +1 -1
  81. package/dist/lib/tools/inspectImageTool.d.ts +1 -1
  82. package/dist/lib/tools/urlContextTool.d.ts +1 -1
  83. package/dist/lib/tools/urlContextTool.js +1 -1
  84. package/dist/lib/tools/webExtractTool.d.ts +1 -1
  85. package/dist/lib/tools/webSearchTool.d.ts +1 -1
  86. package/dist/lib/tools/webSearchTool.js +1 -1
  87. package/dist/lib/tools/xApiSearchTool.d.ts +1 -1
  88. package/dist/lib/tools/xSearchTool.d.ts +1 -1
  89. package/dist/lib/tools/xSearchTool.js +1 -1
  90. package/dist/lib/wiki/agentRun.d.ts +1 -1
  91. package/dist/lib/wiki/agentRun.js +2 -1
  92. package/dist/lib/workflow/agentNode.d.ts +21 -4
  93. package/dist/lib/workflow/agentNode.js +96 -5
  94. package/dist/lib/workflow/graph.d.ts +5 -4
  95. package/dist/lib/workflow/graph.js +14 -4
  96. package/dist/lib/workflow/resume.d.ts +10 -9
  97. package/dist/lib/workflow/resume.js +13 -12
  98. package/dist/lib/workflow/scheduler.d.ts +10 -7
  99. package/dist/lib/workflow/scheduler.js +15 -2
  100. package/dist/lib/workflow.d.ts +33 -11
  101. package/dist/lib/workflow.js +49 -27
  102. package/dist/lib/workflowConfig.d.ts +4 -0
  103. package/dist/lib/workflowConfig.js +4 -0
  104. package/dist/scripts/doctor.d.ts +3 -1
  105. package/dist/scripts/doctor.js +4 -2
  106. package/package.json +6 -3
@@ -0,0 +1,240 @@
1
+ /**
2
+ * lib/adkPeer.ts — @google/adk as an optional peer (ADR 0102).
3
+ *
4
+ * WHY this file exists:
5
+ * From 0.20.0 the native runtime is the default, and @google/adk is needed
6
+ * only for MELCHIZEDEK_RUNTIME=adk and the features only ADK runs. Until
7
+ * 1.0.0 removes ADK (ADR 0045), the engine's objects still carry an ADK
8
+ * face: the tool registry holds FunctionTools that carry their own Tool,
9
+ * the model shims are BaseLlm classes that carry their contract adapter,
10
+ * and the durable session stores are ADK BaseSessionServices as well as
11
+ * the engine's SessionService. Those classes extend ADK's at load time.
12
+ *
13
+ * This module is the one place the package loads ADK. It tries once, when
14
+ * it is first imported, and every other module takes ADK's values from
15
+ * here, never from '@google/adk' directly (a type-only import is fine):
16
+ * - When ADK is installed, each export below IS ADK's own value, so the
17
+ * ADK runtime, `instanceof` and ADK's registry behave exactly as before.
18
+ * - When it is not, the base classes are stand-ins: they construct as
19
+ * ADK's do (a tool's name, a model's id, a toolset's filter) and carry
20
+ * ADK's own Symbol.for marks, so the engine reads them as it reads ADK's,
21
+ * and every method only ADK calls throws AdkNotInstalledError, which
22
+ * names the package. Everything else (`Runner`, `LlmAgent`, the plugins)
23
+ * is reached through requireAdk(feature), which throws the same error.
24
+ *
25
+ * A package that is installed but fails to load for another reason (a
26
+ * missing dependency of its own, a syntax error) is not "absent": that
27
+ * error is rethrown, so nothing hides a broken install.
28
+ *
29
+ * The load is a top-level await of a dynamic import: an ES module cannot
30
+ * load another synchronously, and the exported classes must exist before
31
+ * any subclass is defined.
32
+ */
33
+ /** The package this module loads. */
34
+ export const ADK_PACKAGE = '@google/adk';
35
+ /** Whether `err` says the package itself could not be found (and not a module it imports). */
36
+ function isMissingPackage(err) {
37
+ const e = err;
38
+ if (!e || typeof e !== 'object')
39
+ return false;
40
+ if (e.code !== 'ERR_MODULE_NOT_FOUND' && e.code !== 'MODULE_NOT_FOUND')
41
+ return false;
42
+ return typeof e.message === 'string' && e.message.includes(`'${ADK_PACKAGE}'`);
43
+ }
44
+ let loaded;
45
+ try {
46
+ loaded = await import('@google/adk');
47
+ }
48
+ catch (err) {
49
+ if (!isMissingPackage(err))
50
+ throw err;
51
+ }
52
+ /** ADK's module when it is installed, else undefined. */
53
+ export const adk = loaded;
54
+ /** Whether @google/adk is installed and loaded. */
55
+ export function adkInstalled() {
56
+ return loaded !== undefined;
57
+ }
58
+ /** The installed @google/adk version, read from its package.json, or undefined when it is not installed. */
59
+ export async function adkVersion() {
60
+ if (!loaded)
61
+ return undefined;
62
+ try {
63
+ const { createRequire } = await import('node:module');
64
+ const require = createRequire(import.meta.url);
65
+ const fs = await import('node:fs');
66
+ const path = await import('node:path');
67
+ // The package's exports map hides package.json, so walk up from its entry.
68
+ let dir = path.dirname(require.resolve(ADK_PACKAGE));
69
+ for (let i = 0; i < 6; i++) {
70
+ const file = path.join(dir, 'package.json');
71
+ if (fs.existsSync(file)) {
72
+ const pkg = JSON.parse(fs.readFileSync(file, 'utf8'));
73
+ if (pkg.name === ADK_PACKAGE)
74
+ return pkg.version;
75
+ }
76
+ dir = path.dirname(dir);
77
+ }
78
+ }
79
+ catch {
80
+ // Unreadable: the version is unknown, not the package absent.
81
+ }
82
+ return undefined;
83
+ }
84
+ /** A feature only ADK runs was used without @google/adk installed. */
85
+ export class AdkNotInstalledError extends Error {
86
+ feature;
87
+ constructor(feature) {
88
+ super(`${feature} needs ${ADK_PACKAGE}, which is not installed. ` +
89
+ `Install it beside melchizedek-agents (npm install ${ADK_PACKAGE}@~2.2.0), ` +
90
+ `or run on the native runtime (the default; unset MELCHIZEDEK_RUNTIME or set it to native).`);
91
+ this.name = 'AdkNotInstalledError';
92
+ this.feature = feature;
93
+ }
94
+ }
95
+ /** ADK's module, or AdkNotInstalledError naming `feature` when it is not installed. */
96
+ export function requireAdk(feature) {
97
+ if (!loaded)
98
+ throw new AdkNotInstalledError(feature);
99
+ return loaded;
100
+ }
101
+ // ── Stand-ins for ADK's base classes ─────────────────────────────────────────
102
+ //
103
+ // Each matches the ADK constructor's effect on the instance and ADK's
104
+ // Symbol.for mark, nothing more. They exist only when ADK is not installed.
105
+ const BASE_TOOL = Symbol.for('google.adk.baseTool');
106
+ const IN_MODEL_TOOL = Symbol.for('google.adk.inModelTool');
107
+ const FUNCTION_TOOL = Symbol.for('google.adk.functionTool');
108
+ const BASE_TOOLSET = Symbol.for('google.adk.baseToolset');
109
+ const BASE_MODEL = Symbol.for('google.adk.baseModel');
110
+ const GEMINI_MODEL = Symbol.for('google.adk.geminiModel');
111
+ function adkOnly(what) {
112
+ throw new AdkNotInstalledError(what);
113
+ }
114
+ class MissingBaseTool {
115
+ [BASE_TOOL] = true;
116
+ name;
117
+ description;
118
+ isLongRunning;
119
+ constructor(params) {
120
+ this.name = params.name;
121
+ this.description = params.description;
122
+ this.isLongRunning = params.isLongRunning ?? false;
123
+ }
124
+ _getDeclaration() {
125
+ return undefined;
126
+ }
127
+ async checkRequireConfirmation() {
128
+ return false;
129
+ }
130
+ async processLlmRequest(_request) {
131
+ adkOnly(`The ADK face of the tool '${this.name}'`);
132
+ }
133
+ async runAsync(_request) {
134
+ return adkOnly(`The ADK face of the tool '${this.name}'`);
135
+ }
136
+ }
137
+ class MissingFunctionTool extends MissingBaseTool {
138
+ [FUNCTION_TOOL] = true;
139
+ execute;
140
+ parameters;
141
+ requireConfirmation;
142
+ constructor(options) {
143
+ const name = options.name ?? options.execute.name;
144
+ if (!name)
145
+ throw new Error('Tool name cannot be empty. Either name the `execute` function or provide a `name`.');
146
+ super({ name, description: options.description, isLongRunning: options.isLongRunning });
147
+ this.execute = options.execute;
148
+ this.parameters = options.parameters;
149
+ this.requireConfirmation = options.requireConfirmation ?? false;
150
+ }
151
+ }
152
+ class MissingBaseToolset {
153
+ [BASE_TOOLSET] = true;
154
+ toolFilter;
155
+ prefix;
156
+ constructor(toolFilter, prefix) {
157
+ this.toolFilter = toolFilter;
158
+ this.prefix = prefix;
159
+ }
160
+ async getTools() {
161
+ return adkOnly('An ADK toolset');
162
+ }
163
+ async close() { }
164
+ }
165
+ class MissingBaseLlm {
166
+ [BASE_MODEL] = true;
167
+ model;
168
+ static supportedModels = [];
169
+ constructor({ model }) {
170
+ this.model = model;
171
+ }
172
+ generateContentAsync() {
173
+ return adkOnly(`The ADK face of the model '${this.model}'`);
174
+ }
175
+ async connect() {
176
+ return adkOnly(`The ADK face of the model '${this.model}'`);
177
+ }
178
+ }
179
+ /**
180
+ * ADK's Gemini, absent: it holds what a caller gave it (the model, a key,
181
+ * Vertex AI's settings), as ADK's does, so the native runtime reads it as it
182
+ * reads TracedGemini (lib/compileNative.ts). Calling it throws.
183
+ */
184
+ class MissingGemini extends MissingBaseLlm {
185
+ [GEMINI_MODEL] = true;
186
+ apiKey;
187
+ vertexai;
188
+ project;
189
+ location;
190
+ headers;
191
+ constructor(params) {
192
+ super({ model: params.model ?? 'gemini-2.5-flash' });
193
+ if (params.apiKey !== undefined)
194
+ this.apiKey = params.apiKey;
195
+ if (params.vertexai !== undefined)
196
+ this.vertexai = params.vertexai;
197
+ if (params.project !== undefined)
198
+ this.project = params.project;
199
+ if (params.location !== undefined)
200
+ this.location = params.location;
201
+ if (params.headers !== undefined)
202
+ this.headers = params.headers;
203
+ }
204
+ }
205
+ /**
206
+ * ADK's session service base, absent. A store that extends it is still the
207
+ * engine's SessionService; only the ADK face (the base's appendEvent, which
208
+ * ADK's Runner reaches) needs ADK.
209
+ */
210
+ class MissingBaseSessionService {
211
+ async appendEvent(_request) {
212
+ return adkOnly('The ADK face of a session store');
213
+ }
214
+ /** ADK's own: the session when it exists, else a new one, through the store's methods. */
215
+ async getOrCreateSession(request) {
216
+ const self = this;
217
+ if (!request.sessionId)
218
+ return self.createSession(request);
219
+ const session = await self.getSession({ appName: request.appName, userId: request.userId, sessionId: request.sessionId });
220
+ return session ?? self.createSession(request);
221
+ }
222
+ }
223
+ /** ADK's GOOGLE_SEARCH, absent: an in-model tool named google_search, which the engine reads by ADK's mark. */
224
+ class MissingGoogleSearchTool extends MissingBaseTool {
225
+ [IN_MODEL_TOOL] = true;
226
+ constructor() {
227
+ super({ name: 'google_search', description: 'Google Search Tool' });
228
+ }
229
+ }
230
+ // ── The values every other module imports ────────────────────────────────────
231
+ //
232
+ // A type of the same name sits beside each value, so `import { BaseTool }`
233
+ // from here serves as both, as it did from '@google/adk'.
234
+ export const BaseTool = loaded?.BaseTool ?? MissingBaseTool;
235
+ export const FunctionTool = loaded?.FunctionTool ?? MissingFunctionTool;
236
+ export const BaseToolset = loaded?.BaseToolset ?? MissingBaseToolset;
237
+ export const BaseLlm = loaded?.BaseLlm ?? MissingBaseLlm;
238
+ export const Gemini = loaded?.Gemini ?? MissingGemini;
239
+ export const BaseSessionService = loaded?.BaseSessionService ?? MissingBaseSessionService;
240
+ export const GOOGLE_SEARCH = loaded?.GOOGLE_SEARCH ?? new MissingGoogleSearchTool();
@@ -37,7 +37,7 @@
37
37
  * the native loop's NativeAgent (ADR 0045). runSyndicateTurn picks one by
38
38
  * MELCHIZEDEK_RUNTIME or its `runtime` option.
39
39
  */
40
- import { BaseLlm, FunctionTool } from '@google/adk';
40
+ import { BaseLlm, FunctionTool } from './adkPeer.ts';
41
41
  import type { BaseTool, LlmAgent } from '@google/adk';
42
42
  import type { ReasoningSetting, SubagentYamlConfig, SyndicateYamlConfig } from './loadSyndicate.ts';
43
43
  import type { ExampleConfig } from './tools/examples.ts';
@@ -122,7 +122,9 @@ export declare function agentGates(agent: {
122
122
  * period an ADK object that carries its own Tool or InstructionTool
123
123
  * (lib/tools/adkTool.ts), an MCP tool or the skills toolset, gated by
124
124
  * require_approval where the YAML says so. `agent` is a delegated subagent
125
- * (DELEGATE mode), `remote` a subagent served over A2A.
125
+ * (DELEGATE mode), `remote` a subagent served over A2A, `workflow` a
126
+ * `yaml_reference` to a workflow syndicate, whose whole graph is the tool
127
+ * (ADR 0098).
126
128
  */
127
129
  export type SpecTool = {
128
130
  kind: 'tool';
@@ -135,7 +137,32 @@ export type SpecTool = {
135
137
  name: string;
136
138
  description: string;
137
139
  url: string;
140
+ } | {
141
+ kind: 'workflow';
142
+ workflow: WorkflowSpec;
138
143
  };
144
+ /**
145
+ * A workflow syndicate, compiled for either runtime: lib/workflow.ts builds
146
+ * ADK's Workflow from it, lib/compileNative.ts the native walk's graph and
147
+ * agents. Nested as a subagent (a `yaml_reference` in DELEGATE mode), the
148
+ * graph runs under the entry's name and description, and that name is the
149
+ * root of every node path, as ADK names a Workflow's nodes after it.
150
+ */
151
+ export interface WorkflowSpec {
152
+ /** The workflow's name: the syndicate's, or the subagent entry's when nested. */
153
+ name: string;
154
+ /** The tool's description when nested; '' at the root. */
155
+ description: string;
156
+ /** The syndicate, its `syndicate_name` set to `name`. */
157
+ config: SyndicateYamlConfig;
158
+ /** Every agent of the graph, the orchestrator first, as compileSubagentSpec builds it, with its YAML entry. */
159
+ agents: Array<{
160
+ yaml: SubagentYamlConfig;
161
+ spec: AgentSpec;
162
+ }>;
163
+ /** The registry entry for a tool node's tool name, or undefined (CompileOptions.onUnknownTool applies). */
164
+ resolveTool: (name: string) => unknown;
165
+ }
139
166
  /**
140
167
  * One YAML agent, compiled, for either runtime: what lib/compileAdk.ts
141
168
  * turns into ADK's LlmAgent and lib/compileNative.ts into the native loop's
@@ -174,6 +201,17 @@ export interface AgentSpec {
174
201
  /** `mode: task`. */
175
202
  mode?: 'task';
176
203
  }
204
+ /**
205
+ * The spec of a workflow syndicate: every agent through compileSubagentSpec
206
+ * (the orchestrator first, as a node like any other), and the registry's
207
+ * lookup for its tool nodes. `name` and `description` name a nested one
208
+ * after its subagent entry (ADR 0098); `ref` is the file it came from.
209
+ *
210
+ * A nested workflow runs inside its caller's tool call, so a pause inside it
211
+ * could not reach the caller (ADR 0028, as for any nested syndicate): an
212
+ * `ask_user` node is refused by name here, on both runtimes.
213
+ */
214
+ export declare function compileWorkflowSpec(config: SyndicateYamlConfig, opts?: CompileOptions, name?: string, description?: string, ref?: string): Promise<WorkflowSpec>;
177
215
  /**
178
216
  * The spec of ONE agent from a subagent entry. A `yaml_reference` entry is
179
217
  * the nested syndicate's whole graph under this entry's name and
@@ -37,10 +37,11 @@
37
37
  * the native loop's NativeAgent (ADR 0045). runSyndicateTurn picks one by
38
38
  * MELCHIZEDEK_RUNTIME or its `runtime` option.
39
39
  */
40
- import { BaseLlm, FunctionTool } from '@google/adk';
40
+ import { BaseLlm, FunctionTool } from './adkPeer.js';
41
41
  import { relative } from 'node:path';
42
42
  import { compileAdk } from './compileAdk.js';
43
43
  import { isDispatchSyndicate } from './dispatch.js';
44
+ import { isWorkflowSyndicate, nodeKind } from './workflowConfig.js';
44
45
  import { loadSyndicate, nestedLoader } from './loadSyndicate.js';
45
46
  import { REASONING_OLDER_SPELLING } from './syndicateSchema.js';
46
47
  import { reasoningConfig } from './models/reasoning.js';
@@ -278,6 +279,71 @@ function specOf(yaml, name, description, instruction, tools, opts) {
278
279
  return spec;
279
280
  }
280
281
  const asTools = (tools) => tools.map((tool) => ({ kind: 'tool', tool }));
282
+ /**
283
+ * A nested `yaml_reference:` syndicate, loaded. A nested syndicate runs
284
+ * inside a tool call (or as a route whose own subagents do): a pause there
285
+ * cannot reach the caller (ADR 0028), so one that declares approval gates is
286
+ * refused.
287
+ */
288
+ /** Where the yaml_reference chain a compile is inside is kept: an own symbol on the options, so a spread copy keeps it. */
289
+ const NESTING = Symbol('melchizedek.compile.nesting');
290
+ /** The deepest a yaml_reference chain may go: well past any syndicate the engine ships, short of the stack. */
291
+ const MAX_NESTING_DEPTH = 16;
292
+ /**
293
+ * The options a nested `ref` compiles with: `opts` and the chain of
294
+ * references above it. A reference already on the chain (a syndicate that
295
+ * reaches itself) or a chain past MAX_NESTING_DEPTH is refused by name (WS5-5,
296
+ * ADR 0101): the compile would otherwise recurse until the stack gave out,
297
+ * which took the process with it.
298
+ */
299
+ function nestedOptions(ref, opts) {
300
+ const chain = (opts[NESTING] ?? []);
301
+ if (chain.includes(ref))
302
+ throw new Error(`${ref}: a nested syndicate reaches itself (${[...chain, ref].join(' → ')}); a yaml_reference chain must end.`);
303
+ if (chain.length >= MAX_NESTING_DEPTH)
304
+ throw new Error(`${ref}: nested syndicates go deeper than ${MAX_NESTING_DEPTH} levels (${[...chain, ref].join(' → ')}).`);
305
+ return { ...opts, [NESTING]: [...chain, ref] };
306
+ }
307
+ function loadNestedSyndicate(ref, opts) {
308
+ opts.log?.(`Loading nested syndicate: ${ref}`);
309
+ const nested = (opts.loadNested ?? loadSyndicate)(ref);
310
+ if (declaresApprovals(nested)) {
311
+ throw new Error(`${ref}: approval gates (require_approval) are not supported inside a nested syndicate.`);
312
+ }
313
+ return nested;
314
+ }
315
+ /**
316
+ * The spec of a workflow syndicate: every agent through compileSubagentSpec
317
+ * (the orchestrator first, as a node like any other), and the registry's
318
+ * lookup for its tool nodes. `name` and `description` name a nested one
319
+ * after its subagent entry (ADR 0098); `ref` is the file it came from.
320
+ *
321
+ * A nested workflow runs inside its caller's tool call, so a pause inside it
322
+ * could not reach the caller (ADR 0028, as for any nested syndicate): an
323
+ * `ask_user` node is refused by name here, on both runtimes.
324
+ */
325
+ export async function compileWorkflowSpec(config, opts = {}, name, description, ref) {
326
+ if (!isWorkflowSyndicate(config))
327
+ throw new Error(`${config.syndicate_name}: no workflow block`);
328
+ if (name) {
329
+ const asking = Object.entries(config.workflow.nodes ?? {}).find(([, node]) => nodeKind(node) === 'ask_user');
330
+ if (asking) {
331
+ throw new Error(`${ref ?? config.syndicate_name}: the ask_user node '${asking[0]}' pauses for a person, which a workflow nested as a subagent (${name}) cannot carry to its caller; run the workflow as its own syndicate, or remove the node.`);
332
+ }
333
+ }
334
+ const workflowName = name || config.syndicate_name;
335
+ const agents = [];
336
+ for (const yaml of [{ description: '', ...config.orchestrator }, ...(config.subagents ?? [])]) {
337
+ agents.push({ yaml, spec: await compileSubagentSpec(yaml, opts) });
338
+ }
339
+ return {
340
+ name: workflowName,
341
+ description: description ?? '',
342
+ config: { ...config, syndicate_name: workflowName },
343
+ agents,
344
+ resolveTool: (tool) => resolveNamedTools([tool], opts.onUnknownTool)[0],
345
+ };
346
+ }
281
347
  /**
282
348
  * The spec of ONE agent from a subagent entry. A `yaml_reference` entry is
283
349
  * the nested syndicate's whole graph under this entry's name and
@@ -291,14 +357,8 @@ export async function compileSubagentSpec(subCfg, opts = {}) {
291
357
  throw new Error(`'${subCfg.name}' is a remote A2A agent (a2a_agent_url) and has no local agent to compile.`);
292
358
  }
293
359
  if (subCfg.yaml_reference) {
294
- opts.log?.(`Loading nested syndicate: ${subCfg.yaml_reference}`);
295
- const nested = (opts.loadNested ?? loadSyndicate)(subCfg.yaml_reference);
296
- if (declaresApprovals(nested)) {
297
- // A nested syndicate runs inside a tool call (or as a route whose
298
- // own subagents do): a pause there cannot reach the caller (ADR 0028).
299
- throw new Error(`${subCfg.yaml_reference}: approval gates (require_approval) are not supported inside a nested syndicate.`);
300
- }
301
- return compileSpec(nested, opts, subCfg.name, subCfg.description);
360
+ const nestedOpts = nestedOptions(subCfg.yaml_reference, opts);
361
+ return compileSpec(loadNestedSyndicate(subCfg.yaml_reference, nestedOpts), nestedOpts, subCfg.name, subCfg.description);
302
362
  }
303
363
  const gated = gateTools(await resolveAgentTools(subCfg.tools, subCfg.mcp_server_url, opts, subCfg.openapi, subCfg.examples, subCfg.mcp_tools), subCfg.require_approval, subCfg.name);
304
364
  const { instruction, tools } = await withSkills(subCfg.instruction ?? '', gated, subCfg.skills, subCfg.name, opts);
@@ -317,13 +377,14 @@ export async function compileSpec(config, opts = {}, overrideName, overrideDescr
317
377
  if (config.bundled_references && !opts.loadNested)
318
378
  opts = { ...opts, loadNested: nestedLoader(config) };
319
379
  // A graph has no orchestrator-with-tools shape to build: its agents are
320
- // nodes (lib/workflow.ts). Nested as a yaml_reference, only its
321
- // orchestrator runs, since ADK cannot yet make a Workflow a subagent.
380
+ // nodes (lib/workflow.ts). A delegated yaml_reference to one runs the whole
381
+ // graph as the tool (nestedWorkflowSpec, ADR 0098); as a dispatch route or
382
+ // a workflow node it is still its orchestrator alone.
322
383
  if (config.workflow && !overrideName) {
323
384
  throw new Error(`${config.syndicate_name}: a workflow syndicate is compiled with compileWorkflow (lib/workflow.ts), not compileGraph`);
324
385
  }
325
386
  if (config.workflow)
326
- opts.log?.(`${config.syndicate_name}: nested as a subagent, so only its orchestrator runs (a Workflow cannot be a subagent yet)`);
387
+ opts.log?.(`${config.syndicate_name}: a dispatch route or a workflow node, so only its orchestrator runs (a delegated subagent runs the whole graph)`);
327
388
  const delegated = isDispatchSyndicate(config)
328
389
  ? []
329
390
  : await Promise.all((config.subagents ?? []).map(async (subCfg) => {
@@ -331,6 +392,13 @@ export async function compileSpec(config, opts = {}, overrideName, overrideDescr
331
392
  opts.log?.(`Remote A2A agent: ${subCfg.name} → ${subCfg.a2a_agent_url}`);
332
393
  return { kind: 'remote', name: subCfg.name, description: subCfg.description, url: subCfg.a2a_agent_url };
333
394
  }
395
+ if (subCfg.yaml_reference) {
396
+ const nestedOpts = nestedOptions(subCfg.yaml_reference, opts);
397
+ const nested = loadNestedSyndicate(subCfg.yaml_reference, nestedOpts);
398
+ if (isWorkflowSyndicate(nested))
399
+ return { kind: 'workflow', workflow: await compileWorkflowSpec(nested, nestedOpts, subCfg.name, subCfg.description, subCfg.yaml_reference) };
400
+ return { kind: 'agent', agent: await compileSpec(nested, nestedOpts, subCfg.name, subCfg.description) };
401
+ }
334
402
  return { kind: 'agent', agent: await compileSubagentSpec(subCfg, opts) };
335
403
  }));
336
404
  const name = overrideName || config.orchestrator.name;
@@ -8,16 +8,19 @@
8
8
  * model resolved once and its generateContentConfig built for that model.
9
9
  * This file adds what only ADK's LlmAgent takes: the model wrapped in a
10
10
  * FallbackLlm for `fallback_model:`, each delegated subagent as an
11
- * AgentTool (a remote one as its A2A tool), Gemini's code executor, the
11
+ * AgentTool (a nested workflow's whole Workflow too; a remote one as its
12
+ * A2A tool), Gemini's code executor, the
12
13
  * context compactor, task mode and a workflow node's settings. Every field
13
14
  * reaches LlmAgent as compileGraph and compileSubagent always passed it, so
14
15
  * the ADK runtime runs the agent it ran before the split.
15
16
  * lib/compileNative.ts builds the native loop's agent from the same spec.
16
17
  *
17
- * Imports from lib/compile.ts are types only, so the two modules load in
18
- * either order.
18
+ * Imports from lib/compile.ts are types only. A nested workflow's Workflow
19
+ * comes from lib/workflow.ts (assembleWorkflow, ADR 0098), which imports
20
+ * lib/compile.ts; every use of either is inside a function, so the modules
21
+ * load in any order.
19
22
  */
20
- import { LlmAgent } from '@google/adk';
23
+ import type { LlmAgent } from '@google/adk';
21
24
  import type { AgentSpec, CompileOptions } from './compile.ts';
22
25
  import { DEFAULT_KEEP_RECENT_EVENTS } from './runtime/native/compaction.ts';
23
26
  /** Events kept verbatim after a compaction summary when `context:` names none: the native compactor's default. */
@@ -8,17 +8,21 @@
8
8
  * model resolved once and its generateContentConfig built for that model.
9
9
  * This file adds what only ADK's LlmAgent takes: the model wrapped in a
10
10
  * FallbackLlm for `fallback_model:`, each delegated subagent as an
11
- * AgentTool (a remote one as its A2A tool), Gemini's code executor, the
11
+ * AgentTool (a nested workflow's whole Workflow too; a remote one as its
12
+ * A2A tool), Gemini's code executor, the
12
13
  * context compactor, task mode and a workflow node's settings. Every field
13
14
  * reaches LlmAgent as compileGraph and compileSubagent always passed it, so
14
15
  * the ADK runtime runs the agent it ran before the split.
15
16
  * lib/compileNative.ts builds the native loop's agent from the same spec.
16
17
  *
17
- * Imports from lib/compile.ts are types only, so the two modules load in
18
- * either order.
18
+ * Imports from lib/compile.ts are types only. A nested workflow's Workflow
19
+ * comes from lib/workflow.ts (assembleWorkflow, ADR 0098), which imports
20
+ * lib/compile.ts; every use of either is inside a function, so the modules
21
+ * load in any order.
19
22
  */
20
- import { AgentTool, BaseLlm, BuiltInCodeExecutor, LLMRegistry, LlmAgent, LlmSummarizer, LogLevel, TokenBasedContextCompactor, setLogLevel as setAdkLogLevel } from '@google/adk';
23
+ import { BaseLlm, adk, requireAdk } from './adkPeer.js';
21
24
  import { remoteAgentTool } from './a2a/remoteAgent.js';
25
+ import { assembleWorkflow } from './workflow.js';
22
26
  import { FallbackLlm } from './models/fallback.js';
23
27
  import { resolveModel as resolveRegistryModel } from './models/registry.js';
24
28
  import { onLogLevel } from './runtime/logging.js';
@@ -27,13 +31,13 @@ import { DEFAULT_KEEP_RECENT_EVENTS } from './runtime/native/compaction.js';
27
31
  * ADK's logger follows the engine's level (lib/runtime/logging.ts, ADR
28
32
  * 0080), so a surface sets one level and never names ADK to quiet it.
29
33
  */
30
- const ADK_LOG_LEVELS = {
31
- debug: LogLevel.DEBUG,
32
- info: LogLevel.INFO,
33
- warn: LogLevel.WARN,
34
- error: LogLevel.ERROR,
35
- };
36
- onLogLevel((level) => setAdkLogLevel(ADK_LOG_LEVELS[level]));
34
+ if (adk) {
35
+ const { LogLevel: Level, setLogLevel } = adk;
36
+ const levels = { debug: Level.DEBUG, info: Level.INFO, warn: Level.WARN, error: Level.ERROR };
37
+ onLogLevel((level) => setLogLevel(levels[level]));
38
+ }
39
+ /** What compileAdk builds needs @google/adk; without it, this names the package (ADR 0102). */
40
+ const FEATURE = 'Compiling an agent for the ADK runtime (MELCHIZEDEK_RUNTIME=adk, compileGraph, compileSubagent)';
37
41
  /** Events kept verbatim after a compaction summary when `context:` names none: the native compactor's default. */
38
42
  export { DEFAULT_KEEP_RECENT_EVENTS };
39
43
  /**
@@ -66,6 +70,7 @@ function passthroughFields(spec) {
66
70
  * node, its arguments become the node's output).
67
71
  */
68
72
  function executionFields(cfg, opts) {
73
+ const { BuiltInCodeExecutor, LLMRegistry, LlmSummarizer, TokenBasedContextCompactor } = requireAdk(FEATURE);
69
74
  const out = {};
70
75
  if (cfg.codeExecution === 'gemini')
71
76
  out.codeExecutor = new BuiltInCodeExecutor();
@@ -98,8 +103,9 @@ function withFallback(primary, fallbackId, opts) {
98
103
  const asLlm = (m) => (m instanceof BaseLlm ? m : resolveRegistryModel(String(m)));
99
104
  return new FallbackLlm(asLlm(primary), asLlm(resolve(fallbackId)), opts.log ?? ((m) => console.warn(m)));
100
105
  }
101
- /** The spec's tools as LlmAgent takes them: a delegated subagent as an AgentTool, a remote one as its A2A tool. */
106
+ /** The spec's tools as LlmAgent takes them: a delegated subagent (a nested workflow's Workflow included) as an AgentTool, a remote one as its A2A tool. */
102
107
  function adkTools(spec, opts) {
108
+ const { AgentTool } = requireAdk(FEATURE);
103
109
  return spec.tools.map((entry) => {
104
110
  switch (entry.kind) {
105
111
  case 'tool':
@@ -108,6 +114,9 @@ function adkTools(spec, opts) {
108
114
  return new AgentTool({ agent: compileAdk(entry.agent, opts) });
109
115
  case 'remote':
110
116
  return remoteAgentTool({ name: entry.name, description: entry.description, url: entry.url });
117
+ case 'workflow':
118
+ // ADK's AgentTool runs any node its Runner takes, a Workflow included: the whole graph, its last event's text the answer (ADR 0098).
119
+ return new AgentTool({ agent: assembleWorkflow(entry.workflow, opts).workflow });
111
120
  }
112
121
  });
113
122
  }
@@ -116,6 +125,7 @@ function adkTools(spec, opts) {
116
125
  * settings (CompileOptions.nodeConfig), which compileSubagent passes.
117
126
  */
118
127
  export function compileAdk(spec, opts = {}, extra = {}) {
128
+ const { LlmAgent } = requireAdk(FEATURE);
119
129
  const tools = adkTools(spec, opts);
120
130
  return new LlmAgent({
121
131
  name: spec.name,
@@ -16,7 +16,8 @@
16
16
  * runs by shape during the dual period (ADR 0071). A delegated subagent
17
17
  * becomes a subagentTool holding its own NativeAgent, which the loop runs as
18
18
  * a child loop (lib/runtime/native/delegate.ts, ADR 0074), a nested
19
- * syndicate as its orchestrator's agent; a remote A2A subagent is the own
19
+ * syndicate as its orchestrator's agent, a nested workflow syndicate as a
20
+ * workflowSubagentTool whose call walks the whole graph (ADR 0098); a remote A2A subagent is the own
20
21
  * Tool the ADK runtime's FunctionTool wraps (lib/a2a/remoteAgent.ts).
21
22
  *
22
23
  * EVERY AGENT KEY COMPILES for the native loop: `context:` is handed to
@@ -38,11 +39,13 @@
38
39
  * ADK classes are told apart by ADK's own Symbol.for marks, so this file
39
40
  * imports nothing from ADK.
40
41
  */
41
- import type { AgentSpec, CompileOptions } from './compile.ts';
42
+ import type { AgentSpec, CompileOptions, WorkflowSpec } from './compile.ts';
42
43
  import type { SubagentYamlConfig, SyndicateYamlConfig } from './loadSyndicate.ts';
43
44
  import type { ModelAdapter } from './models/contract.ts';
45
+ import type { WorkflowSubagent } from './runtime/native/delegate.ts';
44
46
  import type { NativeAgent } from './runtime/native/request.ts';
45
47
  export { UnsupportedOnRuntimeError, unsupportedOnNative } from './runtime/runtimeFlag.ts';
48
+ import type { WorkflowGraph } from './workflow/graph.ts';
46
49
  /**
47
50
  * The class name of an ADK model the native runtime cannot run, else
48
51
  * undefined: an ADK BaseLlm that is neither ADK's Gemini (TracedGemini
@@ -66,6 +69,29 @@ export declare function compileNative(spec: AgentSpec): NativeAgent;
66
69
  export declare function wireModelOf(spec: Pick<AgentSpec, 'modelId' | 'resolvedModel'>): string | undefined;
67
70
  /** A syndicate's orchestrator for the native loop: compileSpec, then compileNative. */
68
71
  export declare function compileNativeGraph(config: SyndicateYamlConfig, opts?: CompileOptions): Promise<NativeAgent>;
72
+ /** A workflow syndicate for the native walk (lib/workflow/turn.ts): its graph, every agent by YAML name, and the tool nodes' lookup. */
73
+ export interface NativeWorkflow {
74
+ name: string;
75
+ description: string;
76
+ graph: WorkflowGraph;
77
+ agents: Map<string, NativeAgent>;
78
+ resolveTool: (name: string) => unknown;
79
+ }
80
+ /**
81
+ * A workflow spec for the native walk: the graph, every agent compiled for
82
+ * native from the same specs ADK's assembleWorkflow builds its agents from,
83
+ * and the registry's lookup for tool nodes. A tool node ADK's compile
84
+ * refuses (an unregistered or long-running tool) is refused here, before
85
+ * any model call, with ADK's message.
86
+ */
87
+ export declare function compileNativeWorkflow(spec: WorkflowSpec): NativeWorkflow;
88
+ /**
89
+ * A nested workflow as the delegated subagent the native loop calls (ADR
90
+ * 0098): one call walks the whole graph with runNativeWorkflow on the child
91
+ * session lib/runtime/native/delegate.ts opened, as ADK's AgentTool runs a
92
+ * Workflow on its own Runner.
93
+ */
94
+ export declare function workflowSubagentOf(workflow: NativeWorkflow): WorkflowSubagent;
69
95
  /** One subagent entry (a dispatch route) for the native loop: compileSubagentSpec, then compileNative. */
70
96
  export declare function compileNativeSubagent(subCfg: SubagentYamlConfig, opts?: CompileOptions): Promise<NativeAgent>;
71
97
  /** What nativeAdapterFor reads from a spec: its model resolution, the other ids it calls, and its delegated subagents. */