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.
- package/.env.example +12 -0
- package/CHANGELOG.md +825 -0
- package/DOCUMENTATION.md +217 -59
- package/README.md +42 -1
- package/config/agents/examples/harness.yaml +3 -1
- package/config/agents/examples/model_zoo.yaml +10 -9
- package/config/agents/syndicateSchema.yaml +50 -16
- package/config/agents/templates/account_memory.yaml +2 -6
- package/config/agents/templates/case_desk.yaml +3 -9
- package/config/agents/templates/draft_review.yaml +3 -9
- package/config/agents/templates/intake_extractor.yaml +1 -3
- package/config/agents/templates/research_brief.yaml +3 -9
- package/config/agents/templates/research_desk.yaml +3 -9
- package/config/agents/templates/review_panel.yaml +4 -12
- package/config/agents/templates/support_triage.yaml +4 -12
- package/config/agents/templates/systems_operator.yaml +2 -6
- package/db/hardening.sql +7 -1
- package/db/migrations/0013_tool_credentials.sql +260 -0
- package/dist/lib/a2a/app.d.ts +70 -6
- package/dist/lib/a2a/app.js +107 -2
- package/dist/lib/a2a/executor.d.ts +20 -3
- package/dist/lib/a2a/executor.js +66 -7
- package/dist/lib/a2a/remoteAgent.d.ts +24 -9
- package/dist/lib/a2a/remoteAgent.js +31 -14
- package/dist/lib/compile.d.ts +121 -30
- package/dist/lib/compile.js +137 -124
- package/dist/lib/compileAdk.d.ts +29 -0
- package/dist/lib/compileAdk.js +132 -0
- package/dist/lib/compileNative.d.ts +85 -0
- package/dist/lib/compileNative.js +222 -0
- package/dist/lib/config.d.ts +6 -5
- package/dist/lib/config.js +6 -5
- package/dist/lib/doctor.js +1 -0
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.js +2 -1
- package/dist/lib/loadSyndicate.d.ts +18 -0
- package/dist/lib/memory/erase.d.ts +5 -3
- package/dist/lib/memory/erase.js +5 -2
- package/dist/lib/memory/namespace.d.ts +8 -1
- package/dist/lib/memory/namespace.js +12 -6
- package/dist/lib/memory/providers.js +2 -1
- package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
- package/dist/lib/memory/supabaseMemoryService.js +42 -2
- package/dist/lib/model.d.ts +47 -0
- package/dist/lib/model.js +37 -0
- package/dist/lib/models/adapterResolver.d.ts +113 -0
- package/dist/lib/models/adapterResolver.js +136 -0
- package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
- package/dist/lib/models/adkGeminiAdapter.js +367 -0
- package/dist/lib/models/adkShim.d.ts +111 -0
- package/dist/lib/models/adkShim.js +136 -0
- package/dist/lib/models/capabilities.d.ts +3 -0
- package/dist/lib/models/capabilities.js +20 -13
- package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
- package/dist/lib/models/chatCompletionsAdapter.js +854 -0
- package/dist/lib/models/circuitBreaker.d.ts +42 -0
- package/dist/lib/models/circuitBreaker.js +70 -0
- package/dist/lib/models/claudeAdapter.d.ts +103 -0
- package/dist/lib/models/claudeAdapter.js +669 -0
- package/dist/lib/models/claudeLlm.d.ts +39 -62
- package/dist/lib/models/claudeLlm.js +44 -349
- package/dist/lib/models/claudeModels.d.ts +127 -0
- package/dist/lib/models/claudeModels.js +237 -0
- package/dist/lib/models/contract.d.ts +395 -0
- package/dist/lib/models/contract.js +39 -0
- package/dist/lib/models/endpoints.js +2 -1
- package/dist/lib/models/errorResponse.d.ts +50 -0
- package/dist/lib/models/errorResponse.js +48 -0
- package/dist/lib/models/fallback.d.ts +17 -10
- package/dist/lib/models/fallback.js +53 -42
- package/dist/lib/models/fallbackAdapter.d.ts +55 -0
- package/dist/lib/models/fallbackAdapter.js +62 -0
- package/dist/lib/models/gateway.js +2 -1
- package/dist/lib/models/gatewayAdapter.d.ts +51 -0
- package/dist/lib/models/gatewayAdapter.js +81 -0
- package/dist/lib/models/gatewayLlm.d.ts +12 -33
- package/dist/lib/models/gatewayLlm.js +14 -75
- package/dist/lib/models/geminiAdapter.d.ts +123 -0
- package/dist/lib/models/geminiAdapter.js +831 -0
- package/dist/lib/models/geminiState.d.ts +25 -0
- package/dist/lib/models/geminiState.js +25 -0
- package/dist/lib/models/genaiMapping.d.ts +263 -0
- package/dist/lib/models/genaiMapping.js +862 -0
- package/dist/lib/models/gptAdapter.d.ts +240 -0
- package/dist/lib/models/gptAdapter.js +804 -0
- package/dist/lib/models/gptLlm.d.ts +63 -123
- package/dist/lib/models/gptLlm.js +88 -602
- package/dist/lib/models/grokAdapter.d.ts +86 -0
- package/dist/lib/models/grokAdapter.js +142 -0
- package/dist/lib/models/grokLlm.d.ts +22 -45
- package/dist/lib/models/grokLlm.js +22 -67
- package/dist/lib/models/kimiAdapter.d.ts +157 -0
- package/dist/lib/models/kimiAdapter.js +203 -0
- package/dist/lib/models/kimiLlm.d.ts +15 -107
- package/dist/lib/models/kimiLlm.js +17 -153
- package/dist/lib/models/ollamaAdapter.d.ts +125 -0
- package/dist/lib/models/ollamaAdapter.js +155 -0
- package/dist/lib/models/ollamaLlm.d.ts +11 -107
- package/dist/lib/models/ollamaLlm.js +13 -151
- package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
- package/dist/lib/models/openAiCompatibleLlm.js +109 -671
- package/dist/lib/models/providerState.d.ts +63 -0
- package/dist/lib/models/providerState.js +61 -0
- package/dist/lib/models/reasoning.d.ts +26 -0
- package/dist/lib/models/reasoning.js +59 -0
- package/dist/lib/models/registry.d.ts +58 -35
- package/dist/lib/models/registry.js +89 -118
- package/dist/lib/models/retry.d.ts +2 -1
- package/dist/lib/models/retry.js +3 -2
- package/dist/lib/models/schemaNormalize.d.ts +98 -0
- package/dist/lib/models/schemaNormalize.js +321 -1
- package/dist/lib/models/tracedGemini.d.ts +41 -0
- package/dist/lib/models/tracedGemini.js +98 -0
- package/dist/lib/models/urls.d.ts +11 -0
- package/dist/lib/models/urls.js +16 -0
- package/dist/lib/net/redirects.d.ts +21 -2
- package/dist/lib/net/redirects.js +11 -34
- package/dist/lib/observability/audit.d.ts +5 -1
- package/dist/lib/observability/lineage.d.ts +14 -0
- package/dist/lib/observability/lineage.js +33 -0
- package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
- package/dist/lib/observability/supabaseSpanExporter.js +17 -9
- package/dist/lib/observability/tracer.d.ts +26 -8
- package/dist/lib/observability/tracer.js +43 -18
- package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
- package/dist/lib/runtime/adkMemoryBridge.js +68 -0
- package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
- package/dist/lib/runtime/adkSessionBridge.js +176 -0
- package/dist/lib/runtime/credentials.d.ts +59 -0
- package/dist/lib/runtime/credentials.js +62 -0
- package/dist/lib/runtime/events.d.ts +229 -0
- package/dist/lib/runtime/events.js +274 -0
- package/dist/lib/runtime/logging.d.ts +32 -0
- package/dist/lib/runtime/logging.js +55 -0
- package/dist/lib/runtime/memoryService.d.ts +72 -0
- package/dist/lib/runtime/memoryService.js +25 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +203 -0
- package/dist/lib/runtime/native/agentLoop.js +687 -0
- package/dist/lib/runtime/native/compaction.d.ts +103 -0
- package/dist/lib/runtime/native/compaction.js +191 -0
- package/dist/lib/runtime/native/delegate.d.ts +98 -0
- package/dist/lib/runtime/native/delegate.js +173 -0
- package/dist/lib/runtime/native/history.d.ts +83 -0
- package/dist/lib/runtime/native/history.js +312 -0
- package/dist/lib/runtime/native/interrupts.d.ts +126 -0
- package/dist/lib/runtime/native/interrupts.js +291 -0
- package/dist/lib/runtime/native/request.d.ts +215 -0
- package/dist/lib/runtime/native/request.js +585 -0
- package/dist/lib/runtime/native/selfCorrection.d.ts +106 -0
- package/dist/lib/runtime/native/selfCorrection.js +253 -0
- package/dist/lib/runtime/native/step.d.ts +148 -0
- package/dist/lib/runtime/native/step.js +239 -0
- package/dist/lib/runtime/native/taskMode.d.ts +64 -0
- package/dist/lib/runtime/native/taskMode.js +126 -0
- package/dist/lib/runtime/native/telemetry.d.ts +75 -0
- package/dist/lib/runtime/native/telemetry.js +299 -0
- package/dist/lib/runtime/native/tempState.d.ts +31 -0
- package/dist/lib/runtime/native/tempState.js +56 -0
- package/dist/lib/runtime/nativeTurn.d.ts +86 -0
- package/dist/lib/runtime/nativeTurn.js +107 -0
- package/dist/lib/runtime/questions.d.ts +29 -7
- package/dist/lib/runtime/questions.js +47 -32
- package/dist/lib/runtime/runtimeFlag.d.ts +30 -0
- package/dist/lib/runtime/runtimeFlag.js +50 -0
- package/dist/lib/runtime/sessions.d.ts +166 -0
- package/dist/lib/runtime/sessions.js +201 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +41 -5
- package/dist/lib/runtime/syndicateTurn.js +202 -32
- package/dist/lib/session/supabaseSessionService.d.ts +64 -17
- package/dist/lib/session/supabaseSessionService.js +148 -105
- package/dist/lib/session/transcript.d.ts +68 -22
- package/dist/lib/session/transcript.js +102 -28
- package/dist/lib/skills.js +33 -7
- package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
- package/dist/lib/storage/postgres/credentialStore.js +82 -0
- package/dist/lib/storage/postgres/index.d.ts +12 -0
- package/dist/lib/storage/postgres/index.js +6 -1
- package/dist/lib/storage/postgres/sessionService.d.ts +43 -15
- package/dist/lib/storage/postgres/sessionService.js +128 -66
- package/dist/lib/syndicateSchema.d.ts +47 -0
- package/dist/lib/syndicateSchema.js +72 -3
- package/dist/lib/toolRegistry.d.ts +15 -2
- package/dist/lib/toolRegistry.js +33 -17
- package/dist/lib/tools/adkTool.d.ts +81 -0
- package/dist/lib/tools/adkTool.js +268 -0
- package/dist/lib/tools/auth.d.ts +118 -0
- package/dist/lib/tools/auth.js +89 -0
- package/dist/lib/tools/collectionsSearchTool.d.ts +7 -6
- package/dist/lib/tools/collectionsSearchTool.js +11 -26
- package/dist/lib/tools/credentialCipher.d.ts +58 -0
- package/dist/lib/tools/credentialCipher.js +116 -0
- package/dist/lib/tools/credentialStore.d.ts +77 -0
- package/dist/lib/tools/credentialStore.js +246 -0
- package/dist/lib/tools/examples.d.ts +43 -0
- package/dist/lib/tools/examples.js +62 -0
- package/dist/lib/tools/generateImageTool.d.ts +15 -6
- package/dist/lib/tools/generateImageTool.js +19 -29
- package/dist/lib/tools/inspectImageTool.d.ts +9 -6
- package/dist/lib/tools/inspectImageTool.js +13 -17
- package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
- package/dist/lib/tools/mcpToolFactory.js +46 -47
- package/dist/lib/tools/memoryTools.d.ts +61 -0
- package/dist/lib/tools/memoryTools.js +119 -0
- package/dist/lib/tools/nativeTools.d.ts +38 -0
- package/dist/lib/tools/nativeTools.js +43 -0
- package/dist/lib/tools/oauthConsent.d.ts +181 -0
- package/dist/lib/tools/oauthConsent.js +330 -0
- package/dist/lib/tools/openapi/call.d.ts +108 -0
- package/dist/lib/tools/openapi/call.js +295 -0
- package/dist/lib/tools/openapi/parse.d.ts +147 -0
- package/dist/lib/tools/openapi/parse.js +570 -0
- package/dist/lib/tools/openapiTools.d.ts +25 -17
- package/dist/lib/tools/openapiTools.js +124 -121
- package/dist/lib/tools/scienceTools.d.ts +14 -14
- package/dist/lib/tools/skillToolset.d.ts +58 -87
- package/dist/lib/tools/skillToolset.js +61 -142
- package/dist/lib/tools/skills/env.d.ts +58 -0
- package/dist/lib/tools/skills/env.js +165 -0
- package/dist/lib/tools/skills/executor.d.ts +114 -0
- package/dist/lib/tools/skills/executor.js +331 -0
- package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
- package/dist/lib/tools/skills/frontmatter.js +215 -0
- package/dist/lib/tools/skills/loader.d.ts +91 -0
- package/dist/lib/tools/skills/loader.js +257 -0
- package/dist/lib/tools/skills/tools.d.ts +187 -0
- package/dist/lib/tools/skills/tools.js +386 -0
- package/dist/lib/tools/taskTools.d.ts +15 -15
- package/dist/lib/tools/tool.d.ts +354 -0
- package/dist/lib/tools/tool.js +304 -0
- package/dist/lib/tools/toolContract.d.ts +89 -33
- package/dist/lib/tools/toolContract.js +104 -82
- package/dist/lib/tools/urlContextTool.d.ts +3 -0
- package/dist/lib/tools/urlContextTool.js +5 -1
- package/dist/lib/tools/webExtractTool.d.ts +2 -2
- package/dist/lib/tools/webExtractTool.js +2 -1
- package/dist/lib/tools/webSearchTool.d.ts +6 -9
- package/dist/lib/tools/webSearchTool.js +14 -59
- package/dist/lib/tools/wikiTools.d.ts +19 -19
- package/dist/lib/tools/wikiTools.js +13 -9
- package/dist/lib/tools/xApiSearchTool.d.ts +2 -2
- package/dist/lib/tools/xApiSearchTool.js +2 -1
- package/dist/lib/tools/xSearchTool.d.ts +7 -9
- package/dist/lib/tools/xSearchTool.js +11 -62
- package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
- package/dist/lib/tools/xaiSearchParams.js +126 -0
- package/dist/lib/wiki/agentRun.d.ts +12 -0
- package/dist/lib/wiki/agentRun.js +72 -34
- package/dist/lib/wiki/entities.d.ts +12 -0
- package/dist/lib/wiki/entities.js +45 -3
- package/dist/lib/workflow/agentNode.d.ts +136 -0
- package/dist/lib/workflow/agentNode.js +274 -0
- package/dist/lib/workflow/graph.d.ts +177 -0
- package/dist/lib/workflow/graph.js +458 -0
- package/dist/lib/workflow/nodeEvents.d.ts +50 -0
- package/dist/lib/workflow/nodeEvents.js +101 -0
- package/dist/lib/workflow/pause.d.ts +122 -0
- package/dist/lib/workflow/pause.js +180 -0
- package/dist/lib/workflow/resume.d.ts +165 -0
- package/dist/lib/workflow/resume.js +440 -0
- package/dist/lib/workflow/route.d.ts +58 -0
- package/dist/lib/workflow/route.js +69 -0
- package/dist/lib/workflow/scheduler.d.ts +415 -0
- package/dist/lib/workflow/scheduler.js +743 -0
- package/dist/lib/workflow/toolNode.d.ts +127 -0
- package/dist/lib/workflow/toolNode.js +236 -0
- package/dist/lib/workflow/turn.d.ts +118 -0
- package/dist/lib/workflow/turn.js +216 -0
- package/dist/lib/workflowConfig.d.ts +3 -2
- package/dist/lib/workflowConfig.js +3 -12
- package/dist/scripts/a2a_server.js +4 -3
- package/dist/scripts/assistant_worker.d.ts +2 -1
- package/dist/scripts/assistant_worker.js +9 -4
- package/dist/scripts/syndicate_chat.js +19 -10
- package/package.json +4 -1
- package/skills/briefs/melchizedek-author.md +3 -2
- package/skills/briefs/melchizedek-models.md +13 -9
- package/skills/briefs/melchizedek.md +3 -3
- package/skills/melchizedek/SKILL.md +2 -1
- package/skills/melchizedek-author/SKILL.md +2 -1
- package/skills/melchizedek-author/assets/minimal.yaml +2 -0
- package/skills/melchizedek-models/SKILL.md +23 -6
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* private one, so any public slice of this graph is closed by
|
|
32
32
|
* construction.
|
|
33
33
|
*/
|
|
34
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
34
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
35
35
|
import { dirname, join } from 'node:path';
|
|
36
36
|
import { z } from 'zod';
|
|
37
37
|
import { isPrivatePath } from './format.js';
|
|
@@ -604,11 +604,53 @@ export function relationEdges(records) {
|
|
|
604
604
|
...(r.at ? { at: r.at } : {}),
|
|
605
605
|
}));
|
|
606
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* Replace the whole store with `records`, in the order given. The store's
|
|
609
|
+
* order is the order of assertion; nothing sorts it.
|
|
610
|
+
*/
|
|
607
611
|
export function saveRelations(wikiRoot, records) {
|
|
608
612
|
const file = relationsPath(wikiRoot);
|
|
609
613
|
mkdirSync(dirname(file), { recursive: true });
|
|
610
|
-
|
|
611
|
-
|
|
614
|
+
writeFileSync(file, `${JSON.stringify({ version: 1, relations: records }, null, 2)}\n`, 'utf-8');
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Add one assertion at the end of the store. Every record already there
|
|
618
|
+
* keeps its place and its bytes, so two branches that each assert something
|
|
619
|
+
* merge by keeping both sides. Returns false, writing nothing, when the edge
|
|
620
|
+
* is already asserted. Throws when the store does not parse rather than
|
|
621
|
+
* write over it.
|
|
622
|
+
*/
|
|
623
|
+
export function appendRelation(wikiRoot, record) {
|
|
624
|
+
const next = relationRecordSchema.parse(record);
|
|
625
|
+
const file = relationsPath(wikiRoot);
|
|
626
|
+
let store = { version: 1, relations: [] };
|
|
627
|
+
// Read once, with no existence check first: a check and then a read can
|
|
628
|
+
// see two different files (CodeQL js/file-system-race).
|
|
629
|
+
let text;
|
|
630
|
+
try {
|
|
631
|
+
text = readFileSync(file, 'utf-8');
|
|
632
|
+
}
|
|
633
|
+
catch (err) {
|
|
634
|
+
if (err.code !== 'ENOENT')
|
|
635
|
+
throw err;
|
|
636
|
+
}
|
|
637
|
+
if (text !== undefined) {
|
|
638
|
+
const raw = JSON.parse(text);
|
|
639
|
+
const parsed = relationStoreSchema.safeParse(raw);
|
|
640
|
+
if (!parsed.success)
|
|
641
|
+
throw new Error(`relations.json is not a relation store: ${parsed.error.issues[0]?.message ?? 'invalid'}`);
|
|
642
|
+
store = raw;
|
|
643
|
+
store.relations ??= [];
|
|
644
|
+
if (parsed.data.relations.some((r) => edgeKey(r) === edgeKey(next)))
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
store.relations.push(next);
|
|
648
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
649
|
+
// Replace the store in one step, so a reader never sees half a file.
|
|
650
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
651
|
+
writeFileSync(tmp, `${JSON.stringify(store, null, 2)}\n`, 'utf-8');
|
|
652
|
+
renameSync(tmp, file);
|
|
653
|
+
return true;
|
|
612
654
|
}
|
|
613
655
|
export function writeSnapshot(wikiRoot, graph, generated) {
|
|
614
656
|
const file = snapshotPath(wikiRoot);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/workflow/agentNode.ts — an agent as a workflow node on the native
|
|
3
|
+
* runtime, as ADK 2.2 runs an LlmAgent node (ADR 0030, ADR 0090).
|
|
4
|
+
*
|
|
5
|
+
* ADK runs an agent node through `runLlmAgentAsNode`
|
|
6
|
+
* (workflow/run_llm_agent_as_node.js) inside its node runner
|
|
7
|
+
* (workflow/node_runner.js). This module is the same run on the native
|
|
8
|
+
* agent loop (lib/runtime/native/agentLoop.ts), rule for rule:
|
|
9
|
+
*
|
|
10
|
+
* 1. THE INPUT IS THE USER TURN. The previous node's output is stored as a
|
|
11
|
+
* user event on the node's branch before the agent runs: a Content as
|
|
12
|
+
* it is (role `user`), a string as one text part, anything else as its
|
|
13
|
+
* JSON. An undefined or null input stores nothing. The event carries no
|
|
14
|
+
* node path, as ADK appends it straight to the session.
|
|
15
|
+
* 2. THE includeContents RULE. An agent whose YAML does not set
|
|
16
|
+
* `includeContents` runs with `none`: it sees the current turn, which
|
|
17
|
+
* is its input, and nothing else of the session. One that sets it keeps
|
|
18
|
+
* what it set. ADK sets this on the agent itself; here the node runs a
|
|
19
|
+
* copy, so the compiled agent is unchanged.
|
|
20
|
+
* 3. TASK MODE. A `mode: task` agent gets no user turn and keeps its
|
|
21
|
+
* includeContents; the loop runs it as a task node (`taskNode`), which
|
|
22
|
+
* ends on finish_task's successful answer and writes the output on that
|
|
23
|
+
* event (lib/runtime/native/taskMode.ts, ADR 0081).
|
|
24
|
+
* 4. THE OUTPUT. Outside task mode, each stored model event with content
|
|
25
|
+
* and no function call carries the node's output: its text (thought
|
|
26
|
+
* parts left out), or, with an output schema, the text parsed as JSON
|
|
27
|
+
* when it parses; and `nodeInfo.messageAsOutput` (ADK's maybeSetOutput).
|
|
28
|
+
* The node's output is the last output an event carried.
|
|
29
|
+
* 5. THE STAMP. Every event the loop stores gets `nodeInfo.path` (the
|
|
30
|
+
* node's path, `<workflow>.<node>`, or `<workflow>.<map>.<agent>@<i>`
|
|
31
|
+
* for a map item), `nodeInfo.outputFor` on an event with an output,
|
|
32
|
+
* and the node's branch when it has none (ADK's enrichEvent, the one
|
|
33
|
+
* port in lib/workflow/toolNode.ts, enrichNodeEvent). The loop
|
|
34
|
+
* calls the stamp before it stores each event (`nodeStamp`), after the
|
|
35
|
+
* outputKey and task hooks, the order ADK applies them in.
|
|
36
|
+
* 6. THE INSTRUCTION SCOPE. The run carries ADK's workflow instruction
|
|
37
|
+
* scope (`workflowScope`): the node's input, and the output every event
|
|
38
|
+
* of the invocation stored before the node ran, by node name
|
|
39
|
+
* (predecessorOutputs, ADK's collectPredecessorOutputs). The request
|
|
40
|
+
* fills `{x.field}` and `<x.field from Node>` from it
|
|
41
|
+
* (lib/runtime/native/request.ts, injectSessionState).
|
|
42
|
+
* 7. FAILURE. An event carrying an error code is the node's reported
|
|
43
|
+
* error; a run that ends with one and no output returns it as the
|
|
44
|
+
* run's `error`, and the scheduler fails the attempt with
|
|
45
|
+
* NodeReportedError, ADK's message, retried or stopping the walk as
|
|
46
|
+
* ADK's does, and reported once (by the node's own event). A run the turn stopped throws. A run that pauses on a person
|
|
47
|
+
* (an ask_user tool, an approval) is an interrupt, not run here yet.
|
|
48
|
+
*
|
|
49
|
+
* `agentNodeRuntime` puts it together for the scheduler: a `runNode` for
|
|
50
|
+
* agent nodes and map items, and an `onEvent` that stores the event ADK
|
|
51
|
+
* stores for each route step (lib/workflow/route.ts), join and map
|
|
52
|
+
* (lib/workflow/nodeEvents.ts), so a session the native walk writes holds
|
|
53
|
+
* what ADK's holds, in the same order.
|
|
54
|
+
*
|
|
55
|
+
* NOT HERE: interrupts inside a node (an ask_user tool call, an approval),
|
|
56
|
+
* tool nodes (lib/workflow/toolNode.ts) and ask_user nodes
|
|
57
|
+
* (lib/workflow/pause.ts). It imports nothing from ADK.
|
|
58
|
+
*/
|
|
59
|
+
import type { TurnContent, TurnEvent } from '../runtime/events.ts';
|
|
60
|
+
import type { AgentLoopContext } from '../runtime/native/agentLoop.ts';
|
|
61
|
+
import type { NativeAgent } from '../runtime/native/request.ts';
|
|
62
|
+
import type { Session, SessionService } from '../runtime/sessions.ts';
|
|
63
|
+
import type { NodeResult, NodeRun, NodeRunner, SchedulerEvent } from './scheduler.ts';
|
|
64
|
+
/** A node whose run ended on an error event and produced no output: the scheduler's, which fails the attempt (rule 7). */
|
|
65
|
+
export { NodeReportedError } from './scheduler.ts';
|
|
66
|
+
/** A node run the turn stopped (cancel, deadline, max_steps): the stop's code and message. */
|
|
67
|
+
export declare class NodeStoppedError extends Error {
|
|
68
|
+
readonly code: string;
|
|
69
|
+
readonly nodeName: string;
|
|
70
|
+
constructor(nodeName: string, stop: {
|
|
71
|
+
code: string;
|
|
72
|
+
message: string;
|
|
73
|
+
} | undefined);
|
|
74
|
+
}
|
|
75
|
+
/** The user turn a node input becomes (ADK's toUserContent). */
|
|
76
|
+
export declare function nodeInputContent(input: unknown): TurnContent;
|
|
77
|
+
/** The agent a node runs: outside task mode, `includeContents: none` unless the agent set it. A copy; the compiled agent is unchanged. */
|
|
78
|
+
export declare function asNodeAgent(agent: NativeAgent): NativeAgent;
|
|
79
|
+
/** ADK's maybeSetOutput: the output a stored model event carries outside task mode, or undefined when it carries none. */
|
|
80
|
+
export declare function eventOutput(agent: Pick<NativeAgent, 'outputSchema'>, event: TurnEvent): unknown;
|
|
81
|
+
/** The loop options a node run takes from its caller; the node sets the rest. */
|
|
82
|
+
export type AgentNodeLoopOptions = Omit<AgentLoopContext, 'session' | 'sessions' | 'invocationId' | 'userContent' | 'branch' | 'signal' | 'taskNode' | 'nodeStamp' | 'workflowScope'>;
|
|
83
|
+
export interface AgentNodeContext {
|
|
84
|
+
session: Session;
|
|
85
|
+
sessions: SessionService;
|
|
86
|
+
/** The workflow run's invocation id: every event of every node carries it. */
|
|
87
|
+
invocationId: string;
|
|
88
|
+
/** The message that started the turn (the invocation's user content). */
|
|
89
|
+
userContent?: TurnContent;
|
|
90
|
+
/** Loop options for every node: adapters, self-correction, memory, streaming. */
|
|
91
|
+
loop?: AgentNodeLoopOptions;
|
|
92
|
+
/** Each event as it is stored, in order. */
|
|
93
|
+
onEvent?: (event: TurnEvent) => void;
|
|
94
|
+
/** Each partial (streamed, never stored) event the node's loop yields, as it yields it. */
|
|
95
|
+
onPartial?: (event: TurnEvent) => void;
|
|
96
|
+
/** Stores the node's user turn; default `sessions.append`. agentNodeRuntime passes its queue. Called synchronously, before the run's first await. */
|
|
97
|
+
appendInput?: (event: TurnEvent) => Promise<TurnEvent>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Runs `agent` as the node `run` names, on the native loop, and resolves
|
|
101
|
+
* with the node's output, or with the error it reported when it ended on
|
|
102
|
+
* one with no output. Throws NodeStoppedError when the turn stopped it, and
|
|
103
|
+
* an Error when it paused on a person.
|
|
104
|
+
*/
|
|
105
|
+
export declare function runAgentNode(agent: NativeAgent, run: Pick<NodeRun, 'input' | 'path' | 'branch' | 'signal'>, ctx: AgentNodeContext): Promise<NodeResult>;
|
|
106
|
+
export interface AgentNodeRuntimeOptions extends Omit<AgentNodeContext, 'appendInput'> {
|
|
107
|
+
/** Every agent of the syndicate, compiled for native, by YAML name. */
|
|
108
|
+
agents: ReadonlyMap<string, NativeAgent>;
|
|
109
|
+
/**
|
|
110
|
+
* Runs every run that is not an agent node or a map item (tool, ask_user),
|
|
111
|
+
* as toolNodeRunner(context, next) hands on what it does not run, so the
|
|
112
|
+
* runners chain. Default: refuses them by name.
|
|
113
|
+
*/
|
|
114
|
+
next?: NodeRunner;
|
|
115
|
+
}
|
|
116
|
+
export interface AgentNodeRuntime {
|
|
117
|
+
/** The scheduler's runNode: agent nodes and map items run here; every other run goes to `next`. */
|
|
118
|
+
runNode: NodeRunner;
|
|
119
|
+
/** The scheduler's onEvent: stores the event ADK stores for each route step, join and map. */
|
|
120
|
+
onEvent: (event: SchedulerEvent) => void;
|
|
121
|
+
/**
|
|
122
|
+
* Stores an event another runner of the chain made (a tool node's, through
|
|
123
|
+
* its context's onEvent) on the same queue, in walk order, and hands it to
|
|
124
|
+
* the `onEvent` option once stored.
|
|
125
|
+
*/
|
|
126
|
+
store(event: TurnEvent): Promise<TurnEvent>;
|
|
127
|
+
/** Resolves once every event queued for storage is stored; rejects with the first store error. */
|
|
128
|
+
settled(): Promise<void>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The scheduler's node runner for a syndicate's agents, and the store for
|
|
132
|
+
* its route steps' events. Node user turns and route events are stored in
|
|
133
|
+
* the order the walk reaches them, through one queue, so a route step's
|
|
134
|
+
* event lands before its successor's input as on ADK.
|
|
135
|
+
*/
|
|
136
|
+
export declare function agentNodeRuntime(options: AgentNodeRuntimeOptions): AgentNodeRuntime;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/workflow/agentNode.ts — an agent as a workflow node on the native
|
|
3
|
+
* runtime, as ADK 2.2 runs an LlmAgent node (ADR 0030, ADR 0090).
|
|
4
|
+
*
|
|
5
|
+
* ADK runs an agent node through `runLlmAgentAsNode`
|
|
6
|
+
* (workflow/run_llm_agent_as_node.js) inside its node runner
|
|
7
|
+
* (workflow/node_runner.js). This module is the same run on the native
|
|
8
|
+
* agent loop (lib/runtime/native/agentLoop.ts), rule for rule:
|
|
9
|
+
*
|
|
10
|
+
* 1. THE INPUT IS THE USER TURN. The previous node's output is stored as a
|
|
11
|
+
* user event on the node's branch before the agent runs: a Content as
|
|
12
|
+
* it is (role `user`), a string as one text part, anything else as its
|
|
13
|
+
* JSON. An undefined or null input stores nothing. The event carries no
|
|
14
|
+
* node path, as ADK appends it straight to the session.
|
|
15
|
+
* 2. THE includeContents RULE. An agent whose YAML does not set
|
|
16
|
+
* `includeContents` runs with `none`: it sees the current turn, which
|
|
17
|
+
* is its input, and nothing else of the session. One that sets it keeps
|
|
18
|
+
* what it set. ADK sets this on the agent itself; here the node runs a
|
|
19
|
+
* copy, so the compiled agent is unchanged.
|
|
20
|
+
* 3. TASK MODE. A `mode: task` agent gets no user turn and keeps its
|
|
21
|
+
* includeContents; the loop runs it as a task node (`taskNode`), which
|
|
22
|
+
* ends on finish_task's successful answer and writes the output on that
|
|
23
|
+
* event (lib/runtime/native/taskMode.ts, ADR 0081).
|
|
24
|
+
* 4. THE OUTPUT. Outside task mode, each stored model event with content
|
|
25
|
+
* and no function call carries the node's output: its text (thought
|
|
26
|
+
* parts left out), or, with an output schema, the text parsed as JSON
|
|
27
|
+
* when it parses; and `nodeInfo.messageAsOutput` (ADK's maybeSetOutput).
|
|
28
|
+
* The node's output is the last output an event carried.
|
|
29
|
+
* 5. THE STAMP. Every event the loop stores gets `nodeInfo.path` (the
|
|
30
|
+
* node's path, `<workflow>.<node>`, or `<workflow>.<map>.<agent>@<i>`
|
|
31
|
+
* for a map item), `nodeInfo.outputFor` on an event with an output,
|
|
32
|
+
* and the node's branch when it has none (ADK's enrichEvent, the one
|
|
33
|
+
* port in lib/workflow/toolNode.ts, enrichNodeEvent). The loop
|
|
34
|
+
* calls the stamp before it stores each event (`nodeStamp`), after the
|
|
35
|
+
* outputKey and task hooks, the order ADK applies them in.
|
|
36
|
+
* 6. THE INSTRUCTION SCOPE. The run carries ADK's workflow instruction
|
|
37
|
+
* scope (`workflowScope`): the node's input, and the output every event
|
|
38
|
+
* of the invocation stored before the node ran, by node name
|
|
39
|
+
* (predecessorOutputs, ADK's collectPredecessorOutputs). The request
|
|
40
|
+
* fills `{x.field}` and `<x.field from Node>` from it
|
|
41
|
+
* (lib/runtime/native/request.ts, injectSessionState).
|
|
42
|
+
* 7. FAILURE. An event carrying an error code is the node's reported
|
|
43
|
+
* error; a run that ends with one and no output returns it as the
|
|
44
|
+
* run's `error`, and the scheduler fails the attempt with
|
|
45
|
+
* NodeReportedError, ADK's message, retried or stopping the walk as
|
|
46
|
+
* ADK's does, and reported once (by the node's own event). A run the turn stopped throws. A run that pauses on a person
|
|
47
|
+
* (an ask_user tool, an approval) is an interrupt, not run here yet.
|
|
48
|
+
*
|
|
49
|
+
* `agentNodeRuntime` puts it together for the scheduler: a `runNode` for
|
|
50
|
+
* agent nodes and map items, and an `onEvent` that stores the event ADK
|
|
51
|
+
* stores for each route step (lib/workflow/route.ts), join and map
|
|
52
|
+
* (lib/workflow/nodeEvents.ts), so a session the native walk writes holds
|
|
53
|
+
* what ADK's holds, in the same order.
|
|
54
|
+
*
|
|
55
|
+
* NOT HERE: interrupts inside a node (an ask_user tool call, an approval),
|
|
56
|
+
* tool nodes (lib/workflow/toolNode.ts) and ask_user nodes
|
|
57
|
+
* (lib/workflow/pause.ts). It imports nothing from ADK.
|
|
58
|
+
*/
|
|
59
|
+
import { createTurnEvent, getFunctionCalls } from '../runtime/events.js';
|
|
60
|
+
import { runAgentLoop } from '../runtime/native/agentLoop.js';
|
|
61
|
+
import { predecessorOutputs } from '../runtime/native/request.js';
|
|
62
|
+
import { joinNodeEvent, mapNodeEvent } from './nodeEvents.js';
|
|
63
|
+
import { routeStepEvent } from './route.js';
|
|
64
|
+
import { enrichNodeEvent } from './toolNode.js';
|
|
65
|
+
// ── Errors, as ADK names them ────────────────────────────────────────────────
|
|
66
|
+
/** A node whose run ended on an error event and produced no output: the scheduler's, which fails the attempt (rule 7). */
|
|
67
|
+
export { NodeReportedError } from './scheduler.js';
|
|
68
|
+
/** A node run the turn stopped (cancel, deadline, max_steps): the stop's code and message. */
|
|
69
|
+
export class NodeStoppedError extends Error {
|
|
70
|
+
code;
|
|
71
|
+
nodeName;
|
|
72
|
+
constructor(nodeName, stop) {
|
|
73
|
+
super(`Node '${nodeName}' stopped: ${stop ? `${stop.code}: ${stop.message}` : 'the turn stopped it'}`);
|
|
74
|
+
this.name = 'NodeStoppedError';
|
|
75
|
+
this.code = stop?.code ?? 'STOPPED';
|
|
76
|
+
this.nodeName = nodeName;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ── The rules, one function each ─────────────────────────────────────────────
|
|
80
|
+
/** ADK's isContent: an object with a `parts` array. */
|
|
81
|
+
function isContent(value) {
|
|
82
|
+
return typeof value === 'object' && value !== null && Array.isArray(value.parts);
|
|
83
|
+
}
|
|
84
|
+
/** The user turn a node input becomes (ADK's toUserContent). */
|
|
85
|
+
export function nodeInputContent(input) {
|
|
86
|
+
if (isContent(input))
|
|
87
|
+
return { ...input, role: 'user' };
|
|
88
|
+
if (typeof input === 'string')
|
|
89
|
+
return { role: 'user', parts: [{ text: input }] };
|
|
90
|
+
return { role: 'user', parts: [{ text: JSON.stringify(input) }] };
|
|
91
|
+
}
|
|
92
|
+
/** The agent a node runs: outside task mode, `includeContents: none` unless the agent set it. A copy; the compiled agent is unchanged. */
|
|
93
|
+
export function asNodeAgent(agent) {
|
|
94
|
+
if (agent.mode === 'task' || agent.includeContents !== undefined)
|
|
95
|
+
return agent;
|
|
96
|
+
return { ...agent, includeContents: 'none' };
|
|
97
|
+
}
|
|
98
|
+
/** ADK's maybeSetOutput: the output a stored model event carries outside task mode, or undefined when it carries none. */
|
|
99
|
+
export function eventOutput(agent, event) {
|
|
100
|
+
if (event.partial)
|
|
101
|
+
return undefined;
|
|
102
|
+
if (getFunctionCalls(event).length > 0)
|
|
103
|
+
return undefined;
|
|
104
|
+
const content = event.content;
|
|
105
|
+
if (!content || content.role !== 'model' || !content.parts)
|
|
106
|
+
return undefined;
|
|
107
|
+
const text = content.parts
|
|
108
|
+
.filter((p) => p.text && !p.thought)
|
|
109
|
+
.map((p) => p.text)
|
|
110
|
+
.join('');
|
|
111
|
+
if (agent.outputSchema && text.trim()) {
|
|
112
|
+
try {
|
|
113
|
+
return JSON.parse(text);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return text;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return text;
|
|
120
|
+
}
|
|
121
|
+
/** The stamp the loop applies to each event before it stores it (rules 4 and 5). */
|
|
122
|
+
function nodeStamp(agent, run, invocationId, state) {
|
|
123
|
+
const taskMode = agent.mode === 'task';
|
|
124
|
+
return (event) => {
|
|
125
|
+
if (!taskMode) {
|
|
126
|
+
const output = eventOutput(agent, event);
|
|
127
|
+
if (output !== undefined) {
|
|
128
|
+
event.output = output;
|
|
129
|
+
event.nodeInfo = { ...(event.nodeInfo ?? {}), messageAsOutput: true };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
enrichNodeEvent(event, run, { invocationId });
|
|
133
|
+
if (event.output !== undefined)
|
|
134
|
+
state.output = event.output;
|
|
135
|
+
if (event.errorCode !== undefined)
|
|
136
|
+
state.reported = { errorCode: event.errorCode, ...(event.errorMessage !== undefined ? { errorMessage: event.errorMessage } : {}) };
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Runs `agent` as the node `run` names, on the native loop, and resolves
|
|
141
|
+
* with the node's output, or with the error it reported when it ended on
|
|
142
|
+
* one with no output. Throws NodeStoppedError when the turn stopped it, and
|
|
143
|
+
* an Error when it paused on a person.
|
|
144
|
+
*/
|
|
145
|
+
export async function runAgentNode(agent, run, ctx) {
|
|
146
|
+
const taskMode = agent.mode === 'task';
|
|
147
|
+
const nodeAgent = asNodeAgent(agent);
|
|
148
|
+
const { session, sessions, invocationId } = ctx;
|
|
149
|
+
if (!taskMode && run.input !== undefined && run.input !== null) {
|
|
150
|
+
const userEvent = createTurnEvent({ author: 'user', invocationId, branch: run.branch, content: nodeInputContent(run.input) });
|
|
151
|
+
const stored = await (ctx.appendInput ?? ((e) => sessions.append(session, e)))(userEvent);
|
|
152
|
+
ctx.onEvent?.(stored);
|
|
153
|
+
}
|
|
154
|
+
// ADK's withWorkflowInstructionScope: the input, and the outputs stored before the node runs.
|
|
155
|
+
const workflowScope = { input: run.input, outputsByNode: predecessorOutputs(session.events, invocationId) };
|
|
156
|
+
const state = { output: undefined };
|
|
157
|
+
const loop = runAgentLoop(nodeAgent, {
|
|
158
|
+
...(ctx.loop ?? {}),
|
|
159
|
+
session,
|
|
160
|
+
sessions,
|
|
161
|
+
invocationId,
|
|
162
|
+
...(ctx.userContent ? { userContent: ctx.userContent } : {}),
|
|
163
|
+
...(run.branch !== undefined ? { branch: run.branch } : {}),
|
|
164
|
+
signal: run.signal,
|
|
165
|
+
taskNode: taskMode,
|
|
166
|
+
nodeStamp: nodeStamp(agent, run, invocationId, state),
|
|
167
|
+
workflowScope,
|
|
168
|
+
});
|
|
169
|
+
let end;
|
|
170
|
+
for (;;) {
|
|
171
|
+
const next = await loop.next();
|
|
172
|
+
if (next.done) {
|
|
173
|
+
end = next.value;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
if (next.value.partial)
|
|
177
|
+
ctx.onPartial?.(next.value);
|
|
178
|
+
else
|
|
179
|
+
ctx.onEvent?.(next.value);
|
|
180
|
+
}
|
|
181
|
+
if (end.reason === 'paused') {
|
|
182
|
+
throw new Error(`Node '${agent.name}' paused on ${(end.pending ?? []).join(', ')}: a pause inside an agent node does not run on the native runtime yet.`);
|
|
183
|
+
}
|
|
184
|
+
if (end.reason === 'stopped')
|
|
185
|
+
throw new NodeStoppedError(agent.name, end.stop);
|
|
186
|
+
// ADK's failIfNodeReportedError: an error with an output is not the node's failure. Without one the error is
|
|
187
|
+
// returned, not thrown: the scheduler fails the attempt with its own NodeReportedError (this class's message), which
|
|
188
|
+
// it knows the node reported, so it writes no node-error event of its own, as ADK writes none (ADR 0095).
|
|
189
|
+
if (state.reported && state.output === undefined) {
|
|
190
|
+
const code = state.reported.errorCode ?? 'UNKNOWN_ERROR';
|
|
191
|
+
return { error: { code, message: state.reported.errorMessage ?? code } };
|
|
192
|
+
}
|
|
193
|
+
return state.output === undefined ? {} : { output: state.output };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The scheduler's node runner for a syndicate's agents, and the store for
|
|
197
|
+
* its route steps' events. Node user turns and route events are stored in
|
|
198
|
+
* the order the walk reaches them, through one queue, so a route step's
|
|
199
|
+
* event lands before its successor's input as on ADK.
|
|
200
|
+
*/
|
|
201
|
+
export function agentNodeRuntime(options) {
|
|
202
|
+
const { agents, session, sessions, invocationId } = options;
|
|
203
|
+
let tail = Promise.resolve();
|
|
204
|
+
let failure;
|
|
205
|
+
const enqueue = (event, onStored) => {
|
|
206
|
+
const stored = tail.then(async () => {
|
|
207
|
+
const appended = await sessions.append(session, event);
|
|
208
|
+
onStored?.(appended);
|
|
209
|
+
return appended;
|
|
210
|
+
});
|
|
211
|
+
tail = stored.then(() => undefined, (error) => {
|
|
212
|
+
failure ??= { error };
|
|
213
|
+
});
|
|
214
|
+
return stored;
|
|
215
|
+
};
|
|
216
|
+
const ctx = {
|
|
217
|
+
session,
|
|
218
|
+
sessions,
|
|
219
|
+
invocationId,
|
|
220
|
+
...(options.userContent ? { userContent: options.userContent } : {}),
|
|
221
|
+
...(options.loop ? { loop: options.loop } : {}),
|
|
222
|
+
...(options.onEvent ? { onEvent: options.onEvent } : {}),
|
|
223
|
+
...(options.onPartial ? { onPartial: options.onPartial } : {}),
|
|
224
|
+
// An event queued before the input that failed to store stops the node before its agent runs.
|
|
225
|
+
appendInput: async (event) => {
|
|
226
|
+
const stored = await enqueue(event);
|
|
227
|
+
if (failure)
|
|
228
|
+
throw failure.error;
|
|
229
|
+
return stored;
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
const agentNamed = (name) => {
|
|
233
|
+
const agent = agents.get(name);
|
|
234
|
+
if (!agent)
|
|
235
|
+
throw new Error(`workflow: '${name}' is not a compiled agent of this syndicate`);
|
|
236
|
+
return agent;
|
|
237
|
+
};
|
|
238
|
+
// A node's user turn is queued as the node starts, in the same synchronous pass, as ADK's runLlmAgentAsNode appends it
|
|
239
|
+
// straight to the session; so it lands after every event queued before it (a route step's, a predecessor's) and
|
|
240
|
+
// before the events other runners hand over in that pass (`store`, below).
|
|
241
|
+
const runNode = async (run) => {
|
|
242
|
+
if (failure)
|
|
243
|
+
throw failure.error;
|
|
244
|
+
const target = run.target;
|
|
245
|
+
if (target.kind === 'agent')
|
|
246
|
+
return runAgentNode(agentNamed(target.name), run, ctx);
|
|
247
|
+
if (target.kind === 'map_item')
|
|
248
|
+
return runAgentNode(agentNamed(target.agent), run, ctx);
|
|
249
|
+
if (options.next)
|
|
250
|
+
return options.next(run);
|
|
251
|
+
throw new Error(`agentNodeRunner runs agent nodes and map items only; ${target.name} is a ${target.kind} run`);
|
|
252
|
+
};
|
|
253
|
+
const onEvent = (event) => {
|
|
254
|
+
if (event.type !== 'node_end')
|
|
255
|
+
return;
|
|
256
|
+
const run = { name: event.node, path: event.path, branch: event.branch, invocationId, output: event.output };
|
|
257
|
+
// The events ADK stores for the nodes the scheduler runs itself: a route step's, a join's, a map's.
|
|
258
|
+
const stored = event.kind === 'route' ? routeStepEvent({ ...run, route: event.route }) : event.kind === 'join' ? joinNodeEvent(run) : event.kind === 'map' ? mapNodeEvent(run) : undefined;
|
|
259
|
+
if (stored)
|
|
260
|
+
enqueue(stored, options.onEvent).catch(() => { }); // surfaced by runNode and settled()
|
|
261
|
+
};
|
|
262
|
+
return {
|
|
263
|
+
runNode,
|
|
264
|
+
onEvent,
|
|
265
|
+
// ADK's Runner stores what a node yields a few ticks after it is yielded, behind the user turns of nodes started in
|
|
266
|
+
// the same pass: queued one microtask later, which is still before the walk starts the node's successors.
|
|
267
|
+
store: (event) => Promise.resolve().then(() => enqueue(event, options.onEvent)),
|
|
268
|
+
async settled() {
|
|
269
|
+
await tail;
|
|
270
|
+
if (failure)
|
|
271
|
+
throw failure.error;
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/workflow/graph.ts — the `workflow:` block as an engine-owned graph.
|
|
3
|
+
*
|
|
4
|
+
* The YAML (lib/workflowConfig.ts) says what runs after what in chains of
|
|
5
|
+
* names; this module turns those chains into the graph a scheduler runs:
|
|
6
|
+
* typed nodes (`start`, `agent`, `join`, `map`, `tool`, `ask_user`, and the
|
|
7
|
+
* hidden `route` step after a node a routing map follows), directed edges
|
|
8
|
+
* that are unconditional, keyed to a route, or the `default` route, and the
|
|
9
|
+
* workflow's `max_concurrency`. It imports nothing from ADK.
|
|
10
|
+
*
|
|
11
|
+
* It is the same graph `compileWorkflow` (lib/workflow.ts) hands ADK's
|
|
12
|
+
* `Workflow`: the same node names in the same order, the same edges in the
|
|
13
|
+
* same order with the same routes (tests/workflowGraph.test.ts holds the
|
|
14
|
+
* two together on every workflow fixture). The engine's own scheduler,
|
|
15
|
+
* lib/workflow/scheduler.ts, runs it (ADR 0087).
|
|
16
|
+
*
|
|
17
|
+
* ── Validation ────────────────────────────────────────────────────────────
|
|
18
|
+
* Two passes, each raising the message today's path raises:
|
|
19
|
+
* 1. The block's cross-field rules — every name an agent or a declared
|
|
20
|
+
* node, `START` opening a chain, a routing map after the node it
|
|
21
|
+
* routes, a declared node exactly one kind, the reserved `__route`
|
|
22
|
+
* suffix, `workflow` without `dispatch`, and the pauses and remotes a
|
|
23
|
+
* node cannot carry yet. Same paths and messages as the schema's
|
|
24
|
+
* (lib/syndicateSchema.ts, `workflowProblems`), thrown together as a
|
|
25
|
+
* `WorkflowGraphError`.
|
|
26
|
+
* 2. The graph's own rules, on the built edges — no empty routing map,
|
|
27
|
+
* `START` present, without routes and without incoming edges, every
|
|
28
|
+
* node reachable from `START`, no duplicate edge, one `default` per
|
|
29
|
+
* node, no unconditional cycle. Same messages ADK's graph validation
|
|
30
|
+
* throws when `compileWorkflow` builds the `Workflow`, first problem
|
|
31
|
+
* only, as ADK does.
|
|
32
|
+
* The schema's type checks (an edge chain of at least two elements, a name
|
|
33
|
+
* that is a non-empty string) are not repeated: the input is a syndicate
|
|
34
|
+
* that has passed `validateSyndicateConfig`, or one shaped like it. Whether
|
|
35
|
+
* a `tool` node's tool is registered is a compile-time question (the
|
|
36
|
+
* registry), not a graph one.
|
|
37
|
+
*/
|
|
38
|
+
import type { SyndicateYamlConfig } from '../loadSyndicate.ts';
|
|
39
|
+
import type { RetryYaml } from '../workflowConfig.ts';
|
|
40
|
+
/**
|
|
41
|
+
* The entry node's name in the graph. The YAML writes it `START`; the graph
|
|
42
|
+
* names it as the ADK compile does, so a node path read from an ADK-written
|
|
43
|
+
* workflow event names the same node here.
|
|
44
|
+
*/
|
|
45
|
+
export declare const START_NODE = "__START__";
|
|
46
|
+
/** What a node is. `route` is the hidden step after a node a routing map follows. */
|
|
47
|
+
export type GraphNodeKind = 'start' | 'agent' | 'join' | 'map' | 'tool' | 'ask_user' | 'route';
|
|
48
|
+
/** The modifiers a node entry carries (`retry`, `timeout`), in the YAML spelling. */
|
|
49
|
+
export interface GraphNodeSettings {
|
|
50
|
+
retry?: RetryYaml;
|
|
51
|
+
/** Seconds this node may run before it fails. */
|
|
52
|
+
timeout?: number;
|
|
53
|
+
}
|
|
54
|
+
export interface StartNode {
|
|
55
|
+
kind: 'start';
|
|
56
|
+
name: typeof START_NODE;
|
|
57
|
+
}
|
|
58
|
+
export interface AgentNode {
|
|
59
|
+
kind: 'agent';
|
|
60
|
+
/** The agent's YAML name: the orchestrator or a subagent. */
|
|
61
|
+
name: string;
|
|
62
|
+
settings: GraphNodeSettings;
|
|
63
|
+
}
|
|
64
|
+
export interface JoinNode {
|
|
65
|
+
kind: 'join';
|
|
66
|
+
name: string;
|
|
67
|
+
settings: GraphNodeSettings;
|
|
68
|
+
}
|
|
69
|
+
export interface MapNode {
|
|
70
|
+
kind: 'map';
|
|
71
|
+
name: string;
|
|
72
|
+
/** The agent run once per item of the list input. It is not itself a graph node. */
|
|
73
|
+
agent: string;
|
|
74
|
+
/** Concurrency of the map; absent means the runtime's default (8). */
|
|
75
|
+
maxParallel?: number;
|
|
76
|
+
/**
|
|
77
|
+
* The map entry's own modifiers. ADK's compile does not hand them to its
|
|
78
|
+
* ParallelWorker, so neither runtime applies them; they are kept as written.
|
|
79
|
+
*/
|
|
80
|
+
settings: GraphNodeSettings;
|
|
81
|
+
/** The mapped agent's own node modifiers (`nodes.<agent>`), which ADK applies to each item's run. */
|
|
82
|
+
agentSettings: GraphNodeSettings;
|
|
83
|
+
}
|
|
84
|
+
export interface ToolNode {
|
|
85
|
+
kind: 'tool';
|
|
86
|
+
name: string;
|
|
87
|
+
/** The registry tool run with the node input as its arguments. */
|
|
88
|
+
tool: string;
|
|
89
|
+
settings: GraphNodeSettings;
|
|
90
|
+
}
|
|
91
|
+
export interface AskUserNode {
|
|
92
|
+
kind: 'ask_user';
|
|
93
|
+
name: string;
|
|
94
|
+
/** The question. */
|
|
95
|
+
message: string;
|
|
96
|
+
/** JSON Schema a structured reply must satisfy. */
|
|
97
|
+
schema?: Record<string, unknown>;
|
|
98
|
+
settings: GraphNodeSettings;
|
|
99
|
+
}
|
|
100
|
+
export interface RouteNode {
|
|
101
|
+
kind: 'route';
|
|
102
|
+
/** `<source>__route`. */
|
|
103
|
+
name: string;
|
|
104
|
+
/** The node whose output this step reads the route from. */
|
|
105
|
+
source: string;
|
|
106
|
+
/** Property of a JSON output holding the route (`route_key`, default "route"); else the trimmed text. */
|
|
107
|
+
routeKey: string;
|
|
108
|
+
}
|
|
109
|
+
export type GraphNode = StartNode | AgentNode | JoinNode | MapNode | ToolNode | AskUserNode | RouteNode;
|
|
110
|
+
/**
|
|
111
|
+
* When an edge fires: always (`always`), when the source's route is `key`
|
|
112
|
+
* (`key`, the YAML routing-map key verbatim), or when no keyed edge from the
|
|
113
|
+
* same source matched (`default`).
|
|
114
|
+
*/
|
|
115
|
+
export type EdgeRoute = {
|
|
116
|
+
kind: 'always';
|
|
117
|
+
} | {
|
|
118
|
+
kind: 'key';
|
|
119
|
+
key: string;
|
|
120
|
+
} | {
|
|
121
|
+
kind: 'default';
|
|
122
|
+
};
|
|
123
|
+
export interface GraphEdge {
|
|
124
|
+
from: string;
|
|
125
|
+
to: string;
|
|
126
|
+
route: EdgeRoute;
|
|
127
|
+
}
|
|
128
|
+
export interface WorkflowGraph {
|
|
129
|
+
/** The syndicate's name, which names the workflow. */
|
|
130
|
+
name: string;
|
|
131
|
+
/** Every node, by name, in order of first appearance in `edges` (`START_NODE` first). */
|
|
132
|
+
nodes: Map<string, GraphNode>;
|
|
133
|
+
/** Every edge, chain by chain, element pair by element pair. */
|
|
134
|
+
edges: GraphEdge[];
|
|
135
|
+
/** Nodes that may run at once; absent means unbounded. */
|
|
136
|
+
maxConcurrency?: number;
|
|
137
|
+
/** Nodes with no outgoing edge: the workflow's output is theirs. */
|
|
138
|
+
terminals: string[];
|
|
139
|
+
/** Every agent the syndicate declares (orchestrator first), including one reached only through a map. */
|
|
140
|
+
agents: string[];
|
|
141
|
+
}
|
|
142
|
+
/** One problem the block's rules found, with the key path the schema reports it at. */
|
|
143
|
+
export interface GraphProblem {
|
|
144
|
+
path: readonly PropertyKey[];
|
|
145
|
+
message: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Thrown by `buildWorkflowGraph`. For the block's rules, `problems` holds one
|
|
149
|
+
* `<key.path> — <what>` line per problem and the message reads as
|
|
150
|
+
* `SyndicateValidationError`'s does without a file; for a graph rule, it
|
|
151
|
+
* holds the one message, which is the error's message.
|
|
152
|
+
*/
|
|
153
|
+
export declare class WorkflowGraphError extends Error {
|
|
154
|
+
readonly problems: string[];
|
|
155
|
+
constructor(problems: string[], message?: string);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The graph a workflow syndicate declares. Throws `WorkflowGraphError` on a
|
|
159
|
+
* rule the block breaks, and an `Error` when the syndicate has no
|
|
160
|
+
* `workflow:` block, with `compileWorkflow`'s message.
|
|
161
|
+
*/
|
|
162
|
+
export declare function buildWorkflowGraph(config: SyndicateYamlConfig): WorkflowGraph;
|
|
163
|
+
/**
|
|
164
|
+
* The cross-field rules of a `workflow:` block, with the paths and messages
|
|
165
|
+
* `validateSyndicateConfig` reports for them, in the same order. Empty for a
|
|
166
|
+
* block that keeps them all.
|
|
167
|
+
*/
|
|
168
|
+
export declare function workflowConfigProblems(raw: Record<string, unknown>, subs: unknown[]): GraphProblem[];
|
|
169
|
+
/**
|
|
170
|
+
* A routing-map key as the ADK compile stores it and prints it: an integer
|
|
171
|
+
* spelling becomes a number and `true`/`false` a boolean, compared as
|
|
172
|
+
* strings, so `01` and `1` name the same route there. Used for the
|
|
173
|
+
* duplicate-edge rule and its message, and by the scheduler
|
|
174
|
+
* (lib/workflow/scheduler.ts) to match an emitted route as ADK matches it
|
|
175
|
+
* (ADR 0087); the graph keeps the key verbatim.
|
|
176
|
+
*/
|
|
177
|
+
export declare function adkRouteString(route: EdgeRoute): string | null;
|