melchizedek-agents 0.17.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 +35 -3
- package/AGENT_SETUP.md +2 -1
- package/CHANGELOG.md +929 -0
- package/DOCUMENTATION.md +290 -66
- 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 +63 -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 +10 -3
- package/db/migrations/0012_audit_log.sql +98 -0
- package/db/migrations/0013_tool_credentials.sql +260 -0
- package/dist/lib/a2a/app.d.ts +90 -6
- package/dist/lib/a2a/app.js +201 -6
- package/dist/lib/a2a/executor.d.ts +54 -7
- package/dist/lib/a2a/executor.js +134 -16
- package/dist/lib/a2a/remoteAgent.d.ts +24 -9
- package/dist/lib/a2a/remoteAgent.js +31 -14
- package/dist/lib/compile.d.ts +121 -31
- package/dist/lib/compile.js +153 -113
- 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 +13 -5
- package/dist/lib/config.js +13 -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/loadEnv.d.ts +5 -0
- package/dist/lib/loadEnv.js +7 -0
- package/dist/lib/loadSyndicate.d.ts +22 -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.d.ts +27 -0
- package/dist/lib/memory/providers.js +30 -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 +48 -0
- package/dist/lib/models/fallback.js +116 -0
- 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 +25 -0
- package/dist/lib/observability/audit.js +39 -0
- package/dist/lib/observability/lineage.d.ts +14 -0
- package/dist/lib/observability/lineage.js +33 -0
- package/dist/lib/observability/metrics.d.ts +6 -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 +39 -8
- package/dist/lib/observability/tracer.js +59 -20
- 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 +44 -6
- package/dist/lib/runtime/syndicateTurn.js +205 -33
- 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 +15 -0
- package/dist/lib/storage/postgres/index.js +7 -0
- 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 +55 -0
- package/dist/lib/syndicateSchema.js +91 -7
- 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 +68 -1
- package/dist/lib/tools/mcpToolFactory.js +106 -50
- 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 +31 -17
- package/dist/lib/tools/openapiTools.js +150 -117
- 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 +55 -11
- package/dist/scripts/assistant_worker.d.ts +2 -1
- package/dist/scripts/assistant_worker.js +9 -4
- package/dist/scripts/db.d.ts +9 -0
- package/dist/scripts/db.js +30 -15
- package/dist/scripts/syndicate_chat.js +19 -10
- package/package.json +6 -3
- 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
|
@@ -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
|
|
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
|
|
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. */
|
|
@@ -25,7 +25,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
25
25
|
* with A2A_AUTH=callers it stays valid beside the caller
|
|
26
26
|
* tokens, with its old scoping, until you remove it
|
|
27
27
|
* A2A_AUTH who a caller is (ADR 0025): secret (default) | callers |
|
|
28
|
-
* jwt | header
|
|
28
|
+
* jwt | header (required with PUBLIC_URL, ADR 0039)
|
|
29
29
|
* A2A_CALLERS callers: `name:sha256[:scope]; …` — one token per calling
|
|
30
30
|
* backend. Mint: melchizedek-serve --new-caller <name>
|
|
31
31
|
* A2A_JWT_JWKS_URL jwt: your identity provider's JWKS (or A2A_JWT_SECRET, HS256)
|
|
@@ -39,20 +39,25 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
39
39
|
* ALLOW_UNHARDENED_DB "true" to accept an unhardened Supabase schema
|
|
40
40
|
* A2A_TASK_TIMEOUT_MS per-task wall-clock budget (900000; 0 = none)
|
|
41
41
|
* A2A_MAX_CONCURRENT_TASKS concurrent tasks across all agents (0 = unlimited)
|
|
42
|
+
* A2A_MAX_CONCURRENT_PER_SCOPE concurrent tasks for one end user (4; 0 = unlimited)
|
|
43
|
+
* A2A_MAX_CONCURRENT_PER_CALLER concurrent tasks for one caller (0 = unlimited)
|
|
42
44
|
* A2A_RATE_LIMIT_MAX task submissions per window per IP (60)
|
|
43
45
|
* A2A_RATE_LIMIT_WINDOW_MS rate-limit window (900000)
|
|
44
46
|
* A2A_AUTH_FAILURE_MAX failed authentications per window per IP (30)
|
|
45
|
-
* A2A_TRUST_PROXY Express trust-proxy: hop count, true/false, or subnets
|
|
47
|
+
* A2A_TRUST_PROXY Express trust-proxy: hop count, true/false, or subnets
|
|
48
|
+
* (false; required with PUBLIC_URL)
|
|
46
49
|
* A2A_BODY_LIMIT JSON body limit ("1mb")
|
|
47
50
|
* A2A_KEY_MODE server (default): models run on the server's keys;
|
|
48
51
|
* byok: the caller's X-API-Key pays. Under A2A_AUTH=secret
|
|
49
52
|
* the mode also scopes data: X-User-Id in server mode,
|
|
50
53
|
* the key's hash in byok (the pre-0.16 behaviour)
|
|
51
54
|
* A2A_SERVED_AGENTS comma list: the only agent ids /:agentId/ serves;
|
|
52
|
-
* only these may fall back to examples/ and templates
|
|
55
|
+
* only these may fall back to examples/ and templates/;
|
|
56
|
+
* * serves every one (required with PUBLIC_URL)
|
|
53
57
|
* A2A_REGISTRY_AGENTS comma list: bare ids that load from adk_agent_registry
|
|
54
58
|
* (others are files; registry:<id> always is the registry)
|
|
55
59
|
* A2A_SHUTDOWN_GRACE_MS how long SIGTERM waits for running tasks (25000)
|
|
60
|
+
* A2A_SHUTDOWN_DELAY_MS of that, how long /readyz fails while requests are still served (5000)
|
|
56
61
|
* A2A_BUDGETS daily budgets per UTC day (ADR 0026), JSON:
|
|
57
62
|
* {"perCaller":{"tasks":500,"llmCalls":5000,"tokens":5000000},
|
|
58
63
|
* "callers":{"reports":{"tasks":100}},"perScope":{"tasks":50}}.
|
|
@@ -74,7 +79,6 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
74
79
|
import { randomBytes } from 'node:crypto';
|
|
75
80
|
import { realpathSync, writeFileSync } from 'node:fs';
|
|
76
81
|
import { pathToFileURL } from 'node:url';
|
|
77
|
-
import { setLogLevel, LogLevel } from '@google/adk';
|
|
78
82
|
import { createA2AApp } from '../lib/a2a/app.js';
|
|
79
83
|
import { callerTokens, firstOf, hashCallerToken, jwtIdentity, parseCallers, sharedSecret, trustedHeader, } from '../lib/a2a/identity.js';
|
|
80
84
|
import { budgets, memoryUsageStore, parseBudgets, postgresUsageStore, supabaseUsageStore } from '../lib/a2a/policy.js';
|
|
@@ -85,6 +89,7 @@ import { dbSchema } from '../lib/storage/schema.js';
|
|
|
85
89
|
import { postgresStorage } from '../lib/storage/postgres/index.js';
|
|
86
90
|
import { isPlaceholderValue, loadEnv } from '../lib/loadEnv.js';
|
|
87
91
|
import { flushTracing } from '../lib/observability/tracer.js';
|
|
92
|
+
import { setLogLevel } from '../lib/runtime/logging.js';
|
|
88
93
|
// Re-exported for existing importers (tests/failNarration.test.ts).
|
|
89
94
|
export { describeTurnError } from '../lib/a2a/executor.js';
|
|
90
95
|
/** Minimum bearer-secret length below which the server warns. */
|
|
@@ -101,8 +106,10 @@ function envInt(name, fallback) {
|
|
|
101
106
|
}
|
|
102
107
|
function envTrustProxy() {
|
|
103
108
|
const raw = process.env.A2A_TRUST_PROXY?.trim();
|
|
109
|
+
// Unset: trust no proxy, so a client cannot choose its own IP with
|
|
110
|
+
// X-Forwarded-For. A public server must set it (the posture check below).
|
|
104
111
|
if (!raw)
|
|
105
|
-
return
|
|
112
|
+
return false;
|
|
106
113
|
if (/^\d+$/.test(raw))
|
|
107
114
|
return Number(raw);
|
|
108
115
|
if (raw === 'true')
|
|
@@ -131,8 +138,9 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
131
138
|
// the root span carries the full user message and answer.
|
|
132
139
|
if (process.env.OTEL_CONSOLE_SPANS === undefined)
|
|
133
140
|
process.env.OTEL_CONSOLE_SPANS = 'false';
|
|
134
|
-
//
|
|
135
|
-
|
|
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');
|
|
136
144
|
const publicUrl = process.env.PUBLIC_URL?.trim() || undefined;
|
|
137
145
|
const port = envInt('PORT', 4000);
|
|
138
146
|
// ── Authentication posture ─────────────────────────────────────────────────
|
|
@@ -219,7 +227,28 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
219
227
|
}
|
|
220
228
|
return n;
|
|
221
229
|
};
|
|
222
|
-
|
|
230
|
+
// `*` serves every file in the agents directory and every registry row,
|
|
231
|
+
// stated on purpose; unset means the same off a public URL (ADR 0039).
|
|
232
|
+
const servedList = list('A2A_SERVED_AGENTS');
|
|
233
|
+
const servedAgents = servedList?.length === 1 && servedList[0] === '*' ? undefined : servedList;
|
|
234
|
+
// ── Public posture (ADR 0039) ──────────────────────────────────────────────
|
|
235
|
+
// A server on a public URL states how it authenticates, which agents it
|
|
236
|
+
// answers, and how many proxies stand in front of it. Each default was the
|
|
237
|
+
// permissive choice, so none is taken silently here.
|
|
238
|
+
if (publicUrl) {
|
|
239
|
+
const missing = [];
|
|
240
|
+
if (!process.env.A2A_AUTH?.trim()) {
|
|
241
|
+
missing.push('A2A_AUTH: callers or jwt (one identity per caller; recommended), header (a gateway authenticates users), or secret (one shared secret; any holder can act as any user by naming them in X-User-Id)');
|
|
242
|
+
}
|
|
243
|
+
if (!servedList?.length) {
|
|
244
|
+
missing.push('A2A_SERVED_AGENTS: the agent ids this server answers, comma-separated, or * for every file in the agents directory and every registry row');
|
|
245
|
+
}
|
|
246
|
+
if (!process.env.A2A_TRUST_PROXY?.trim()) {
|
|
247
|
+
missing.push('A2A_TRUST_PROXY: how many proxies stand in front of this server (1 behind one load balancer), or false when clients connect directly');
|
|
248
|
+
}
|
|
249
|
+
if (missing.length)
|
|
250
|
+
fatal(`PUBLIC_URL is set, so this server must state its security posture. Set:\n - ${missing.join('\n - ')}`);
|
|
251
|
+
}
|
|
223
252
|
const registryAgents = list('A2A_REGISTRY_AGENTS');
|
|
224
253
|
// Storage: Postgres when DATABASE_URL is set (every durable store, safe
|
|
225
254
|
// across instances); otherwise createA2AApp's default (Supabase, else memory).
|
|
@@ -305,6 +334,8 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
305
334
|
allowSchemaMismatch: process.env.ALLOW_SCHEMA_MISMATCH?.trim().toLowerCase() === 'true',
|
|
306
335
|
turnLockWaitMs: envInt('A2A_TURN_LOCK_WAIT_MS', 30_000),
|
|
307
336
|
maxConcurrentTasks: envInt('A2A_MAX_CONCURRENT_TASKS', 0),
|
|
337
|
+
maxConcurrentPerScope: envInt('A2A_MAX_CONCURRENT_PER_SCOPE', 4),
|
|
338
|
+
maxConcurrentPerCaller: envInt('A2A_MAX_CONCURRENT_PER_CALLER', 0),
|
|
308
339
|
rateLimit: { windowMs: envInt('A2A_RATE_LIMIT_WINDOW_MS', 15 * 60 * 1000), max: envInt('A2A_RATE_LIMIT_MAX', 60) },
|
|
309
340
|
authFailureLimit: { windowMs: 15 * 60 * 1000, max: envInt('A2A_AUTH_FAILURE_MAX', 30) },
|
|
310
341
|
trustProxy: envTrustProxy(),
|
|
@@ -334,7 +365,7 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
334
365
|
const authMode = `${identityLabel || (secret ? 'bearer secret' : 'no secret (loopback only)')}; keys: ${keyMode === 'byok' ? "caller's X-API-Key (byok)" : "server's own"}`;
|
|
335
366
|
// The secret is not needed past this point; drop the reference.
|
|
336
367
|
secret = undefined;
|
|
337
|
-
const { app, config, sessionBackend, shutdown } = built;
|
|
368
|
+
const { app, config, sessionBackend, shutdown, markUnready } = built;
|
|
338
369
|
const server = await new Promise((resolve) => {
|
|
339
370
|
const s = host ? app.listen(port, host, () => resolve(s)) : app.listen(port, () => resolve(s));
|
|
340
371
|
});
|
|
@@ -364,13 +395,26 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
364
395
|
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
365
396
|
let stopping = false;
|
|
366
397
|
const graceMs = envInt('A2A_SHUTDOWN_GRACE_MS', 25_000);
|
|
398
|
+
// A load balancer keeps sending requests for a few seconds after SIGTERM,
|
|
399
|
+
// until it sees /readyz fail and deregisters this instance. Closing the
|
|
400
|
+
// listener at once would refuse those requests; fail readiness first and
|
|
401
|
+
// keep serving for the delay, then close and drain. The delay is part of
|
|
402
|
+
// the grace budget (running tasks keep running through it), so a stop still
|
|
403
|
+
// takes at most A2A_SHUTDOWN_GRACE_MS, inside Kubernetes' default 30 s.
|
|
404
|
+
const delayMs = Math.min(envInt('A2A_SHUTDOWN_DELAY_MS', 5_000), graceMs);
|
|
367
405
|
const stop = async (signal) => {
|
|
368
406
|
if (stopping)
|
|
369
407
|
return;
|
|
370
408
|
stopping = true;
|
|
371
|
-
|
|
409
|
+
markUnready();
|
|
410
|
+
if (delayMs > 0) {
|
|
411
|
+
emit('info', `[A2A] ${signal}: /readyz now fails; still serving for ${delayMs} ms while the load balancer deregisters this instance.`);
|
|
412
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
413
|
+
}
|
|
414
|
+
const drainMs = graceMs - delayMs;
|
|
415
|
+
emit('info', `[A2A] ${signal}: draining — no new tasks; waiting up to ${drainMs} ms for running ones.`);
|
|
372
416
|
server.close();
|
|
373
|
-
const canceled = await shutdown(
|
|
417
|
+
const canceled = await shutdown(drainMs);
|
|
374
418
|
if (canceled > 0)
|
|
375
419
|
emit('warn', `[A2A] ⚠ ${canceled} task(s) did not finish in time and were canceled.`);
|
|
376
420
|
// Give canceled tasks a moment to publish their final status, then flush
|
|
@@ -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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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,
|
package/dist/scripts/db.d.ts
CHANGED
|
@@ -22,5 +22,14 @@
|
|
|
22
22
|
/** db/migrations/NNNN_name.sql, in numeric order. */
|
|
23
23
|
export declare function migrationFiles(dir?: string): string[];
|
|
24
24
|
export declare function installOrder(withTelemetry: boolean): string[];
|
|
25
|
+
/**
|
|
26
|
+
* The whole install as one psql script: an advisory lock first, then every
|
|
27
|
+
* file in order with a progress line before each. psql runs it with
|
|
28
|
+
* --single-transaction and ON_ERROR_STOP, so a failure anywhere rolls the
|
|
29
|
+
* whole install back (no half-applied migration), and a second `apply`
|
|
30
|
+
* started meanwhile waits on the lock instead of interleaving with this one.
|
|
31
|
+
*/
|
|
32
|
+
export declare const APPLY_LOCK_KEY = "melchizedek-db apply";
|
|
33
|
+
export declare function applyScript(files: string[], sqlOf: (file: string) => string, label?: (file: string) => string): string;
|
|
25
34
|
/** postgres://user:pass@host:port/db?sslmode=… → libpq environment variables. */
|
|
26
35
|
export declare function pgEnv(url: string): Record<string, string>;
|
package/dist/scripts/db.js
CHANGED
|
@@ -42,6 +42,20 @@ export function installOrder(withTelemetry) {
|
|
|
42
42
|
const base = [...migrationFiles(), 'hardening.sql'];
|
|
43
43
|
return withTelemetry ? [...base, 'telemetry.sql', 'hardening.sql'] : base;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The whole install as one psql script: an advisory lock first, then every
|
|
47
|
+
* file in order with a progress line before each. psql runs it with
|
|
48
|
+
* --single-transaction and ON_ERROR_STOP, so a failure anywhere rolls the
|
|
49
|
+
* whole install back (no half-applied migration), and a second `apply`
|
|
50
|
+
* started meanwhile waits on the lock instead of interleaving with this one.
|
|
51
|
+
*/
|
|
52
|
+
export const APPLY_LOCK_KEY = 'melchizedek-db apply';
|
|
53
|
+
export function applyScript(files, sqlOf, label = (f) => `db/${f}`) {
|
|
54
|
+
const parts = [`SELECT pg_advisory_xact_lock(hashtext('${APPLY_LOCK_KEY}'));\n`];
|
|
55
|
+
for (const file of files)
|
|
56
|
+
parts.push(`\\echo '→ applying ${label(file).replace(/'/g, '')}'\n`, sqlOf(file), '\n');
|
|
57
|
+
return parts.join('');
|
|
58
|
+
}
|
|
45
59
|
/** postgres://user:pass@host:port/db?sslmode=… → libpq environment variables. */
|
|
46
60
|
export function pgEnv(url) {
|
|
47
61
|
const u = new URL(url);
|
|
@@ -100,21 +114,22 @@ async function main() {
|
|
|
100
114
|
console.error(' or set DATABASE_URL to the Postgres connection string (Supabase: Settings → Database).');
|
|
101
115
|
process.exit(1);
|
|
102
116
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
// One transaction under one lock (see applyScript): all of it, or none.
|
|
118
|
+
const script = applyScript(installOrder(withTelemetry), sqlOf, (f) => `db/${f}${schema === 'public' ? '' : ` into schema ${schema}`}`);
|
|
119
|
+
const r = spawnSync('psql', ['-v', 'ON_ERROR_STOP=1', '-q', '--single-transaction', '-f', '-'], {
|
|
120
|
+
input: script,
|
|
121
|
+
stdio: ['pipe', 'inherit', 'inherit'],
|
|
122
|
+
// The connection goes to psql as PG* variables, never as an
|
|
123
|
+
// argument, so the password stays out of the process list.
|
|
124
|
+
env: { ...process.env, ...pgEnv(url) },
|
|
125
|
+
});
|
|
126
|
+
if (r.error) {
|
|
127
|
+
console.error(`✗ could not run psql (${r.error.message}). Install the Postgres client, or use \`print\`.`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
if (r.status !== 0) {
|
|
131
|
+
console.error('✗ the install failed and was rolled back: the database is as it was before `apply`.');
|
|
132
|
+
process.exit(r.status ?? 1);
|
|
118
133
|
}
|
|
119
134
|
console.log('✓ database is installed and hardened');
|
|
120
135
|
return;
|
|
@@ -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 {
|
|
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
|
-
//
|
|
22
|
-
setLogLevel(
|
|
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
|
-
|
|
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.
|
|
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 (!
|
|
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,
|