melchizedek-agents 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +252 -76
- package/DOCUMENTATION.md +44 -15
- package/QUICKSTART.md +9 -6
- package/README.md +6 -9
- package/config/agents/syndicateSchema.yaml +3 -0
- package/dist/lib/adkPeer.d.ts +63 -0
- package/dist/lib/adkPeer.js +240 -0
- package/dist/lib/compile.d.ts +40 -2
- package/dist/lib/compile.js +80 -12
- package/dist/lib/compileAdk.d.ts +7 -4
- package/dist/lib/compileAdk.js +22 -12
- package/dist/lib/compileNative.d.ts +28 -2
- package/dist/lib/compileNative.js +60 -4
- package/dist/lib/doctor.d.ts +28 -1
- package/dist/lib/doctor.js +63 -2
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.js +5 -0
- package/dist/lib/models/adapterResolver.d.ts +13 -11
- package/dist/lib/models/adapterResolver.js +11 -10
- package/dist/lib/models/adkGeminiAdapter.js +2 -0
- package/dist/lib/models/adkShim.d.ts +1 -1
- package/dist/lib/models/adkShim.js +1 -1
- package/dist/lib/models/capabilities.js +13 -10
- package/dist/lib/models/claudeLlm.js +2 -2
- package/dist/lib/models/fallback.d.ts +1 -1
- package/dist/lib/models/fallback.js +1 -1
- package/dist/lib/models/gatewayLlm.js +2 -2
- package/dist/lib/models/geminiAdapter.d.ts +6 -4
- package/dist/lib/models/geminiAdapter.js +28 -5
- package/dist/lib/models/geminiState.d.ts +21 -0
- package/dist/lib/models/geminiState.js +26 -0
- package/dist/lib/models/genaiMapping.d.ts +24 -4
- package/dist/lib/models/genaiMapping.js +109 -6
- package/dist/lib/models/gptLlm.js +2 -2
- package/dist/lib/models/grokLlm.js +2 -2
- package/dist/lib/models/kimiLlm.js +2 -2
- package/dist/lib/models/ollamaLlm.js +2 -2
- package/dist/lib/models/registry.d.ts +5 -1
- package/dist/lib/models/registry.js +17 -12
- package/dist/lib/models/tracedGemini.d.ts +1 -1
- package/dist/lib/models/tracedGemini.js +1 -1
- package/dist/lib/runtime/adkSessionBridge.d.ts +1 -1
- package/dist/lib/runtime/adkSessionBridge.js +1 -1
- package/dist/lib/runtime/approvals.d.ts +5 -0
- package/dist/lib/runtime/approvals.js +39 -8
- package/dist/lib/runtime/credentials.d.ts +3 -1
- package/dist/lib/runtime/credentials.js +6 -1
- package/dist/lib/runtime/native/agentLoop.d.ts +7 -1
- package/dist/lib/runtime/native/agentLoop.js +31 -13
- package/dist/lib/runtime/native/delegate.d.ts +53 -0
- package/dist/lib/runtime/native/delegate.js +91 -8
- package/dist/lib/runtime/native/interrupts.d.ts +6 -3
- package/dist/lib/runtime/native/interrupts.js +23 -4
- package/dist/lib/runtime/native/request.d.ts +1 -1
- package/dist/lib/runtime/native/request.js +1 -1
- package/dist/lib/runtime/native/selfCorrection.d.ts +64 -6
- package/dist/lib/runtime/native/selfCorrection.js +82 -8
- package/dist/lib/runtime/native/step.d.ts +14 -6
- package/dist/lib/runtime/native/step.js +34 -10
- package/dist/lib/runtime/runtimeFlag.d.ts +13 -5
- package/dist/lib/runtime/runtimeFlag.js +14 -6
- package/dist/lib/runtime/syndicateTurn.d.ts +15 -10
- package/dist/lib/runtime/syndicateTurn.js +59 -32
- package/dist/lib/runtime/valueDepth.d.ts +23 -0
- package/dist/lib/runtime/valueDepth.js +41 -0
- package/dist/lib/session/supabaseSessionService.d.ts +1 -1
- package/dist/lib/session/supabaseSessionService.js +1 -1
- package/dist/lib/session/transcript.d.ts +1 -1
- package/dist/lib/session/transcript.js +5 -3
- package/dist/lib/storage/postgres/sessionService.d.ts +1 -1
- package/dist/lib/storage/postgres/sessionService.js +1 -1
- package/dist/lib/syndicateSchema.d.ts +2 -0
- package/dist/lib/syndicateSchema.js +27 -6
- package/dist/lib/toolRegistry.d.ts +12 -1
- package/dist/lib/toolRegistry.js +42 -2
- package/dist/lib/tools/adkTool.d.ts +1 -1
- package/dist/lib/tools/adkTool.js +1 -1
- package/dist/lib/tools/collectionsSearchTool.d.ts +1 -1
- package/dist/lib/tools/collectionsSearchTool.js +1 -1
- package/dist/lib/tools/generateImageTool.d.ts +1 -1
- package/dist/lib/tools/inspectImageTool.d.ts +1 -1
- package/dist/lib/tools/urlContextTool.d.ts +1 -1
- package/dist/lib/tools/urlContextTool.js +1 -1
- package/dist/lib/tools/webExtractTool.d.ts +1 -1
- package/dist/lib/tools/webSearchTool.d.ts +1 -1
- package/dist/lib/tools/webSearchTool.js +1 -1
- package/dist/lib/tools/xApiSearchTool.d.ts +1 -1
- package/dist/lib/tools/xSearchTool.d.ts +1 -1
- package/dist/lib/tools/xSearchTool.js +1 -1
- package/dist/lib/wiki/agentRun.d.ts +1 -1
- package/dist/lib/wiki/agentRun.js +2 -1
- package/dist/lib/workflow/agentNode.d.ts +21 -4
- package/dist/lib/workflow/agentNode.js +96 -5
- package/dist/lib/workflow/graph.d.ts +5 -4
- package/dist/lib/workflow/graph.js +14 -4
- package/dist/lib/workflow/resume.d.ts +10 -9
- package/dist/lib/workflow/resume.js +13 -12
- package/dist/lib/workflow/scheduler.d.ts +10 -7
- package/dist/lib/workflow/scheduler.js +15 -2
- package/dist/lib/workflow.d.ts +33 -11
- package/dist/lib/workflow.js +49 -27
- package/dist/lib/workflowConfig.d.ts +4 -0
- package/dist/lib/workflowConfig.js +4 -0
- package/dist/scripts/doctor.d.ts +3 -1
- package/dist/scripts/doctor.js +4 -2
- package/package.json +6 -3
|
@@ -12,9 +12,14 @@
|
|
|
12
12
|
* read differently on the other:
|
|
13
13
|
*
|
|
14
14
|
* THE MODEL (`retries.model_errors`, default 2; 0 off):
|
|
15
|
-
* -
|
|
16
|
-
* after the agent's own
|
|
17
|
-
* to the toolsDict last
|
|
15
|
+
* - The reflection tool, `adk_handle_model_error`, is one of every step's
|
|
16
|
+
* tools, after the agent's own: the plugin's beforeModelCallback adds it
|
|
17
|
+
* to the request's toolsDict last, and never to its config. Whether the
|
|
18
|
+
* model is told of it depends on the model class ADK hands the request
|
|
19
|
+
* to (ADR 0097): the ADK shim and the engine's own ADK classes declare
|
|
20
|
+
* the toolsDict, so the tool is declared; ADK's own Gemini sends the
|
|
21
|
+
* config alone, so a Gemini model is never told of it. The step asks
|
|
22
|
+
* `declaresReflectionTool` and runs the tool either way.
|
|
18
23
|
* - Each response is checked (afterModelCallback), partials included. A
|
|
19
24
|
* response that calls the reflection tool itself, or whose finish reason
|
|
20
25
|
* is MALFORMED_FUNCTION_CALL, is replaced by a call to the reflection
|
|
@@ -22,6 +27,13 @@
|
|
|
22
27
|
* the retry's number counted per agent in the run. The loop runs that
|
|
23
28
|
* call like any other, and the tool answers with reflection guidance.
|
|
24
29
|
* Any other response resets the agent's count.
|
|
30
|
+
* - ON A GEMINI MODEL the stored reflection call is signed (ADR 0103), where
|
|
31
|
+
* ADK's plugin stores it unsigned: it carries the replaced response's
|
|
32
|
+
* thoughtSignature, or on Gemini 3 Gemini's documented placeholder when
|
|
33
|
+
* the response had none (a MALFORMED_FUNCTION_CALL has no parts). Gemini 3
|
|
34
|
+
* rejects the next request with an unsigned current-turn call (400,
|
|
35
|
+
* "missing a thought_signature"); ADK's runtime still does. This is the
|
|
36
|
+
* one place the native loop stores what ADK would not (reflectionSigning).
|
|
25
37
|
* - Past the limit the step ends on ADK's error event for a callback that
|
|
26
38
|
* threw: UNKNOWN_ERROR, and the plugin manager's message.
|
|
27
39
|
*
|
|
@@ -46,6 +58,7 @@
|
|
|
46
58
|
* reads responses as.
|
|
47
59
|
*/
|
|
48
60
|
import { randomUUID } from 'node:crypto';
|
|
61
|
+
import { GEMINI_PROVIDER, PLACEHOLDER_THOUGHT_SIGNATURE } from '../../models/geminiState.js';
|
|
49
62
|
export const DEFAULT_MODEL_ERROR_RETRIES = 2;
|
|
50
63
|
export const DEFAULT_TOOL_ERROR_RETRIES = 3;
|
|
51
64
|
export const ADK_HANDLE_MODEL_ERROR = 'adk_handle_model_error';
|
|
@@ -54,6 +67,63 @@ const RESERVED_TOOL_CALL = 'RESERVED_TOOL_CALL';
|
|
|
54
67
|
/** The finish reasons the model plugin retries (its onModelErrors default). */
|
|
55
68
|
const MODEL_ERRORS = ['MALFORMED_FUNCTION_CALL'];
|
|
56
69
|
const MODEL_PLUGIN = 'reflect_retry_model_plugin';
|
|
70
|
+
// ── Where ADK declares the reflection tool ──────────────────────────────────
|
|
71
|
+
/** Gemini adapters a caller handed over as a contract adapter or behind the ADK shim: on ADK the shim declares the toolsDict. */
|
|
72
|
+
const SHIMMED_GEMINI = new WeakSet();
|
|
73
|
+
/**
|
|
74
|
+
* Marks `adapter` as one the ADK runtime would call through the ADK shim
|
|
75
|
+
* (a resolver's shim or contract adapter, lib/compileNative.ts), not through
|
|
76
|
+
* ADK's own Gemini. Returns it.
|
|
77
|
+
*/
|
|
78
|
+
export function servedThroughShim(adapter) {
|
|
79
|
+
SHIMMED_GEMINI.add(adapter);
|
|
80
|
+
return adapter;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Whether the ADK runtime would tell this adapter's model of the reflection
|
|
84
|
+
* tool (ADR 0097). The plugin puts the tool in the toolsDict alone. Every
|
|
85
|
+
* model class the engine hands ADK declares the toolsDict, but ADK's own
|
|
86
|
+
* Gemini (and TracedGemini over it) sends only the request's config, which
|
|
87
|
+
* never holds it. A Gemini adapter stands for ADK's Gemini unless a caller
|
|
88
|
+
* handed it over behind the shim (servedThroughShim).
|
|
89
|
+
*/
|
|
90
|
+
export function declaresReflectionTool(adapter) {
|
|
91
|
+
return !standsForAdkGemini(adapter);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether this adapter stands, on the native runtime, for ADK's own Gemini
|
|
95
|
+
* (TracedGemini) on the ADK runtime: a Gemini adapter no caller handed over
|
|
96
|
+
* behind the shim. The step declares no reflection tool to it (ADR 0097) and
|
|
97
|
+
* stores no `turnComplete` on its events, as ADK's Gemini writes none
|
|
98
|
+
* (ADR 0100).
|
|
99
|
+
*/
|
|
100
|
+
export function standsForAdkGemini(adapter) {
|
|
101
|
+
return adapter.provider === GEMINI_PROVIDER && !SHIMMED_GEMINI.has(adapter);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* How self-correction signs the reflection call it stores in place of this
|
|
105
|
+
* adapter's response, or undefined to store it unsigned, as ADK's plugin
|
|
106
|
+
* does. A Gemini adapter's call carries the replaced response's signature;
|
|
107
|
+
* on a Gemini 3 model, whose API rejects an unsigned current-turn call, the
|
|
108
|
+
* placeholder stands in when there was none.
|
|
109
|
+
*/
|
|
110
|
+
export function reflectionSigning(adapter, model) {
|
|
111
|
+
if (adapter.provider !== GEMINI_PROVIDER)
|
|
112
|
+
return undefined;
|
|
113
|
+
return isGemini3(model) ? { placeholder: PLACEHOLDER_THOUGHT_SIGNATURE } : {};
|
|
114
|
+
}
|
|
115
|
+
/** A Gemini 3 model id, bare or under a resource path (`models/…`, `publishers/google/models/…`). */
|
|
116
|
+
function isGemini3(model) {
|
|
117
|
+
if (!model)
|
|
118
|
+
return false;
|
|
119
|
+
return model.slice(model.lastIndexOf('/') + 1).startsWith('gemini-3');
|
|
120
|
+
}
|
|
121
|
+
/** The signature the replaced response carried: its first call's, else its first signed part's. */
|
|
122
|
+
function signatureOf(response) {
|
|
123
|
+
const parts = response.content?.parts ?? [];
|
|
124
|
+
const signed = (p) => typeof p.thoughtSignature === 'string' && p.thoughtSignature.length > 0;
|
|
125
|
+
return (parts.find((p) => p.functionCall && signed(p)) ?? parts.find(signed))?.thoughtSignature;
|
|
126
|
+
}
|
|
57
127
|
// ── Counting, per run ────────────────────────────────────────────────────────
|
|
58
128
|
/** ADK's ScopedFailureTracker: failures per name, within one run (invocation scope). */
|
|
59
129
|
class FailureTracker {
|
|
@@ -100,7 +170,7 @@ export class SelfCorrection {
|
|
|
100
170
|
const max = this.modelErrors;
|
|
101
171
|
const tracker = this.#models;
|
|
102
172
|
const name = agentName || 'default_model';
|
|
103
|
-
const retry = (errorType, errorDetails, finishReason) => {
|
|
173
|
+
const retry = (errorType, errorDetails, finishReason, signature) => {
|
|
104
174
|
const count = tracker.increment(invocationId, name);
|
|
105
175
|
if (count > max) {
|
|
106
176
|
const thrown = `The model has failed consecutively ${max} times and the retry limit has been exceeded.`;
|
|
@@ -108,18 +178,22 @@ export class SelfCorrection {
|
|
|
108
178
|
}
|
|
109
179
|
const args = { response_type: REFLECT_AND_RETRY_RESPONSE_TYPE, error_type: errorType, error_details: errorDetails, finish_reason: finishReason, retry_count: count };
|
|
110
180
|
const response = {
|
|
111
|
-
content: {
|
|
181
|
+
content: {
|
|
182
|
+
role: 'model',
|
|
183
|
+
parts: [{ functionCall: { id: `${ADK_HANDLE_MODEL_ERROR}_${randomUUID()}`, name: ADK_HANDLE_MODEL_ERROR, args }, ...(signature ? { thoughtSignature: signature } : {}) }],
|
|
184
|
+
},
|
|
112
185
|
};
|
|
113
186
|
return { response, replaced: true };
|
|
114
187
|
};
|
|
115
188
|
return {
|
|
116
189
|
tools: [reflectionTool(max)],
|
|
117
|
-
afterModel(response) {
|
|
190
|
+
afterModel(response, signing) {
|
|
191
|
+
const signature = () => (signing ? (signatureOf(response) ?? signing.placeholder) : undefined);
|
|
118
192
|
if (response.content?.parts?.some((p) => p.functionCall?.name === ADK_HANDLE_MODEL_ERROR)) {
|
|
119
|
-
return retry(RESERVED_TOOL_CALL, `Model attempted to call reserved tool ${ADK_HANDLE_MODEL_ERROR} directly. This tool is reserved for framework use only. Do not call it.`, 'OTHER');
|
|
193
|
+
return retry(RESERVED_TOOL_CALL, `Model attempted to call reserved tool ${ADK_HANDLE_MODEL_ERROR} directly. This tool is reserved for framework use only. Do not call it.`, 'OTHER', signature());
|
|
120
194
|
}
|
|
121
195
|
if (response.finishReason && MODEL_ERRORS.includes(response.finishReason)) {
|
|
122
|
-
return retry(response.errorCode ?? 'MODEL_ERROR', response.errorMessage ?? 'Model error encountered.', response.finishReason);
|
|
196
|
+
return retry(response.errorCode ?? 'MODEL_ERROR', response.errorMessage ?? 'Model error encountered.', response.finishReason, signature());
|
|
123
197
|
}
|
|
124
198
|
tracker.reset(invocationId, name);
|
|
125
199
|
return { response, replaced: false };
|
|
@@ -23,16 +23,24 @@
|
|
|
23
23
|
* - THE EVENT. Each response becomes ADK's event for it: created before
|
|
24
24
|
* the call with the run's id, the agent as author and the branch, and
|
|
25
25
|
* merged with the response as the shim maps it (modelResponseToLlmResponse),
|
|
26
|
-
* a fresh id for every response after the first.
|
|
26
|
+
* a fresh id for every response after the first. For a Gemini adapter
|
|
27
|
+
* that stands for ADK's own Gemini (standsForAdkGemini), the event has no
|
|
28
|
+
* turnComplete, since ADK's Gemini writes none (ADR 0100). A tool call with no id
|
|
27
29
|
* gets ADK's `adk-<uuid>`; a call to a long-running tool is listed in
|
|
28
30
|
* longRunningToolIds; a set_model_response call becomes its arguments as
|
|
29
31
|
* JSON text, ending the step (skipSummarization). An answer with no
|
|
30
32
|
* parts, no error and no usage makes no event.
|
|
31
33
|
* - SELF-CORRECTION (ADR 0075). With the caller's `correction`, the
|
|
32
|
-
*
|
|
33
|
-
* response passes through it first, as ADK's reflect-and-retry
|
|
34
|
-
* plugin sees it: a retry may stand in its place, or the step may
|
|
35
|
-
* the plugin's UNKNOWN_ERROR event (lib/runtime/native/
|
|
34
|
+
* reflection tool is one of the step's tools, after the agent's, and
|
|
35
|
+
* each response passes through it first, as ADK's reflect-and-retry
|
|
36
|
+
* model plugin sees it: a retry may stand in its place, or the step may
|
|
37
|
+
* end on the plugin's UNKNOWN_ERROR event (lib/runtime/native/
|
|
38
|
+
* selfCorrection.ts). The request declares the tool only where ADK's
|
|
39
|
+
* model class would (declaresReflectionTool): never to a Gemini model
|
|
40
|
+
* ADK's own Gemini would serve, which sends no toolsDict (ADR 0097).
|
|
41
|
+
* On a Gemini adapter the reflection call stored in a response's place
|
|
42
|
+
* carries its signature, or Gemini 3's placeholder (reflectionSigning,
|
|
43
|
+
* ADR 0103), where ADK stores it unsigned.
|
|
36
44
|
* - A THROWN FAILURE. The contract forbids an adapter to throw, but a
|
|
37
45
|
* leaf that does (an ADK-era model class) is read as ADK reads it. With
|
|
38
46
|
* a `redirect`, a provider-side failure (errorDecision, lib/models/
|
|
@@ -132,7 +140,7 @@ export interface ModelStepResult {
|
|
|
132
140
|
stopped?: StepStop;
|
|
133
141
|
/** The failure `redirect` took: nothing was stored for it. */
|
|
134
142
|
redirected?: ModelError;
|
|
135
|
-
/** The client-side tools the request declared, by name: what runs the answer's calls. */
|
|
143
|
+
/** The client-side tools the request declared, and the reflection tool when it was not declared, by name: what runs the answer's calls. */
|
|
136
144
|
tools: Map<string, unknown>;
|
|
137
145
|
}
|
|
138
146
|
export declare function eitherSignal(...candidates: Array<AbortSignal | undefined>): AbortSignal | undefined;
|
|
@@ -23,16 +23,24 @@
|
|
|
23
23
|
* - THE EVENT. Each response becomes ADK's event for it: created before
|
|
24
24
|
* the call with the run's id, the agent as author and the branch, and
|
|
25
25
|
* merged with the response as the shim maps it (modelResponseToLlmResponse),
|
|
26
|
-
* a fresh id for every response after the first.
|
|
26
|
+
* a fresh id for every response after the first. For a Gemini adapter
|
|
27
|
+
* that stands for ADK's own Gemini (standsForAdkGemini), the event has no
|
|
28
|
+
* turnComplete, since ADK's Gemini writes none (ADR 0100). A tool call with no id
|
|
27
29
|
* gets ADK's `adk-<uuid>`; a call to a long-running tool is listed in
|
|
28
30
|
* longRunningToolIds; a set_model_response call becomes its arguments as
|
|
29
31
|
* JSON text, ending the step (skipSummarization). An answer with no
|
|
30
32
|
* parts, no error and no usage makes no event.
|
|
31
33
|
* - SELF-CORRECTION (ADR 0075). With the caller's `correction`, the
|
|
32
|
-
*
|
|
33
|
-
* response passes through it first, as ADK's reflect-and-retry
|
|
34
|
-
* plugin sees it: a retry may stand in its place, or the step may
|
|
35
|
-
* the plugin's UNKNOWN_ERROR event (lib/runtime/native/
|
|
34
|
+
* reflection tool is one of the step's tools, after the agent's, and
|
|
35
|
+
* each response passes through it first, as ADK's reflect-and-retry
|
|
36
|
+
* model plugin sees it: a retry may stand in its place, or the step may
|
|
37
|
+
* end on the plugin's UNKNOWN_ERROR event (lib/runtime/native/
|
|
38
|
+
* selfCorrection.ts). The request declares the tool only where ADK's
|
|
39
|
+
* model class would (declaresReflectionTool): never to a Gemini model
|
|
40
|
+
* ADK's own Gemini would serve, which sends no toolsDict (ADR 0097).
|
|
41
|
+
* On a Gemini adapter the reflection call stored in a response's place
|
|
42
|
+
* carries its signature, or Gemini 3's placeholder (reflectionSigning,
|
|
43
|
+
* ADR 0103), where ADK stores it unsigned.
|
|
36
44
|
* - A THROWN FAILURE. The contract forbids an adapter to throw, but a
|
|
37
45
|
* leaf that does (an ADK-era model class) is read as ADK reads it. With
|
|
38
46
|
* a `redirect`, a provider-side failure (errorDecision, lib/models/
|
|
@@ -53,7 +61,7 @@
|
|
|
53
61
|
*/
|
|
54
62
|
import { randomUUID } from 'node:crypto';
|
|
55
63
|
import { errorDecision, errorText } from '../../models/errorResponse.js';
|
|
56
|
-
import { modelResponseToLlmResponse } from '../../models/genaiMapping.js';
|
|
64
|
+
import { contractModelResponse, modelResponseToLlmResponse } from '../../models/genaiMapping.js';
|
|
57
65
|
import { resolveAdapter } from '../../models/registry.js';
|
|
58
66
|
import { traceLlmGeneration } from '../../observability/tracer.js';
|
|
59
67
|
import { createTurnEvent, getFunctionCalls, newEventId } from '../events.js';
|
|
@@ -61,6 +69,7 @@ import { currentTurnControl, currentTurnSignal, stopCode, stopMessage } from '..
|
|
|
61
69
|
import { toolOf } from '../../tools/tool.js';
|
|
62
70
|
import { ADK_CALL_ID_PREFIX } from './history.js';
|
|
63
71
|
import { SET_MODEL_RESPONSE, buildModelRequest } from './request.js';
|
|
72
|
+
import { declaresReflectionTool, reflectionSigning, standsForAdkGemini } from './selfCorrection.js';
|
|
64
73
|
export function eitherSignal(...candidates) {
|
|
65
74
|
const signals = [...new Set(candidates.filter((s) => s !== undefined))];
|
|
66
75
|
return signals.length <= 1 ? signals[0] : AbortSignal.any(signals);
|
|
@@ -108,6 +117,10 @@ function thrownModelError(err) {
|
|
|
108
117
|
export async function runModelStep(options) {
|
|
109
118
|
const { agent, session, sessions } = options;
|
|
110
119
|
const signal = eitherSignal(options.signal, currentTurnSignal());
|
|
120
|
+
const adapter = options.adapter ?? resolveAdapter(agent.model);
|
|
121
|
+
// ADK's plugin puts the reflection tool in the toolsDict alone: declared where the model class reads it, run either way.
|
|
122
|
+
const correctionTools = options.correction?.tools ?? [];
|
|
123
|
+
const declareCorrection = correctionTools.length > 0 && declaresReflectionTool(adapter);
|
|
111
124
|
const { request, tools } = await buildModelRequest(agent, {
|
|
112
125
|
session,
|
|
113
126
|
invocationId: options.invocationId,
|
|
@@ -119,15 +132,17 @@ export async function runModelStep(options) {
|
|
|
119
132
|
...(options.stateOverlay ? { stateOverlay: options.stateOverlay } : {}),
|
|
120
133
|
stream: options.stream ?? false,
|
|
121
134
|
...(signal ? { signal } : {}),
|
|
122
|
-
...(
|
|
135
|
+
...(declareCorrection ? { extraTools: correctionTools } : {}),
|
|
123
136
|
...(options.workflowScope ? { workflowScope: options.workflowScope } : {}),
|
|
124
137
|
});
|
|
138
|
+
if (!declareCorrection)
|
|
139
|
+
for (const tool of correctionTools)
|
|
140
|
+
tools.set(tool.name, tool);
|
|
125
141
|
if (options.model)
|
|
126
142
|
request.model = options.model;
|
|
127
143
|
const result = { request, text: '', thinking: '', toolCalls: [], longRunningToolIds: [], tools };
|
|
128
144
|
if (signal?.aborted)
|
|
129
145
|
return { ...result, stopped: stopOf() };
|
|
130
|
-
const adapter = options.adapter ?? resolveAdapter(agent.model);
|
|
131
146
|
// The event ADK creates before the call; each response after the first gets a fresh id and time.
|
|
132
147
|
const base = createTurnEvent({ invocationId: options.invocationId, author: agent.name, branch: options.branch });
|
|
133
148
|
let next = { id: base.id, timestamp: base.timestamp };
|
|
@@ -135,10 +150,18 @@ export async function runModelStep(options) {
|
|
|
135
150
|
const sources = new WeakMap();
|
|
136
151
|
// What the adapter itself threw, as opposed to the store or a callback.
|
|
137
152
|
let thrown;
|
|
153
|
+
// ADK's own Gemini writes no turnComplete, so its stand-in's events carry none (ADR 0100).
|
|
154
|
+
const adkGemini = standsForAdkGemini(adapter);
|
|
155
|
+
// How a reflection call stored in this adapter's place is signed (ADR 0103).
|
|
156
|
+
const signing = reflectionSigning(adapter, request.model);
|
|
138
157
|
async function* inner() {
|
|
139
158
|
try {
|
|
140
|
-
for await (const
|
|
159
|
+
for await (const unchecked of adapter.generate(request)) {
|
|
160
|
+
// The answer held to the contract, as the shim's mapping holds it (contractModelResponse): the step reads and stores the same parts.
|
|
161
|
+
const response = contractModelResponse(unchecked);
|
|
141
162
|
const mapped = modelResponseToLlmResponse(response);
|
|
163
|
+
if (adkGemini)
|
|
164
|
+
delete mapped.turnComplete;
|
|
142
165
|
sources.set(mapped, response);
|
|
143
166
|
yield mapped;
|
|
144
167
|
}
|
|
@@ -186,7 +209,8 @@ export async function runModelStep(options) {
|
|
|
186
209
|
return { ...result, response: source, redirected: source.error };
|
|
187
210
|
produced = true;
|
|
188
211
|
// Self-correction sees the response as ADK's afterModelCallback does: it may stand a retry in its place, or end the step.
|
|
189
|
-
|
|
212
|
+
// On Gemini the reflection call it stores is signed, so the next request does not 400 (ADR 0103).
|
|
213
|
+
const corrected = options.correction?.afterModel(traced, signing) ?? { response: traced, replaced: false };
|
|
190
214
|
if ('failed' in corrected) {
|
|
191
215
|
// ADK's runAndHandleError: a callback that threw ends the step on an error event of its own.
|
|
192
216
|
const { code, message } = corrected.failed;
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* lib/runtime/runtimeFlag.ts — which runtime runs a turn: MELCHIZEDEK_RUNTIME
|
|
3
3
|
* and the turn's `runtime` option (ADR 0045, ADR 0073).
|
|
4
4
|
*
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
5
|
+
* `native` runs each agent on the engine's own agent loop
|
|
6
|
+
* (lib/runtime/native/agentLoop.ts), `adk` on Google ADK's Runner. `native`
|
|
7
|
+
* is the default since 0.20.0 (ADR 0045, ADR 0102); `adk` needs the optional
|
|
8
|
+
* peer @google/adk installed, and leaves the package at 1.0.0. A feature the
|
|
9
|
+
* native runtime does not run yet fails before any model call with
|
|
9
10
|
* UnsupportedOnRuntimeError, which names the feature and the runtime.
|
|
10
11
|
*
|
|
11
12
|
* A leaf module: it imports nothing, so every caller (the compiler, the
|
|
@@ -18,7 +19,7 @@ export declare const RUNTIMES: readonly RuntimeName[];
|
|
|
18
19
|
export declare const DEFAULT_RUNTIME: RuntimeName;
|
|
19
20
|
/** MELCHIZEDEK_RUNTIME as set, or undefined when unset or blank. Any other value is a configuration error. */
|
|
20
21
|
export declare function runtimeSetting(env?: NodeJS.ProcessEnv): RuntimeName | undefined;
|
|
21
|
-
/** The runtime to use: the caller's option, else MELCHIZEDEK_RUNTIME, else
|
|
22
|
+
/** The runtime to use: the caller's option, else MELCHIZEDEK_RUNTIME, else native. */
|
|
22
23
|
export declare function chooseRuntime(option?: string, env?: NodeJS.ProcessEnv): RuntimeName;
|
|
23
24
|
/** A feature the chosen runtime does not run yet. Thrown before any model call. */
|
|
24
25
|
export declare class UnsupportedOnRuntimeError extends Error {
|
|
@@ -28,3 +29,10 @@ export declare class UnsupportedOnRuntimeError extends Error {
|
|
|
28
29
|
}
|
|
29
30
|
/** The error for a feature the native runtime does not run yet; `where` names the agent or syndicate. */
|
|
30
31
|
export declare function unsupportedOnNative(feature: string, where: string): UnsupportedOnRuntimeError;
|
|
32
|
+
/** Where the runtime in use came from: the caller's option, MELCHIZEDEK_RUNTIME, or the default. */
|
|
33
|
+
export type RuntimeSource = 'option' | 'MELCHIZEDEK_RUNTIME' | 'default';
|
|
34
|
+
/** The runtime chooseRuntime picks, and where it came from (the doctor prints both). */
|
|
35
|
+
export declare function describeRuntime(option?: string, env?: NodeJS.ProcessEnv): {
|
|
36
|
+
runtime: RuntimeName;
|
|
37
|
+
source: RuntimeSource;
|
|
38
|
+
};
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* lib/runtime/runtimeFlag.ts — which runtime runs a turn: MELCHIZEDEK_RUNTIME
|
|
3
3
|
* and the turn's `runtime` option (ADR 0045, ADR 0073).
|
|
4
4
|
*
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
5
|
+
* `native` runs each agent on the engine's own agent loop
|
|
6
|
+
* (lib/runtime/native/agentLoop.ts), `adk` on Google ADK's Runner. `native`
|
|
7
|
+
* is the default since 0.20.0 (ADR 0045, ADR 0102); `adk` needs the optional
|
|
8
|
+
* peer @google/adk installed, and leaves the package at 1.0.0. A feature the
|
|
9
|
+
* native runtime does not run yet fails before any model call with
|
|
9
10
|
* UnsupportedOnRuntimeError, which names the feature and the runtime.
|
|
10
11
|
*
|
|
11
12
|
* A leaf module: it imports nothing, so every caller (the compiler, the
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
*/
|
|
14
15
|
export const RUNTIMES = ['adk', 'native'];
|
|
15
16
|
/** The runtime when neither the turn nor MELCHIZEDEK_RUNTIME names one. */
|
|
16
|
-
export const DEFAULT_RUNTIME = '
|
|
17
|
+
export const DEFAULT_RUNTIME = 'native';
|
|
17
18
|
function asRuntime(raw, source) {
|
|
18
19
|
const value = raw.trim().toLowerCase();
|
|
19
20
|
if (value === 'adk' || value === 'native')
|
|
@@ -27,7 +28,7 @@ export function runtimeSetting(env = process.env) {
|
|
|
27
28
|
return undefined;
|
|
28
29
|
return asRuntime(raw, 'MELCHIZEDEK_RUNTIME');
|
|
29
30
|
}
|
|
30
|
-
/** The runtime to use: the caller's option, else MELCHIZEDEK_RUNTIME, else
|
|
31
|
+
/** The runtime to use: the caller's option, else MELCHIZEDEK_RUNTIME, else native. */
|
|
31
32
|
export function chooseRuntime(option, env = process.env) {
|
|
32
33
|
if (option !== undefined)
|
|
33
34
|
return asRuntime(option, 'The runtime option');
|
|
@@ -48,3 +49,10 @@ export class UnsupportedOnRuntimeError extends Error {
|
|
|
48
49
|
export function unsupportedOnNative(feature, where) {
|
|
49
50
|
return new UnsupportedOnRuntimeError(feature, 'native', where);
|
|
50
51
|
}
|
|
52
|
+
/** The runtime chooseRuntime picks, and where it came from (the doctor prints both). */
|
|
53
|
+
export function describeRuntime(option, env = process.env) {
|
|
54
|
+
if (option !== undefined)
|
|
55
|
+
return { runtime: asRuntime(option, 'The runtime option'), source: 'option' };
|
|
56
|
+
const set = runtimeSetting(env);
|
|
57
|
+
return set ? { runtime: set, source: 'MELCHIZEDEK_RUNTIME' } : { runtime: DEFAULT_RUNTIME, source: 'default' };
|
|
58
|
+
}
|
|
@@ -26,13 +26,15 @@
|
|
|
26
26
|
* time, and an outer AbortSignal cancels it. All three abort the provider
|
|
27
27
|
* request in flight, not just the loop around it.
|
|
28
28
|
*
|
|
29
|
-
* ── Runtimes (ADR 0045, ADR 0073)
|
|
30
|
-
* Each agent of a turn runs on
|
|
31
|
-
*
|
|
29
|
+
* ── Runtimes (ADR 0045, ADR 0073, ADR 0102) ───────────────────────────────
|
|
30
|
+
* Each agent of a turn runs on the engine's own agent loop (the default
|
|
31
|
+
* since 0.20.0) or on ADK's Runner: the turn's `runtime` option, else
|
|
32
32
|
* MELCHIZEDEK_RUNTIME. Both compile the same AgentSpec (lib/compile.ts),
|
|
33
33
|
* store the same events and drain through drainAgentStream, so everything
|
|
34
34
|
* else here is shared. What native does not run yet fails before any model
|
|
35
|
-
* call, naming the feature (lib/runtime/nativeTurn.ts).
|
|
35
|
+
* call, naming the feature (lib/runtime/nativeTurn.ts). The adk runtime
|
|
36
|
+
* needs @google/adk installed; without it, a turn asked to run there fails
|
|
37
|
+
* before the session is touched, naming the package (lib/adkPeer.ts).
|
|
36
38
|
*/
|
|
37
39
|
import type { BasePlugin } from '@google/adk';
|
|
38
40
|
import type { BaseMemoryService, BaseSessionService, Event, LlmAgent } from '@google/adk';
|
|
@@ -42,8 +44,11 @@ import type { PendingConsent } from './credentials.ts';
|
|
|
42
44
|
export { CREDENTIAL_REQUEST, credentialResponsePart, describeConsent, pendingConsent } from './credentials.ts';
|
|
43
45
|
export type { PendingConsent } from './credentials.ts';
|
|
44
46
|
export type { ToolCredentials } from '../tools/oauthConsent.ts';
|
|
45
|
-
export { chooseRuntime, runtimeSetting, DEFAULT_RUNTIME, RUNTIMES, UnsupportedOnRuntimeError } from './runtimeFlag.ts';
|
|
46
|
-
export type { RuntimeName } from './runtimeFlag.ts';
|
|
47
|
+
export { chooseRuntime, describeRuntime, runtimeSetting, DEFAULT_RUNTIME, RUNTIMES, UnsupportedOnRuntimeError } from './runtimeFlag.ts';
|
|
48
|
+
export type { RuntimeName, RuntimeSource } from './runtimeFlag.ts';
|
|
49
|
+
export { AdkNotInstalledError, adkInstalled } from '../adkPeer.ts';
|
|
50
|
+
export { InProcessSessionService } from './sessions.ts';
|
|
51
|
+
export { asAdkSessionService, asSessionService } from './adkSessionBridge.ts';
|
|
47
52
|
import type { PendingInput } from '../workflow.ts';
|
|
48
53
|
export { describeInput } from '../workflow.ts';
|
|
49
54
|
export type { PendingInput } from '../workflow.ts';
|
|
@@ -112,10 +117,10 @@ export interface SyndicateTurnOptions {
|
|
|
112
117
|
/** Applied to every compiled agent before it runs (eval tool replay). ADK runtime only. */
|
|
113
118
|
transformAgent?: (agent: LlmAgent) => LlmAgent;
|
|
114
119
|
/**
|
|
115
|
-
* The runtime that runs the turn's agents: `
|
|
116
|
-
* `
|
|
117
|
-
* `
|
|
118
|
-
* before any model call.
|
|
120
|
+
* The runtime that runs the turn's agents: `native` (the engine's own
|
|
121
|
+
* loop) or `adk` (Google ADK's Runner, which needs @google/adk installed).
|
|
122
|
+
* Default: MELCHIZEDEK_RUNTIME, else `native`. A feature native does not
|
|
123
|
+
* run yet throws UnsupportedOnRuntimeError before any model call.
|
|
119
124
|
*/
|
|
120
125
|
runtime?: RuntimeName;
|
|
121
126
|
/** Plan-dispatch only: skip the classifier and run this route. Overrides
|
|
@@ -26,30 +26,36 @@
|
|
|
26
26
|
* time, and an outer AbortSignal cancels it. All three abort the provider
|
|
27
27
|
* request in flight, not just the loop around it.
|
|
28
28
|
*
|
|
29
|
-
* ── Runtimes (ADR 0045, ADR 0073)
|
|
30
|
-
* Each agent of a turn runs on
|
|
31
|
-
*
|
|
29
|
+
* ── Runtimes (ADR 0045, ADR 0073, ADR 0102) ───────────────────────────────
|
|
30
|
+
* Each agent of a turn runs on the engine's own agent loop (the default
|
|
31
|
+
* since 0.20.0) or on ADK's Runner: the turn's `runtime` option, else
|
|
32
32
|
* MELCHIZEDEK_RUNTIME. Both compile the same AgentSpec (lib/compile.ts),
|
|
33
33
|
* store the same events and drain through drainAgentStream, so everything
|
|
34
34
|
* else here is shared. What native does not run yet fails before any model
|
|
35
|
-
* call, naming the feature (lib/runtime/nativeTurn.ts).
|
|
35
|
+
* call, naming the feature (lib/runtime/nativeTurn.ts). The adk runtime
|
|
36
|
+
* needs @google/adk installed; without it, a turn asked to run there fails
|
|
37
|
+
* before the session is touched, naming the package (lib/adkPeer.ts).
|
|
36
38
|
*/
|
|
37
|
-
import { InMemorySessionService, ReflectAndRetryModelPlugin, ReflectAndRetryToolPlugin, Runner, StreamingMode, getFunctionCalls, getFunctionResponses } from '@google/adk';
|
|
38
39
|
import { DEFAULT_MAX_STEPS } from '../config.js';
|
|
39
|
-
import { agentGates, compileGraph, compileSpec, compileSubagent, compileSubagentSpec } from '../compile.js';
|
|
40
|
-
import { compileNative, nativeAdapterFor } from '../compileNative.js';
|
|
40
|
+
import { agentGates, compileGraph, compileSpec, compileSubagent, compileSubagentSpec, compileWorkflowSpec, declaresApprovals } from '../compile.js';
|
|
41
|
+
import { compileNative, compileNativeWorkflow, nativeAdapterFor } from '../compileNative.js';
|
|
41
42
|
import { nativeMemory, refuseOnNative, runNativeAgent } from './nativeTurn.js';
|
|
42
|
-
import { asSessionService } from './adkSessionBridge.js';
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
43
|
+
import { asAdkSessionService, asSessionService } from './adkSessionBridge.js';
|
|
44
|
+
import { requireAdk } from '../adkPeer.js';
|
|
45
|
+
import { getFunctionCalls, getFunctionResponses } from './events.js';
|
|
46
|
+
import { InProcessSessionService } from './sessions.js';
|
|
45
47
|
import { UnsupportedWorkflowResumeError } from '../workflow/resume.js';
|
|
46
|
-
import {
|
|
48
|
+
import { runNativeWorkflow } from '../workflow/turn.js';
|
|
47
49
|
import { SelfCorrection } from './native/selfCorrection.js';
|
|
48
50
|
import { UnsupportedOnRuntimeError, chooseRuntime } from './runtimeFlag.js';
|
|
49
51
|
import { pinnedCredentialStore } from '../tools/auth.js';
|
|
50
52
|
import { credentialResponsePart, describeConsent, pendingConsent } from './credentials.js';
|
|
51
53
|
export { CREDENTIAL_REQUEST, credentialResponsePart, describeConsent, pendingConsent } from './credentials.js';
|
|
52
|
-
export { chooseRuntime, runtimeSetting, DEFAULT_RUNTIME, RUNTIMES, UnsupportedOnRuntimeError } from './runtimeFlag.js';
|
|
54
|
+
export { chooseRuntime, describeRuntime, runtimeSetting, DEFAULT_RUNTIME, RUNTIMES, UnsupportedOnRuntimeError } from './runtimeFlag.js';
|
|
55
|
+
export { AdkNotInstalledError, adkInstalled } from '../adkPeer.js';
|
|
56
|
+
// A turn's session store with no ADK: runSyndicateTurn takes it as asAdkSessionService(new InProcessSessionService()) (ADR 0102).
|
|
57
|
+
export { InProcessSessionService } from './sessions.js';
|
|
58
|
+
export { asAdkSessionService, asSessionService } from './adkSessionBridge.js';
|
|
53
59
|
import { ROUTE_STEP_SUFFIX, compileWorkflow, describeInput, inputRequestFrom, isWorkflowSyndicate } from '../workflow.js';
|
|
54
60
|
export { describeInput } from '../workflow.js';
|
|
55
61
|
import { isDispatchSyndicate, matchRouteOverride, resolveRoute } from '../dispatch.js';
|
|
@@ -85,6 +91,9 @@ export function retryPlugins(retries) {
|
|
|
85
91
|
const model = retries?.model_errors ?? DEFAULT_MODEL_ERROR_RETRIES;
|
|
86
92
|
const tool = retries?.tool_errors ?? DEFAULT_TOOL_ERROR_RETRIES;
|
|
87
93
|
const plugins = [];
|
|
94
|
+
if (model <= 0 && tool <= 0)
|
|
95
|
+
return plugins;
|
|
96
|
+
const { ReflectAndRetryModelPlugin, ReflectAndRetryToolPlugin } = requireAdk("ADK's retry plugins (retryPlugins)");
|
|
88
97
|
if (model > 0)
|
|
89
98
|
plugins.push(new ReflectAndRetryModelPlugin({ maxRetries: model }));
|
|
90
99
|
if (tool > 0)
|
|
@@ -173,7 +182,7 @@ export async function drainAgentStream(stream, opts = {}) {
|
|
|
173
182
|
d.tokens.output += e.usageMetadata.candidatesTokenCount ?? 0;
|
|
174
183
|
d.tokens.thinking += e.usageMetadata.thoughtsTokenCount ?? 0;
|
|
175
184
|
}
|
|
176
|
-
const calls = getFunctionCalls(event)
|
|
185
|
+
const calls = getFunctionCalls(event);
|
|
177
186
|
// Text streamed before a tool call was narration, not the answer.
|
|
178
187
|
if (streamed && calls.length) {
|
|
179
188
|
ev.onTextReset?.();
|
|
@@ -207,7 +216,7 @@ export async function drainAgentStream(stream, opts = {}) {
|
|
|
207
216
|
if (opts.publishToolStatus)
|
|
208
217
|
ev.onProgress?.(`Invoking tool: ${name}`);
|
|
209
218
|
}
|
|
210
|
-
for (const resp of getFunctionResponses(event)
|
|
219
|
+
for (const resp of getFunctionResponses(event)) {
|
|
211
220
|
const r = resp;
|
|
212
221
|
const name = r.name ?? r.functionResponse?.name ?? '';
|
|
213
222
|
const content = r.response ?? r.functionResponse?.response ?? {};
|
|
@@ -286,6 +295,9 @@ export async function runSyndicateTurn(opts) {
|
|
|
286
295
|
// class, which bypasses traceLlmGeneration: no max_steps, no cancel. A
|
|
287
296
|
// caller that resolves models itself (the A2A server) is left alone.
|
|
288
297
|
const runtime = chooseRuntime(opts.runtime);
|
|
298
|
+
// The adk runtime without @google/adk installed fails here, naming the package (ADR 0102).
|
|
299
|
+
if (runtime === 'adk')
|
|
300
|
+
requireAdk("The adk runtime (MELCHIZEDEK_RUNTIME=adk, or the turn's runtime option)");
|
|
289
301
|
if (!opts.compile?.resolveModel)
|
|
290
302
|
registerAvailableProviders();
|
|
291
303
|
const control = createTurnControl({
|
|
@@ -303,23 +315,15 @@ export async function runSyndicateTurn(opts) {
|
|
|
303
315
|
/**
|
|
304
316
|
* A workflow syndicate for the native runtime: its graph, every agent
|
|
305
317
|
* compiled for native from the same specs ADK's compileWorkflow builds its
|
|
306
|
-
* agents from,
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
* call, with ADK's message.
|
|
318
|
+
* agents from (compileWorkflowSpec, then compileNativeWorkflow), one adapter
|
|
319
|
+
* lookup that knows every agent's models, and the registry's lookup for tool
|
|
320
|
+
* nodes. A tool node ADK's compile refuses (an unregistered or long-running
|
|
321
|
+
* tool) is refused here, before any model call, with ADK's message.
|
|
310
322
|
*/
|
|
311
|
-
async function
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
for (const sub of [{ description: '', ...config.orchestrator }, ...(config.subagents ?? [])]) {
|
|
316
|
-
const spec = await compileSubagentSpec(sub, opts);
|
|
317
|
-
specs.push(spec);
|
|
318
|
-
agents.set(sub.name, compileNative(spec));
|
|
319
|
-
}
|
|
320
|
-
const resolveTool = (name) => resolveTools([name], opts.onUnknownTool)[0];
|
|
321
|
-
refuseUnrunnableNodes(graph, resolveTool);
|
|
322
|
-
return { runtime: 'native-workflow', graph, agents, adapterFor: nativeAdapterFor(opts, specs), resolveTool };
|
|
323
|
+
async function compileNativeWorkflowAgent(config, opts) {
|
|
324
|
+
const spec = await compileWorkflowSpec(config, opts);
|
|
325
|
+
const { graph, agents, resolveTool } = compileNativeWorkflow(spec);
|
|
326
|
+
return { runtime: 'native-workflow', graph, agents, adapterFor: nativeAdapterFor(opts, spec.agents.map((a) => a.spec)), resolveTool };
|
|
323
327
|
}
|
|
324
328
|
async function runTurnInner(opts, control, runtime) {
|
|
325
329
|
const { config, appName, userId, sessionId, sessionService } = opts;
|
|
@@ -336,6 +340,11 @@ async function runTurnInner(opts, control, runtime) {
|
|
|
336
340
|
// What native does not run yet fails here, before the session is touched.
|
|
337
341
|
if (native)
|
|
338
342
|
refuseOnNative(config, { isWorkflow: isWorkflowSyndicate(config), transformAgent: opts.transformAgent });
|
|
343
|
+
// ADK pauses a gated call in a workflow node, but its resume reruns the node from its input, so the
|
|
344
|
+
// pinned call never runs: approvals on workflow nodes run on native only (ADR 0098).
|
|
345
|
+
if (!native && isWorkflowSyndicate(config) && declaresApprovals(config)) {
|
|
346
|
+
throw new UnsupportedOnRuntimeError('an approval gate (require_approval) on a workflow node', 'adk', config.syndicate_name || 'syndicate');
|
|
347
|
+
}
|
|
339
348
|
const nativeOf = (spec) => ({ runtime: 'native', agent: compileNative(spec), adapterFor: nativeAdapterFor(compileOpts, spec) });
|
|
340
349
|
/** The orchestrator (a DELEGATE root, or a dispatch classifier) for the turn's runtime. */
|
|
341
350
|
const compileRoot = async () => native ? nativeOf(await compileSpec(config, compileOpts)) : { runtime: 'adk', agent: transform(await compileGraph(config, compileOpts)) };
|
|
@@ -534,6 +543,7 @@ async function runTurnInner(opts, control, runtime) {
|
|
|
534
543
|
});
|
|
535
544
|
}
|
|
536
545
|
else {
|
|
546
|
+
const { Runner, StreamingMode } = requireAdk('The adk runtime');
|
|
537
547
|
const runner = new Runner({
|
|
538
548
|
agent: params.agent.agent,
|
|
539
549
|
appName,
|
|
@@ -624,7 +634,10 @@ async function runTurnInner(opts, control, runtime) {
|
|
|
624
634
|
// conversation the next specialist reads.
|
|
625
635
|
const routerAgent = await compileRoot();
|
|
626
636
|
const routerSid = `${sessionId}::route`;
|
|
627
|
-
|
|
637
|
+
// ADK's in-memory store on adk; the engine's on native, which needs no ADK.
|
|
638
|
+
const routerSessions = native
|
|
639
|
+
? asAdkSessionService(new InProcessSessionService())
|
|
640
|
+
: new (requireAdk('The adk runtime').InMemorySessionService)();
|
|
628
641
|
await routerSessions.createSession({ appName, userId, sessionId: routerSid });
|
|
629
642
|
const shared = await sessionService.getSession({ appName, userId, sessionId });
|
|
630
643
|
const digest = renderTranscriptDigest(shared?.events ?? []);
|
|
@@ -726,8 +739,15 @@ async function runTurnInner(opts, control, runtime) {
|
|
|
726
739
|
// unless its YAML says otherwise, so no projection is needed. An
|
|
727
740
|
// `ask_user` node ends the turn input-required; the next message
|
|
728
741
|
// resumes the graph where it waited.
|
|
742
|
+
// While a gated call waits, a message that is not its decision repeats the request: the walk cannot move past
|
|
743
|
+
// the waiting node, so nothing is stored and nothing runs, as the A2A server answers it (ADR 0098).
|
|
744
|
+
if (!decision && declaresApprovals(config)) {
|
|
745
|
+
const open = pendingApproval(existing?.events ?? []);
|
|
746
|
+
if (open)
|
|
747
|
+
return pause(open);
|
|
748
|
+
}
|
|
729
749
|
const workflowAgent = native
|
|
730
|
-
? await
|
|
750
|
+
? await compileNativeWorkflowAgent(config, compileOpts)
|
|
731
751
|
: { runtime: 'adk', agent: (await compileWorkflow(config, compileOpts, transform)).workflow };
|
|
732
752
|
try {
|
|
733
753
|
answer = await runAgent({
|
|
@@ -759,6 +779,13 @@ async function runTurnInner(opts, control, runtime) {
|
|
|
759
779
|
result.error = answer.error;
|
|
760
780
|
return finish();
|
|
761
781
|
}
|
|
782
|
+
// A gated call paused its node, and the walk with it (ADR 0098): the next message answers it.
|
|
783
|
+
if (declaresApprovals(config)) {
|
|
784
|
+
const after = await sessionService.getSession({ appName, userId, sessionId });
|
|
785
|
+
const pending = pendingApproval(after?.events ?? []);
|
|
786
|
+
if (pending)
|
|
787
|
+
return pause(pending);
|
|
788
|
+
}
|
|
762
789
|
if (answer.inputRequests.length) {
|
|
763
790
|
const input = answer.inputRequests[answer.inputRequests.length - 1];
|
|
764
791
|
result.status = 'input-required';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/runtime/valueDepth.ts — how deeply a JSON value nests, read without
|
|
3
|
+
* recursion (WS5-5, ADR 0101).
|
|
4
|
+
*
|
|
5
|
+
* WHY this file exists:
|
|
6
|
+
* A tool call's arguments come from a model, and a tool's result can come
|
|
7
|
+
* from a remote server (an MCP tool, a fetched API). Both are stored in the
|
|
8
|
+
* session and read again on every later step: cloned for the history,
|
|
9
|
+
* serialized for a durable store and a span. Those readers recurse, so a
|
|
10
|
+
* value nested a few thousand levels deep overflows the stack in each of
|
|
11
|
+
* them, on this turn and on every turn after it: the session stops
|
|
12
|
+
* working. No tool schema the engine knows nests anywhere near
|
|
13
|
+
* MAX_VALUE_DEPTH, so a value past it is malformed, and the runtime keeps
|
|
14
|
+
* a short note in its place (lib/models/genaiMapping.ts contractOutputPart for
|
|
15
|
+
* arguments, lib/runtime/native/agentLoop.ts for results).
|
|
16
|
+
*
|
|
17
|
+
* The check itself walks with an explicit stack, so the value it guards
|
|
18
|
+
* against cannot overflow it.
|
|
19
|
+
*/
|
|
20
|
+
/** The deepest a tool call's arguments or a tool's result may nest, in levels of objects and arrays (the value itself is level 1). */
|
|
21
|
+
export declare const MAX_VALUE_DEPTH = 64;
|
|
22
|
+
/** True when `value` nests objects or arrays more than `max` levels deep. Iterative; a value seen twice (a cycle) is walked once. */
|
|
23
|
+
export declare function nestedDeeperThan(value: unknown, max?: number): boolean;
|