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
|
@@ -36,7 +36,11 @@
|
|
|
36
36
|
* - a long-running tool (ask_user) that returns nothing answers
|
|
37
37
|
* nothing; its actions, when it set any, make an event of their own;
|
|
38
38
|
* - a result that is not an object is wrapped `{ result }`, an array
|
|
39
|
-
* `{ results }`, as ADK wraps them
|
|
39
|
+
* `{ results }`, as ADK wraps them;
|
|
40
|
+
* - a result nested deeper than MAX_VALUE_DEPTH levels answers
|
|
41
|
+
* `{ error: TOO_DEEP_RESULT }` instead: every later reader of the
|
|
42
|
+
* session would overflow its stack on it (lib/runtime/valueDepth.ts,
|
|
43
|
+
* ADR 0101). ADK keeps it.
|
|
40
44
|
* One call's response is stored as its own event; several are merged
|
|
41
45
|
* into one, parts in call order and actions merged (ADK's
|
|
42
46
|
* mergeParallelFunctionResponseEvents). ADK runs the calls one after
|
|
@@ -115,10 +119,11 @@ import { z } from 'zod';
|
|
|
115
119
|
import { ToolCredentialError, toolAccessToken } from '../../tools/auth.js';
|
|
116
120
|
import { APPROVAL_TEXTS, CONSENT_TEXTS, isTool } from '../../tools/tool.js';
|
|
117
121
|
import { createEventActions, createTurnEvent, getFunctionCalls, getFunctionResponses, isFinal } from '../events.js';
|
|
122
|
+
import { MAX_VALUE_DEPTH, nestedDeeperThan } from '../valueDepth.js';
|
|
118
123
|
import { currentTurnSignal } from '../turnControl.js';
|
|
119
124
|
import { compactBeforeStep } from './compaction.js';
|
|
120
125
|
import { ADK_CALL_ID_PREFIX } from './history.js';
|
|
121
|
-
import { runSubagent, subagentOf } from './delegate.js';
|
|
126
|
+
import { runSubagent, runWorkflowSubagent, subagentOf, workflowSubagentOf } from './delegate.js';
|
|
122
127
|
import { approvedCalls, grantedCalls } from './interrupts.js';
|
|
123
128
|
import { SelfCorrection } from './selfCorrection.js';
|
|
124
129
|
import { eitherSignal, runModelStep, stopOf } from './step.js';
|
|
@@ -145,19 +150,23 @@ function isDefaultActions(a) {
|
|
|
145
150
|
a.transferToAgent === undefined &&
|
|
146
151
|
a.escalate === undefined);
|
|
147
152
|
}
|
|
153
|
+
/** Copies `from`'s own keys onto `to` as own keys: Object.assign would re-parent `to` for a key `__proto__` (a model-chosen call id). */
|
|
154
|
+
function assignOwn(to, from) {
|
|
155
|
+
for (const [k, v] of Object.entries(from))
|
|
156
|
+
setOwn(to, k, v);
|
|
157
|
+
}
|
|
148
158
|
/** ADK's mergeEventActions: dictionaries merged in order, the later flag winning. */
|
|
149
159
|
function mergeActions(sources) {
|
|
150
160
|
const out = createEventActions();
|
|
151
161
|
for (const s of sources) {
|
|
152
162
|
if (s.stateDelta)
|
|
153
|
-
|
|
154
|
-
setOwn(out.stateDelta, k, v);
|
|
163
|
+
assignOwn(out.stateDelta, s.stateDelta);
|
|
155
164
|
if (s.artifactDelta)
|
|
156
|
-
|
|
165
|
+
assignOwn(out.artifactDelta, s.artifactDelta);
|
|
157
166
|
if (s.requestedAuthConfigs)
|
|
158
|
-
|
|
167
|
+
assignOwn(out.requestedAuthConfigs, s.requestedAuthConfigs);
|
|
159
168
|
if (s.requestedToolConfirmations)
|
|
160
|
-
|
|
169
|
+
assignOwn(out.requestedToolConfirmations, s.requestedToolConfirmations);
|
|
161
170
|
if (s.skipSummarization !== undefined)
|
|
162
171
|
out.skipSummarization = s.skipSummarization;
|
|
163
172
|
if (s.transferToAgent !== undefined)
|
|
@@ -235,7 +244,8 @@ function callContext(scope, functionCallId, confirmation) {
|
|
|
235
244
|
const requestCredential = consent && functionCallId
|
|
236
245
|
? async (provider) => {
|
|
237
246
|
const request = await consent.begin({ appName: session.appName, userId: session.userId, sessionId: session.id, functionCallId, provider });
|
|
238
|
-
|
|
247
|
+
// An own key, as every delta: a call id such as `__proto__` names a request, never a prototype.
|
|
248
|
+
setOwn(actions.requestedAuthConfigs, functionCallId, request.authConfig);
|
|
239
249
|
}
|
|
240
250
|
: undefined;
|
|
241
251
|
const credentials = ctx.credentials;
|
|
@@ -270,7 +280,7 @@ function callContext(scope, functionCallId, confirmation) {
|
|
|
270
280
|
requestConfirmation: ({ hint, payload } = {}) => {
|
|
271
281
|
if (!functionCallId)
|
|
272
282
|
throw new Error('functionCallId is not set.');
|
|
273
|
-
actions.requestedToolConfirmations
|
|
283
|
+
setOwn(actions.requestedToolConfirmations, functionCallId, { hint: hint ?? '', confirmed: false, payload });
|
|
274
284
|
},
|
|
275
285
|
// The person's answer, when this call is the pinned call an approval resumes (interrupts.ts).
|
|
276
286
|
confirmation,
|
|
@@ -292,6 +302,8 @@ function callContext(scope, functionCallId, confirmation) {
|
|
|
292
302
|
},
|
|
293
303
|
};
|
|
294
304
|
}
|
|
305
|
+
/** What a tool's result becomes when it nests past MAX_VALUE_DEPTH (ADR 0101). */
|
|
306
|
+
export const TOO_DEEP_RESULT = (toolName) => `The result of tool '${toolName}' nested deeper than ${MAX_VALUE_DEPTH} levels and was not kept.`;
|
|
295
307
|
/** ADK's normalization of a tool's result into a function response. */
|
|
296
308
|
function asResponse(value) {
|
|
297
309
|
if (typeof value !== 'object' || value === null)
|
|
@@ -339,14 +351,18 @@ async function runCall(scope, call, tools, correction, confirmation) {
|
|
|
339
351
|
const args = call.args ?? {};
|
|
340
352
|
const toolName = tool.name;
|
|
341
353
|
const longRunning = isTool(tool) ? tool.longRunning === true : tool.isLongRunning === true;
|
|
342
|
-
// Delegation (WS2-6): a subagent runs as its own child loop, before the generic path (delegate.ts).
|
|
354
|
+
// Delegation (WS2-6): a subagent runs as its own child loop, a nested workflow as its own walk (ADR 0098), before the generic path (delegate.ts).
|
|
343
355
|
const subagent = subagentOf(tool);
|
|
356
|
+
const workflow = subagent ? undefined : workflowSubagentOf(tool);
|
|
357
|
+
const delegation = { ctx: scope.ctx, stateBase: scope.stateBase, signal: scope.signal, runLoop: runAgentLoop, queue: scope };
|
|
344
358
|
let response;
|
|
345
359
|
let failure;
|
|
346
360
|
try {
|
|
347
361
|
response = subagent
|
|
348
|
-
? await runSubagent(subagent, args, context,
|
|
349
|
-
:
|
|
362
|
+
? await runSubagent(subagent, args, context, delegation)
|
|
363
|
+
: workflow
|
|
364
|
+
? await runWorkflowSubagent(workflow, args, context, delegation)
|
|
365
|
+
: isTool(tool) ? await runOwnTool(tool, args, context) : await tool.runAsync({ args, toolContext: context });
|
|
350
366
|
}
|
|
351
367
|
catch (e) {
|
|
352
368
|
failure = e instanceof Error ? e.message : e;
|
|
@@ -368,7 +384,9 @@ async function runCall(scope, call, tools, correction, confirmation) {
|
|
|
368
384
|
return isDefaultActions(context.actions) ? undefined : { actions: context.actions };
|
|
369
385
|
}
|
|
370
386
|
const answer = failure ? { error: failure } : response === null || response === undefined ? { result: response } : asResponse(response);
|
|
371
|
-
|
|
387
|
+
// A result nested past MAX_VALUE_DEPTH would overflow every later reader of the session (lib/runtime/valueDepth.ts, ADR 0101): a note stands in for it.
|
|
388
|
+
const kept = nestedDeeperThan(answer) ? { error: TOO_DEEP_RESULT(toolName) } : answer;
|
|
389
|
+
return { part: { functionResponse: { id: context.functionCallId, name: toolName, response: kept } }, actions: context.actions };
|
|
372
390
|
}
|
|
373
391
|
/**
|
|
374
392
|
* The calls run, in parallel, as the one response event ADK stores for them
|
|
@@ -47,6 +47,17 @@
|
|
|
47
47
|
* here like any subagent, and its own delegations open sessions under their
|
|
48
48
|
* own names, as on ADK.
|
|
49
49
|
*
|
|
50
|
+
* A NESTED WORKFLOW (ADR 0098): a `yaml_reference` to a workflow syndicate
|
|
51
|
+
* is a workflowSubagentTool, holding the whole graph as a WorkflowSubagent.
|
|
52
|
+
* A call runs as steps 1 to 6 say, with the walk in place of the loop: the
|
|
53
|
+
* graph's walk (lib/workflow/turn.ts, runNativeWorkflow, which
|
|
54
|
+
* lib/compileNative.ts hands over) stores the message itself and yields the
|
|
55
|
+
* events ADK's Runner yields for a Workflow root, node input turns aside;
|
|
56
|
+
* the answer is the last of them's text, as ADK's AgentTool reads it from a
|
|
57
|
+
* Workflow (never parsed: a Workflow has no output schema). A node that
|
|
58
|
+
* gave up fails the call with its error. The walk is not imported here, so
|
|
59
|
+
* this module stays below lib/workflow/ in the import graph.
|
|
60
|
+
*
|
|
50
61
|
* The DELEGATE relay fallback (an orchestrator that echoes a tool name or
|
|
51
62
|
* says nothing) stays in lib/runtime/syndicateTurn.ts: it reads the drained
|
|
52
63
|
* run, whichever runtime produced it.
|
|
@@ -54,12 +65,17 @@
|
|
|
54
65
|
* ADK stays out of this file: an ADK AgentTool is recognized by ADK's global
|
|
55
66
|
* symbol only to refuse it, with a message naming what to list instead.
|
|
56
67
|
*/
|
|
68
|
+
import type { ModelAdapter } from '../../models/contract.ts';
|
|
57
69
|
import type { Tool, ToolContext } from '../../tools/tool.ts';
|
|
58
70
|
import type { TurnEvent } from '../events.ts';
|
|
71
|
+
import type { SessionService } from '../sessions.ts';
|
|
59
72
|
import type { AgentLoopContext, AgentLoopEnd } from './agentLoop.ts';
|
|
60
73
|
import type { NativeAgent } from './request.ts';
|
|
74
|
+
import { SelfCorrection } from './selfCorrection.ts';
|
|
61
75
|
/** Where a subagent tool keeps the agent it runs. A global symbol, so a second copy of this module still finds it. */
|
|
62
76
|
export declare const SUBAGENT: unique symbol;
|
|
77
|
+
/** Where a workflow subagent tool keeps the graph it runs. */
|
|
78
|
+
export declare const WORKFLOW_SUBAGENT: unique symbol;
|
|
63
79
|
/** A subagent listed on its caller as a tool: the declaration ADK's AgentTool gives it, and the agent the call runs. */
|
|
64
80
|
export interface SubagentTool extends Tool {
|
|
65
81
|
readonly [SUBAGENT]: NativeAgent;
|
|
@@ -70,6 +86,36 @@ export interface SubagentTool extends Tool {
|
|
|
70
86
|
* one string `request`. Only the native loop runs it.
|
|
71
87
|
*/
|
|
72
88
|
export declare function subagentTool(agent: NativeAgent): SubagentTool;
|
|
89
|
+
/** One call's walk of a nested workflow, on the child session (WorkflowSubagent.walk). */
|
|
90
|
+
export interface WorkflowSubagentRun {
|
|
91
|
+
sessions: SessionService;
|
|
92
|
+
appName: string;
|
|
93
|
+
userId: string;
|
|
94
|
+
sessionId: string;
|
|
95
|
+
/** The message's parts: the call's request as one text part. */
|
|
96
|
+
userParts: unknown[];
|
|
97
|
+
signal?: AbortSignal;
|
|
98
|
+
adapterFor: (model: string) => ModelAdapter;
|
|
99
|
+
memory?: AgentLoopContext['memory'];
|
|
100
|
+
log?: (message: string) => void;
|
|
101
|
+
selfCorrection: SelfCorrection;
|
|
102
|
+
credentials?: AgentLoopContext['credentials'];
|
|
103
|
+
}
|
|
104
|
+
/** A workflow syndicate delegated to as a subagent (ADR 0098): its name and description, and the walk one call runs. */
|
|
105
|
+
export interface WorkflowSubagent {
|
|
106
|
+
name: string;
|
|
107
|
+
description?: string;
|
|
108
|
+
/** Stores the message on the child session and walks the whole graph, yielding each event ADK's Runner yields. Throws what a node gave up with. */
|
|
109
|
+
walk(run: WorkflowSubagentRun): AsyncGenerator<TurnEvent, unknown>;
|
|
110
|
+
}
|
|
111
|
+
/** A nested workflow listed on its caller as a tool: the declaration ADK's AgentTool gives a Workflow, and the graph the call walks. */
|
|
112
|
+
export interface WorkflowSubagentTool extends Tool {
|
|
113
|
+
readonly [WORKFLOW_SUBAGENT]: WorkflowSubagent;
|
|
114
|
+
}
|
|
115
|
+
/** `workflow` as a tool its caller lists, as lib/compileAdk.ts lists a nested Workflow as an AgentTool: one string `request`. Only the native loop runs it. */
|
|
116
|
+
export declare function workflowSubagentTool(workflow: WorkflowSubagent): WorkflowSubagentTool;
|
|
117
|
+
/** The nested workflow a delegated call walks: the one a workflow subagent tool holds, else undefined. */
|
|
118
|
+
export declare function workflowSubagentOf(tool: unknown): WorkflowSubagent | undefined;
|
|
73
119
|
/**
|
|
74
120
|
* The agent a delegated call runs: the one a subagent tool (or anything
|
|
75
121
|
* carrying SUBAGENT) holds, else undefined. Throws for an ADK AgentTool that
|
|
@@ -96,3 +142,10 @@ export interface DelegationScope {
|
|
|
96
142
|
* throws, and for an output-schema answer that does not parse.
|
|
97
143
|
*/
|
|
98
144
|
export declare function runSubagent(agent: NativeAgent, args: Record<string, unknown>, context: ToolContext, scope: DelegationScope): Promise<unknown>;
|
|
145
|
+
/**
|
|
146
|
+
* Runs one delegated call to a nested workflow (ADR 0098): the whole graph
|
|
147
|
+
* walked on the child session with the call's `request` as its message,
|
|
148
|
+
* the walk's state writes recorded in `context`. Resolves to the last
|
|
149
|
+
* yielded event's text, '' for none. Throws what the walk throws.
|
|
150
|
+
*/
|
|
151
|
+
export declare function runWorkflowSubagent(workflow: WorkflowSubagent, args: Record<string, unknown>, context: ToolContext, scope: DelegationScope): Promise<unknown>;
|
|
@@ -47,6 +47,17 @@
|
|
|
47
47
|
* here like any subagent, and its own delegations open sessions under their
|
|
48
48
|
* own names, as on ADK.
|
|
49
49
|
*
|
|
50
|
+
* A NESTED WORKFLOW (ADR 0098): a `yaml_reference` to a workflow syndicate
|
|
51
|
+
* is a workflowSubagentTool, holding the whole graph as a WorkflowSubagent.
|
|
52
|
+
* A call runs as steps 1 to 6 say, with the walk in place of the loop: the
|
|
53
|
+
* graph's walk (lib/workflow/turn.ts, runNativeWorkflow, which
|
|
54
|
+
* lib/compileNative.ts hands over) stores the message itself and yields the
|
|
55
|
+
* events ADK's Runner yields for a Workflow root, node input turns aside;
|
|
56
|
+
* the answer is the last of them's text, as ADK's AgentTool reads it from a
|
|
57
|
+
* Workflow (never parsed: a Workflow has no output schema). A node that
|
|
58
|
+
* gave up fails the call with its error. The walk is not imported here, so
|
|
59
|
+
* this module stays below lib/workflow/ in the import graph.
|
|
60
|
+
*
|
|
50
61
|
* The DELEGATE relay fallback (an orchestrator that echoes a tool name or
|
|
51
62
|
* says nothing) stays in lib/runtime/syndicateTurn.ts: it reads the drained
|
|
52
63
|
* run, whichever runtime produced it.
|
|
@@ -55,6 +66,7 @@
|
|
|
55
66
|
* symbol only to refuse it, with a message naming what to list instead.
|
|
56
67
|
*/
|
|
57
68
|
import { randomUUID } from 'node:crypto';
|
|
69
|
+
import { resolveAdapter } from '../../models/registry.js';
|
|
58
70
|
import { createTurnEvent } from '../events.js';
|
|
59
71
|
import { TEMP_STATE_PREFIX } from '../sessions.js';
|
|
60
72
|
import { SelfCorrection } from './selfCorrection.js';
|
|
@@ -63,6 +75,8 @@ import { SelfCorrection } from './selfCorrection.js';
|
|
|
63
75
|
export const SUBAGENT = Symbol.for('melchizedek.subagent');
|
|
64
76
|
/** ADK's own mark on an AgentTool (agent_tool.js). */
|
|
65
77
|
const ADK_AGENT_TOOL = Symbol.for('google.adk.agentTool');
|
|
78
|
+
/** Where a workflow subagent tool keeps the graph it runs. */
|
|
79
|
+
export const WORKFLOW_SUBAGENT = Symbol.for('melchizedek.workflowSubagent');
|
|
66
80
|
/**
|
|
67
81
|
* `agent` as a tool its caller lists, as lib/compile.ts lists a subagent as
|
|
68
82
|
* an AgentTool: named for the agent, described by its description, taking
|
|
@@ -82,6 +96,28 @@ export function subagentTool(agent) {
|
|
|
82
96
|
},
|
|
83
97
|
};
|
|
84
98
|
}
|
|
99
|
+
/** `workflow` as a tool its caller lists, as lib/compileAdk.ts lists a nested Workflow as an AgentTool: one string `request`. Only the native loop runs it. */
|
|
100
|
+
export function workflowSubagentTool(workflow) {
|
|
101
|
+
return {
|
|
102
|
+
name: workflow.name,
|
|
103
|
+
[WORKFLOW_SUBAGENT]: workflow,
|
|
104
|
+
declaration: () => ({
|
|
105
|
+
name: workflow.name,
|
|
106
|
+
description: workflow.description ?? '',
|
|
107
|
+
parameters: { type: 'object', properties: { request: { type: 'string' } }, required: ['request'] },
|
|
108
|
+
}),
|
|
109
|
+
execute: async () => {
|
|
110
|
+
throw new Error(`${workflow.name}: a workflow subagent tool runs only on the native loop (lib/runtime/native/agentLoop.ts)`);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** The nested workflow a delegated call walks: the one a workflow subagent tool holds, else undefined. */
|
|
115
|
+
export function workflowSubagentOf(tool) {
|
|
116
|
+
if (!tool || typeof tool !== 'object')
|
|
117
|
+
return undefined;
|
|
118
|
+
const workflow = tool[WORKFLOW_SUBAGENT];
|
|
119
|
+
return workflow && typeof workflow === 'object' ? workflow : undefined;
|
|
120
|
+
}
|
|
85
121
|
/**
|
|
86
122
|
* The agent a delegated call runs: the one a subagent tool (or anything
|
|
87
123
|
* carrying SUBAGENT) holds, else undefined. Throws for an ADK AgentTool that
|
|
@@ -107,7 +143,7 @@ function inTurn(key, run) {
|
|
|
107
143
|
queues.set(key, mine.then(() => undefined, () => undefined));
|
|
108
144
|
return mine;
|
|
109
145
|
}
|
|
110
|
-
/** ADK's AgentTool answer: the last event's non-thought text, joined by a newline. */
|
|
146
|
+
/** ADK's AgentTool answer: the last event's non-thought text, joined by a newline; parsed under an output schema. */
|
|
111
147
|
function answerOf(agent, last) {
|
|
112
148
|
const parts = last?.content?.parts;
|
|
113
149
|
if (!parts?.length)
|
|
@@ -119,6 +155,19 @@ function answerOf(agent, last) {
|
|
|
119
155
|
.join('\n');
|
|
120
156
|
return agent.outputSchema ? JSON.parse(text) : text;
|
|
121
157
|
}
|
|
158
|
+
/** The child's session, as ADK's AgentTool gets or creates it: under the subagent's name, from the caller's state the first time. */
|
|
159
|
+
async function childSession(name, context, scope) {
|
|
160
|
+
const { sessions, session: parent } = scope.ctx;
|
|
161
|
+
const key = { appName: name, userId: parent.userId, sessionId: parent.id };
|
|
162
|
+
return (await sessions.get(key)) ?? (await sessions.create({ ...key, state: { ...scope.stateBase, ...context.stateDelta } }));
|
|
163
|
+
}
|
|
164
|
+
/** Step 5: an event's state writes, `temp:` keys aside, into the call's state delta. */
|
|
165
|
+
function recordState(event, context) {
|
|
166
|
+
for (const [k, v] of Object.entries(event.actions?.stateDelta ?? {})) {
|
|
167
|
+
if (!k.startsWith(TEMP_STATE_PREFIX))
|
|
168
|
+
context.state.set(k, v);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
122
171
|
/**
|
|
123
172
|
* Runs one delegated call: `agent` on its own session with the call's
|
|
124
173
|
* `request` as its message, the child's state writes recorded in `context`.
|
|
@@ -131,10 +180,8 @@ export function runSubagent(agent, args, context, scope) {
|
|
|
131
180
|
async function runChild(agent, args, context, scope) {
|
|
132
181
|
const { ctx, signal } = scope;
|
|
133
182
|
const { sessions } = ctx;
|
|
134
|
-
const parent = ctx.session;
|
|
135
183
|
const content = { role: 'user', parts: [{ text: args.request }] };
|
|
136
|
-
const
|
|
137
|
-
const session = (await sessions.get(key)) ?? (await sessions.create({ ...key, state: { ...scope.stateBase, ...context.stateDelta } }));
|
|
184
|
+
const session = await childSession(agent.name, context, scope);
|
|
138
185
|
if (signal?.aborted)
|
|
139
186
|
return '';
|
|
140
187
|
const invocationId = `e-${randomUUID()}`;
|
|
@@ -163,11 +210,47 @@ async function runChild(agent, args, context, scope) {
|
|
|
163
210
|
// ADK's Runner stops yielding once the turn stopped: the answer is the last event it yielded.
|
|
164
211
|
if (signal?.aborted)
|
|
165
212
|
break;
|
|
166
|
-
|
|
167
|
-
if (!k.startsWith(TEMP_STATE_PREFIX))
|
|
168
|
-
context.state.set(k, v);
|
|
169
|
-
}
|
|
213
|
+
recordState(event, context);
|
|
170
214
|
last = event;
|
|
171
215
|
}
|
|
172
216
|
return answerOf(agent, last);
|
|
173
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Runs one delegated call to a nested workflow (ADR 0098): the whole graph
|
|
220
|
+
* walked on the child session with the call's `request` as its message,
|
|
221
|
+
* the walk's state writes recorded in `context`. Resolves to the last
|
|
222
|
+
* yielded event's text, '' for none. Throws what the walk throws.
|
|
223
|
+
*/
|
|
224
|
+
export function runWorkflowSubagent(workflow, args, context, scope) {
|
|
225
|
+
return inTurn(scope.queue, () => walkChild(workflow, args, context, scope));
|
|
226
|
+
}
|
|
227
|
+
async function walkChild(workflow, args, context, scope) {
|
|
228
|
+
const { ctx, signal } = scope;
|
|
229
|
+
const session = await childSession(workflow.name, context, scope);
|
|
230
|
+
if (signal?.aborted)
|
|
231
|
+
return '';
|
|
232
|
+
const walk = workflow.walk({
|
|
233
|
+
sessions: ctx.sessions,
|
|
234
|
+
appName: session.appName,
|
|
235
|
+
userId: session.userId,
|
|
236
|
+
sessionId: session.id,
|
|
237
|
+
userParts: [{ text: args.request }],
|
|
238
|
+
adapterFor: ctx.adapterFor ?? resolveAdapter,
|
|
239
|
+
// As runChild: ADK's AgentTool runs its sub-runner without the reflect-and-retry plugins (ADR 0075).
|
|
240
|
+
selfCorrection: new SelfCorrection({ model_errors: 0, tool_errors: 0 }),
|
|
241
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
242
|
+
...(ctx.memory ? { memory: ctx.memory } : {}),
|
|
243
|
+
...(ctx.log ? { log: ctx.log } : {}),
|
|
244
|
+
...(ctx.credentials ? { credentials: ctx.credentials } : {}),
|
|
245
|
+
});
|
|
246
|
+
let last;
|
|
247
|
+
for await (const event of walk) {
|
|
248
|
+
if (signal?.aborted)
|
|
249
|
+
break;
|
|
250
|
+
if (event.partial)
|
|
251
|
+
continue;
|
|
252
|
+
recordState(event, context);
|
|
253
|
+
last = event;
|
|
254
|
+
}
|
|
255
|
+
return answerOf({}, last);
|
|
256
|
+
}
|
|
@@ -62,7 +62,9 @@
|
|
|
62
62
|
* nothing happens.
|
|
63
63
|
* 2. The requests they answer: `adk_request_credential` calls this agent
|
|
64
64
|
* made, by id. An answer naming no such request is ignored, as ADK
|
|
65
|
-
* ignores it.
|
|
65
|
+
* ignores it. So is an answer to a request an earlier grant already
|
|
66
|
+
* bound (step 3): a replayed grant runs nothing, where ADK would run
|
|
67
|
+
* the paused call again (WS5-5).
|
|
66
68
|
* 3. The binding. ADK's answer carries the authorization response (a code
|
|
67
69
|
* or the redirect URL), which ADK exchanges in-process with the client
|
|
68
70
|
* secret its request event stored. Here the server's callback route has
|
|
@@ -71,8 +73,9 @@
|
|
|
71
73
|
* `{ credentialKey, granted: true }`, bound to the request by its
|
|
72
74
|
* credentialKey. An answer that does not bind is ignored.
|
|
73
75
|
* 4. The paused calls the bound requests name (`function_call_id`, ADK's
|
|
74
|
-
* toolset requests aside) run again, from the latest event
|
|
75
|
-
* them
|
|
76
|
+
* toolset requests aside) run again, from the latest event this agent
|
|
77
|
+
* authored that made them (ADK reads any author's; a call forged into
|
|
78
|
+
* a user event never runs), through the loop's own call path. The tool now reads its grant
|
|
76
79
|
* through ctx.accessToken. The response is stored before the step
|
|
77
80
|
* builds its request. A later step finds the agent's own events last,
|
|
78
81
|
* so the call runs once.
|
|
@@ -62,7 +62,9 @@
|
|
|
62
62
|
* nothing happens.
|
|
63
63
|
* 2. The requests they answer: `adk_request_credential` calls this agent
|
|
64
64
|
* made, by id. An answer naming no such request is ignored, as ADK
|
|
65
|
-
* ignores it.
|
|
65
|
+
* ignores it. So is an answer to a request an earlier grant already
|
|
66
|
+
* bound (step 3): a replayed grant runs nothing, where ADK would run
|
|
67
|
+
* the paused call again (WS5-5).
|
|
66
68
|
* 3. The binding. ADK's answer carries the authorization response (a code
|
|
67
69
|
* or the redirect URL), which ADK exchanges in-process with the client
|
|
68
70
|
* secret its request event stored. Here the server's callback route has
|
|
@@ -71,8 +73,9 @@
|
|
|
71
73
|
* `{ credentialKey, granted: true }`, bound to the request by its
|
|
72
74
|
* credentialKey. An answer that does not bind is ignored.
|
|
73
75
|
* 4. The paused calls the bound requests name (`function_call_id`, ADK's
|
|
74
|
-
* toolset requests aside) run again, from the latest event
|
|
75
|
-
* them
|
|
76
|
+
* toolset requests aside) run again, from the latest event this agent
|
|
77
|
+
* authored that made them (ADK reads any author's; a call forged into
|
|
78
|
+
* a user event never runs), through the loop's own call path. The tool now reads its grant
|
|
76
79
|
* through ctx.accessToken. The response is stored before the step
|
|
77
80
|
* builds its request. A later step finds the agent's own events last,
|
|
78
81
|
* so the call runs once.
|
|
@@ -256,6 +259,16 @@ export async function grantedCalls(agent, scope) {
|
|
|
256
259
|
answers.set(r.id, r.response);
|
|
257
260
|
if (answers.size === 0)
|
|
258
261
|
return undefined;
|
|
262
|
+
// The answers earlier events gave the same requests: one that bound closed its request (below).
|
|
263
|
+
const earlier = new Map();
|
|
264
|
+
for (const event of events) {
|
|
265
|
+
if (event === last)
|
|
266
|
+
break;
|
|
267
|
+
for (const r of getFunctionResponses(event)) {
|
|
268
|
+
if (r.name === REQUEST_CREDENTIAL_CALL && r.id && answers.has(r.id))
|
|
269
|
+
earlier.set(r.id, [...(earlier.get(r.id) ?? []), r.response]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
259
272
|
// The requests this agent made, by id.
|
|
260
273
|
const requests = new Map();
|
|
261
274
|
for (const event of events) {
|
|
@@ -274,14 +287,20 @@ export async function grantedCalls(agent, scope) {
|
|
|
274
287
|
const request = requests.get(id);
|
|
275
288
|
if (!request || !bindsGrant(request.config, response))
|
|
276
289
|
continue;
|
|
290
|
+
// A request a grant already bound is closed (WS5-5): a replayed grant runs nothing, as a replayed approval runs nothing. ADK resumes it again.
|
|
291
|
+
if ((earlier.get(id) ?? []).some((before) => bindsGrant(request.config, before)))
|
|
292
|
+
continue;
|
|
277
293
|
const callId = request.args.function_call_id ?? request.args.functionCallId;
|
|
278
294
|
if (typeof callId === 'string' && callId && !callId.startsWith(TOOLSET_AUTH_CREDENTIAL_ID_PREFIX))
|
|
279
295
|
resume.add(callId);
|
|
280
296
|
}
|
|
281
297
|
if (resume.size === 0)
|
|
282
298
|
return undefined;
|
|
283
|
-
// As ADK: the latest event before the answer that made any of the calls, those calls only.
|
|
299
|
+
// As ADK: the latest event before the answer that made any of the calls, those calls only. Only one this agent authored (WS5-5):
|
|
300
|
+
// ADK takes any author's, so a call forged into a user event with the paused call's id would run with the forged arguments.
|
|
284
301
|
for (let i = events.length - 2; i >= 0; i--) {
|
|
302
|
+
if (events[i]?.author !== agent.name)
|
|
303
|
+
continue;
|
|
285
304
|
const calls = getFunctionCalls(events[i]);
|
|
286
305
|
if (!calls.some((c) => c.id && resume.has(c.id)))
|
|
287
306
|
continue;
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* xAI); `code_execution` first among Gemini's own tools; and the
|
|
44
44
|
* set_model_response tool when (2) asked for it, then the caller's
|
|
45
45
|
* `extraTools` (self-correction's reflection tool, as ADK's plugin adds
|
|
46
|
-
* it to the toolsDict last). In `mode: task`, finish_task takes
|
|
46
|
+
* it to the toolsDict last, when the step declares it: ADR 0097). In `mode: task`, finish_task takes
|
|
47
47
|
* set_model_response's place and the output schema is never the
|
|
48
48
|
* response schema (lib/runtime/native/taskMode.ts).
|
|
49
49
|
* 5. Tool choice, the output schema or JSON mode, reasoning and sampling,
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* xAI); `code_execution` first among Gemini's own tools; and the
|
|
44
44
|
* set_model_response tool when (2) asked for it, then the caller's
|
|
45
45
|
* `extraTools` (self-correction's reflection tool, as ADK's plugin adds
|
|
46
|
-
* it to the toolsDict last). In `mode: task`, finish_task takes
|
|
46
|
+
* it to the toolsDict last, when the step declares it: ADR 0097). In `mode: task`, finish_task takes
|
|
47
47
|
* set_model_response's place and the output schema is never the
|
|
48
48
|
* response schema (lib/runtime/native/taskMode.ts).
|
|
49
49
|
* 5. Tool choice, the output schema or JSON mode, reasoning and sampling,
|
|
@@ -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
|
*
|
|
@@ -56,6 +68,48 @@ export declare const DEFAULT_MODEL_ERROR_RETRIES = 2;
|
|
|
56
68
|
export declare const DEFAULT_TOOL_ERROR_RETRIES = 3;
|
|
57
69
|
export declare const ADK_HANDLE_MODEL_ERROR = "adk_handle_model_error";
|
|
58
70
|
export declare const REFLECT_AND_RETRY_RESPONSE_TYPE = "ERROR_HANDLED_BY_REFLECT_AND_RETRY_PLUGIN";
|
|
71
|
+
/**
|
|
72
|
+
* Marks `adapter` as one the ADK runtime would call through the ADK shim
|
|
73
|
+
* (a resolver's shim or contract adapter, lib/compileNative.ts), not through
|
|
74
|
+
* ADK's own Gemini. Returns it.
|
|
75
|
+
*/
|
|
76
|
+
export declare function servedThroughShim<T extends object>(adapter: T): T;
|
|
77
|
+
/**
|
|
78
|
+
* Whether the ADK runtime would tell this adapter's model of the reflection
|
|
79
|
+
* tool (ADR 0097). The plugin puts the tool in the toolsDict alone. Every
|
|
80
|
+
* model class the engine hands ADK declares the toolsDict, but ADK's own
|
|
81
|
+
* Gemini (and TracedGemini over it) sends only the request's config, which
|
|
82
|
+
* never holds it. A Gemini adapter stands for ADK's Gemini unless a caller
|
|
83
|
+
* handed it over behind the shim (servedThroughShim).
|
|
84
|
+
*/
|
|
85
|
+
export declare function declaresReflectionTool(adapter: {
|
|
86
|
+
readonly provider: string;
|
|
87
|
+
}): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Whether this adapter stands, on the native runtime, for ADK's own Gemini
|
|
90
|
+
* (TracedGemini) on the ADK runtime: a Gemini adapter no caller handed over
|
|
91
|
+
* behind the shim. The step declares no reflection tool to it (ADR 0097) and
|
|
92
|
+
* stores no `turnComplete` on its events, as ADK's Gemini writes none
|
|
93
|
+
* (ADR 0100).
|
|
94
|
+
*/
|
|
95
|
+
export declare function standsForAdkGemini(adapter: {
|
|
96
|
+
readonly provider: string;
|
|
97
|
+
}): boolean;
|
|
98
|
+
/** How the reflection call stored in a model's place is signed. */
|
|
99
|
+
export interface ReflectionSigning {
|
|
100
|
+
/** The signature for a reflection call whose replaced response carried none; absent: left unsigned. */
|
|
101
|
+
placeholder?: string;
|
|
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 declare function reflectionSigning(adapter: {
|
|
111
|
+
readonly provider: string;
|
|
112
|
+
}, model: string | undefined): ReflectionSigning | undefined;
|
|
59
113
|
/** What afterModel leaves of a response: the response to record, or the error event that ends the step. */
|
|
60
114
|
export type CorrectedResponse = {
|
|
61
115
|
response: LlmResponse;
|
|
@@ -68,10 +122,14 @@ export type CorrectedResponse = {
|
|
|
68
122
|
};
|
|
69
123
|
/** The model side, for one agent's steps in one run (lib/runtime/native/step.ts). */
|
|
70
124
|
export interface ModelCorrection {
|
|
71
|
-
/**
|
|
125
|
+
/** After the agent's own tools: the reflection tool. Declared to the model only where ADK declares it (declaresReflectionTool). */
|
|
72
126
|
readonly tools: readonly Tool[];
|
|
73
|
-
/**
|
|
74
|
-
|
|
127
|
+
/**
|
|
128
|
+
* ADK's afterModelCallback, on every response the step reads. With
|
|
129
|
+
* `signing` (reflectionSigning), a reflection call put in the response's
|
|
130
|
+
* place is signed.
|
|
131
|
+
*/
|
|
132
|
+
afterModel(response: LlmResponse, signing?: ReflectionSigning): CorrectedResponse;
|
|
75
133
|
}
|
|
76
134
|
/** The tool side, for one call (lib/runtime/native/agentLoop.ts). Each call makes exactly one of these. */
|
|
77
135
|
export interface CallCorrection {
|