melchizedek-agents 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +12 -0
- package/CHANGELOG.md +825 -0
- package/DOCUMENTATION.md +217 -59
- package/README.md +42 -1
- package/config/agents/examples/harness.yaml +3 -1
- package/config/agents/examples/model_zoo.yaml +10 -9
- package/config/agents/syndicateSchema.yaml +50 -16
- package/config/agents/templates/account_memory.yaml +2 -6
- package/config/agents/templates/case_desk.yaml +3 -9
- package/config/agents/templates/draft_review.yaml +3 -9
- package/config/agents/templates/intake_extractor.yaml +1 -3
- package/config/agents/templates/research_brief.yaml +3 -9
- package/config/agents/templates/research_desk.yaml +3 -9
- package/config/agents/templates/review_panel.yaml +4 -12
- package/config/agents/templates/support_triage.yaml +4 -12
- package/config/agents/templates/systems_operator.yaml +2 -6
- package/db/hardening.sql +7 -1
- package/db/migrations/0013_tool_credentials.sql +260 -0
- package/dist/lib/a2a/app.d.ts +70 -6
- package/dist/lib/a2a/app.js +107 -2
- package/dist/lib/a2a/executor.d.ts +20 -3
- package/dist/lib/a2a/executor.js +66 -7
- package/dist/lib/a2a/remoteAgent.d.ts +24 -9
- package/dist/lib/a2a/remoteAgent.js +31 -14
- package/dist/lib/compile.d.ts +121 -30
- package/dist/lib/compile.js +137 -124
- package/dist/lib/compileAdk.d.ts +29 -0
- package/dist/lib/compileAdk.js +132 -0
- package/dist/lib/compileNative.d.ts +85 -0
- package/dist/lib/compileNative.js +222 -0
- package/dist/lib/config.d.ts +6 -5
- package/dist/lib/config.js +6 -5
- package/dist/lib/doctor.js +1 -0
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.js +2 -1
- package/dist/lib/loadSyndicate.d.ts +18 -0
- package/dist/lib/memory/erase.d.ts +5 -3
- package/dist/lib/memory/erase.js +5 -2
- package/dist/lib/memory/namespace.d.ts +8 -1
- package/dist/lib/memory/namespace.js +12 -6
- package/dist/lib/memory/providers.js +2 -1
- package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
- package/dist/lib/memory/supabaseMemoryService.js +42 -2
- package/dist/lib/model.d.ts +47 -0
- package/dist/lib/model.js +37 -0
- package/dist/lib/models/adapterResolver.d.ts +113 -0
- package/dist/lib/models/adapterResolver.js +136 -0
- package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
- package/dist/lib/models/adkGeminiAdapter.js +367 -0
- package/dist/lib/models/adkShim.d.ts +111 -0
- package/dist/lib/models/adkShim.js +136 -0
- package/dist/lib/models/capabilities.d.ts +3 -0
- package/dist/lib/models/capabilities.js +20 -13
- package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
- package/dist/lib/models/chatCompletionsAdapter.js +854 -0
- package/dist/lib/models/circuitBreaker.d.ts +42 -0
- package/dist/lib/models/circuitBreaker.js +70 -0
- package/dist/lib/models/claudeAdapter.d.ts +103 -0
- package/dist/lib/models/claudeAdapter.js +669 -0
- package/dist/lib/models/claudeLlm.d.ts +39 -62
- package/dist/lib/models/claudeLlm.js +44 -349
- package/dist/lib/models/claudeModels.d.ts +127 -0
- package/dist/lib/models/claudeModels.js +237 -0
- package/dist/lib/models/contract.d.ts +395 -0
- package/dist/lib/models/contract.js +39 -0
- package/dist/lib/models/endpoints.js +2 -1
- package/dist/lib/models/errorResponse.d.ts +50 -0
- package/dist/lib/models/errorResponse.js +48 -0
- package/dist/lib/models/fallback.d.ts +17 -10
- package/dist/lib/models/fallback.js +53 -42
- package/dist/lib/models/fallbackAdapter.d.ts +55 -0
- package/dist/lib/models/fallbackAdapter.js +62 -0
- package/dist/lib/models/gateway.js +2 -1
- package/dist/lib/models/gatewayAdapter.d.ts +51 -0
- package/dist/lib/models/gatewayAdapter.js +81 -0
- package/dist/lib/models/gatewayLlm.d.ts +12 -33
- package/dist/lib/models/gatewayLlm.js +14 -75
- package/dist/lib/models/geminiAdapter.d.ts +123 -0
- package/dist/lib/models/geminiAdapter.js +831 -0
- package/dist/lib/models/geminiState.d.ts +25 -0
- package/dist/lib/models/geminiState.js +25 -0
- package/dist/lib/models/genaiMapping.d.ts +263 -0
- package/dist/lib/models/genaiMapping.js +862 -0
- package/dist/lib/models/gptAdapter.d.ts +240 -0
- package/dist/lib/models/gptAdapter.js +804 -0
- package/dist/lib/models/gptLlm.d.ts +63 -123
- package/dist/lib/models/gptLlm.js +88 -602
- package/dist/lib/models/grokAdapter.d.ts +86 -0
- package/dist/lib/models/grokAdapter.js +142 -0
- package/dist/lib/models/grokLlm.d.ts +22 -45
- package/dist/lib/models/grokLlm.js +22 -67
- package/dist/lib/models/kimiAdapter.d.ts +157 -0
- package/dist/lib/models/kimiAdapter.js +203 -0
- package/dist/lib/models/kimiLlm.d.ts +15 -107
- package/dist/lib/models/kimiLlm.js +17 -153
- package/dist/lib/models/ollamaAdapter.d.ts +125 -0
- package/dist/lib/models/ollamaAdapter.js +155 -0
- package/dist/lib/models/ollamaLlm.d.ts +11 -107
- package/dist/lib/models/ollamaLlm.js +13 -151
- package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
- package/dist/lib/models/openAiCompatibleLlm.js +109 -671
- package/dist/lib/models/providerState.d.ts +63 -0
- package/dist/lib/models/providerState.js +61 -0
- package/dist/lib/models/reasoning.d.ts +26 -0
- package/dist/lib/models/reasoning.js +59 -0
- package/dist/lib/models/registry.d.ts +58 -35
- package/dist/lib/models/registry.js +89 -118
- package/dist/lib/models/retry.d.ts +2 -1
- package/dist/lib/models/retry.js +3 -2
- package/dist/lib/models/schemaNormalize.d.ts +98 -0
- package/dist/lib/models/schemaNormalize.js +321 -1
- package/dist/lib/models/tracedGemini.d.ts +41 -0
- package/dist/lib/models/tracedGemini.js +98 -0
- package/dist/lib/models/urls.d.ts +11 -0
- package/dist/lib/models/urls.js +16 -0
- package/dist/lib/net/redirects.d.ts +21 -2
- package/dist/lib/net/redirects.js +11 -34
- package/dist/lib/observability/audit.d.ts +5 -1
- package/dist/lib/observability/lineage.d.ts +14 -0
- package/dist/lib/observability/lineage.js +33 -0
- package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
- package/dist/lib/observability/supabaseSpanExporter.js +17 -9
- package/dist/lib/observability/tracer.d.ts +26 -8
- package/dist/lib/observability/tracer.js +43 -18
- package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
- package/dist/lib/runtime/adkMemoryBridge.js +68 -0
- package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
- package/dist/lib/runtime/adkSessionBridge.js +176 -0
- package/dist/lib/runtime/credentials.d.ts +59 -0
- package/dist/lib/runtime/credentials.js +62 -0
- package/dist/lib/runtime/events.d.ts +229 -0
- package/dist/lib/runtime/events.js +274 -0
- package/dist/lib/runtime/logging.d.ts +32 -0
- package/dist/lib/runtime/logging.js +55 -0
- package/dist/lib/runtime/memoryService.d.ts +72 -0
- package/dist/lib/runtime/memoryService.js +25 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +203 -0
- package/dist/lib/runtime/native/agentLoop.js +687 -0
- package/dist/lib/runtime/native/compaction.d.ts +103 -0
- package/dist/lib/runtime/native/compaction.js +191 -0
- package/dist/lib/runtime/native/delegate.d.ts +98 -0
- package/dist/lib/runtime/native/delegate.js +173 -0
- package/dist/lib/runtime/native/history.d.ts +83 -0
- package/dist/lib/runtime/native/history.js +312 -0
- package/dist/lib/runtime/native/interrupts.d.ts +126 -0
- package/dist/lib/runtime/native/interrupts.js +291 -0
- package/dist/lib/runtime/native/request.d.ts +215 -0
- package/dist/lib/runtime/native/request.js +585 -0
- package/dist/lib/runtime/native/selfCorrection.d.ts +106 -0
- package/dist/lib/runtime/native/selfCorrection.js +253 -0
- package/dist/lib/runtime/native/step.d.ts +148 -0
- package/dist/lib/runtime/native/step.js +239 -0
- package/dist/lib/runtime/native/taskMode.d.ts +64 -0
- package/dist/lib/runtime/native/taskMode.js +126 -0
- package/dist/lib/runtime/native/telemetry.d.ts +75 -0
- package/dist/lib/runtime/native/telemetry.js +299 -0
- package/dist/lib/runtime/native/tempState.d.ts +31 -0
- package/dist/lib/runtime/native/tempState.js +56 -0
- package/dist/lib/runtime/nativeTurn.d.ts +86 -0
- package/dist/lib/runtime/nativeTurn.js +107 -0
- package/dist/lib/runtime/questions.d.ts +29 -7
- package/dist/lib/runtime/questions.js +47 -32
- package/dist/lib/runtime/runtimeFlag.d.ts +30 -0
- package/dist/lib/runtime/runtimeFlag.js +50 -0
- package/dist/lib/runtime/sessions.d.ts +166 -0
- package/dist/lib/runtime/sessions.js +201 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +41 -5
- package/dist/lib/runtime/syndicateTurn.js +202 -32
- package/dist/lib/session/supabaseSessionService.d.ts +64 -17
- package/dist/lib/session/supabaseSessionService.js +148 -105
- package/dist/lib/session/transcript.d.ts +68 -22
- package/dist/lib/session/transcript.js +102 -28
- package/dist/lib/skills.js +33 -7
- package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
- package/dist/lib/storage/postgres/credentialStore.js +82 -0
- package/dist/lib/storage/postgres/index.d.ts +12 -0
- package/dist/lib/storage/postgres/index.js +6 -1
- package/dist/lib/storage/postgres/sessionService.d.ts +43 -15
- package/dist/lib/storage/postgres/sessionService.js +128 -66
- package/dist/lib/syndicateSchema.d.ts +47 -0
- package/dist/lib/syndicateSchema.js +72 -3
- package/dist/lib/toolRegistry.d.ts +15 -2
- package/dist/lib/toolRegistry.js +33 -17
- package/dist/lib/tools/adkTool.d.ts +81 -0
- package/dist/lib/tools/adkTool.js +268 -0
- package/dist/lib/tools/auth.d.ts +118 -0
- package/dist/lib/tools/auth.js +89 -0
- package/dist/lib/tools/collectionsSearchTool.d.ts +7 -6
- package/dist/lib/tools/collectionsSearchTool.js +11 -26
- package/dist/lib/tools/credentialCipher.d.ts +58 -0
- package/dist/lib/tools/credentialCipher.js +116 -0
- package/dist/lib/tools/credentialStore.d.ts +77 -0
- package/dist/lib/tools/credentialStore.js +246 -0
- package/dist/lib/tools/examples.d.ts +43 -0
- package/dist/lib/tools/examples.js +62 -0
- package/dist/lib/tools/generateImageTool.d.ts +15 -6
- package/dist/lib/tools/generateImageTool.js +19 -29
- package/dist/lib/tools/inspectImageTool.d.ts +9 -6
- package/dist/lib/tools/inspectImageTool.js +13 -17
- package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
- package/dist/lib/tools/mcpToolFactory.js +46 -47
- package/dist/lib/tools/memoryTools.d.ts +61 -0
- package/dist/lib/tools/memoryTools.js +119 -0
- package/dist/lib/tools/nativeTools.d.ts +38 -0
- package/dist/lib/tools/nativeTools.js +43 -0
- package/dist/lib/tools/oauthConsent.d.ts +181 -0
- package/dist/lib/tools/oauthConsent.js +330 -0
- package/dist/lib/tools/openapi/call.d.ts +108 -0
- package/dist/lib/tools/openapi/call.js +295 -0
- package/dist/lib/tools/openapi/parse.d.ts +147 -0
- package/dist/lib/tools/openapi/parse.js +570 -0
- package/dist/lib/tools/openapiTools.d.ts +25 -17
- package/dist/lib/tools/openapiTools.js +124 -121
- package/dist/lib/tools/scienceTools.d.ts +14 -14
- package/dist/lib/tools/skillToolset.d.ts +58 -87
- package/dist/lib/tools/skillToolset.js +61 -142
- package/dist/lib/tools/skills/env.d.ts +58 -0
- package/dist/lib/tools/skills/env.js +165 -0
- package/dist/lib/tools/skills/executor.d.ts +114 -0
- package/dist/lib/tools/skills/executor.js +331 -0
- package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
- package/dist/lib/tools/skills/frontmatter.js +215 -0
- package/dist/lib/tools/skills/loader.d.ts +91 -0
- package/dist/lib/tools/skills/loader.js +257 -0
- package/dist/lib/tools/skills/tools.d.ts +187 -0
- package/dist/lib/tools/skills/tools.js +386 -0
- package/dist/lib/tools/taskTools.d.ts +15 -15
- package/dist/lib/tools/tool.d.ts +354 -0
- package/dist/lib/tools/tool.js +304 -0
- package/dist/lib/tools/toolContract.d.ts +89 -33
- package/dist/lib/tools/toolContract.js +104 -82
- package/dist/lib/tools/urlContextTool.d.ts +3 -0
- package/dist/lib/tools/urlContextTool.js +5 -1
- package/dist/lib/tools/webExtractTool.d.ts +2 -2
- package/dist/lib/tools/webExtractTool.js +2 -1
- package/dist/lib/tools/webSearchTool.d.ts +6 -9
- package/dist/lib/tools/webSearchTool.js +14 -59
- package/dist/lib/tools/wikiTools.d.ts +19 -19
- package/dist/lib/tools/wikiTools.js +13 -9
- package/dist/lib/tools/xApiSearchTool.d.ts +2 -2
- package/dist/lib/tools/xApiSearchTool.js +2 -1
- package/dist/lib/tools/xSearchTool.d.ts +7 -9
- package/dist/lib/tools/xSearchTool.js +11 -62
- package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
- package/dist/lib/tools/xaiSearchParams.js +126 -0
- package/dist/lib/wiki/agentRun.d.ts +12 -0
- package/dist/lib/wiki/agentRun.js +72 -34
- package/dist/lib/wiki/entities.d.ts +12 -0
- package/dist/lib/wiki/entities.js +45 -3
- package/dist/lib/workflow/agentNode.d.ts +136 -0
- package/dist/lib/workflow/agentNode.js +274 -0
- package/dist/lib/workflow/graph.d.ts +177 -0
- package/dist/lib/workflow/graph.js +458 -0
- package/dist/lib/workflow/nodeEvents.d.ts +50 -0
- package/dist/lib/workflow/nodeEvents.js +101 -0
- package/dist/lib/workflow/pause.d.ts +122 -0
- package/dist/lib/workflow/pause.js +180 -0
- package/dist/lib/workflow/resume.d.ts +165 -0
- package/dist/lib/workflow/resume.js +440 -0
- package/dist/lib/workflow/route.d.ts +58 -0
- package/dist/lib/workflow/route.js +69 -0
- package/dist/lib/workflow/scheduler.d.ts +415 -0
- package/dist/lib/workflow/scheduler.js +743 -0
- package/dist/lib/workflow/toolNode.d.ts +127 -0
- package/dist/lib/workflow/toolNode.js +236 -0
- package/dist/lib/workflow/turn.d.ts +118 -0
- package/dist/lib/workflow/turn.js +216 -0
- package/dist/lib/workflowConfig.d.ts +3 -2
- package/dist/lib/workflowConfig.js +3 -12
- package/dist/scripts/a2a_server.js +4 -3
- package/dist/scripts/assistant_worker.d.ts +2 -1
- package/dist/scripts/assistant_worker.js +9 -4
- package/dist/scripts/syndicate_chat.js +19 -10
- package/package.json +4 -1
- package/skills/briefs/melchizedek-author.md +3 -2
- package/skills/briefs/melchizedek-models.md +13 -9
- package/skills/briefs/melchizedek.md +3 -3
- package/skills/melchizedek/SKILL.md +2 -1
- package/skills/melchizedek-author/SKILL.md +2 -1
- package/skills/melchizedek-author/assets/minimal.yaml +2 -0
- package/skills/melchizedek-models/SKILL.md +23 -6
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/runtime/native/history.ts — which stored events a native model call
|
|
3
|
+
* sees, and in what form (ADR 0045, ADR 0066).
|
|
4
|
+
*
|
|
5
|
+
* WHY this file exists:
|
|
6
|
+
* A model request's history is the session's events projected for one
|
|
7
|
+
* agent: its own turns as they are, the person's words as they are,
|
|
8
|
+
* another agent's turns retold as context, approvals and credential
|
|
9
|
+
* requests left out, and a tool's late answer moved next to the call it
|
|
10
|
+
* answers. ADK's content processor does this on the ADK runtime
|
|
11
|
+
* (agents/processors/content_processor_utils.js in @google/adk 2.2), and a
|
|
12
|
+
* session one runtime wrote must read the same on the other, so this is
|
|
13
|
+
* that processor, rule for rule, over TurnEvents:
|
|
14
|
+
*
|
|
15
|
+
* - includeContents `default`: every visible event; `none`: from the
|
|
16
|
+
* latest message by the person or by another agent, reaching back to
|
|
17
|
+
* the calls its tool answers answer (getCurrentTurnContents).
|
|
18
|
+
* - Skipped: an event with no role, or whose first part is empty text; an
|
|
19
|
+
* event on another branch (a branch that is not a prefix of this one);
|
|
20
|
+
* an event in another isolation scope; ADK's credential, confirmation
|
|
21
|
+
* and input-request calls and answers.
|
|
22
|
+
* - Another agent's event becomes a user message: "For context:", then
|
|
23
|
+
* `[name] said: …`, `[name] called tool …`, `[name] tool … returned
|
|
24
|
+
* result: …`, any other part copied.
|
|
25
|
+
* - The latest compaction summary stands in for the events it covers, as
|
|
26
|
+
* "[Previous Context Summary]:\n<summary>".
|
|
27
|
+
* - The answer to the latest call is merged and moved next to that call;
|
|
28
|
+
* an earlier call's answers likewise.
|
|
29
|
+
* - ADK's own call ids (`adk-…`) are removed: the provider never sees them.
|
|
30
|
+
* The contents are deep copies, so nothing here changes a stored event.
|
|
31
|
+
*
|
|
32
|
+
* The result is genai-shaped content, as stored. The request builder maps it
|
|
33
|
+
* to the model contract through lib/models/genaiMapping.ts, the same mapping
|
|
34
|
+
* the ADK shim applies, so both runtimes hand an adapter the same messages.
|
|
35
|
+
*/
|
|
36
|
+
import { getFunctionCalls, getFunctionResponses } from '../events.js';
|
|
37
|
+
/** ADK prefixes the call ids it makes with this (AF_FUNCTION_CALL_ID_PREFIX). */
|
|
38
|
+
export const ADK_CALL_ID_PREFIX = 'adk-';
|
|
39
|
+
/** ADK's framework calls that a model never sees in its history. */
|
|
40
|
+
export const REQUEST_CONFIRMATION_CALL = 'adk_request_confirmation';
|
|
41
|
+
export const REQUEST_CREDENTIAL_CALL = 'adk_request_credential';
|
|
42
|
+
export const REQUEST_INPUT_CALL = 'adk_request_input';
|
|
43
|
+
const UNSTRINGIFIABLE_VALUE = '<unstringifiable value>';
|
|
44
|
+
export function isCompacted(event) {
|
|
45
|
+
return event.isCompacted === true;
|
|
46
|
+
}
|
|
47
|
+
function isVisible(event, scope) {
|
|
48
|
+
return event.isolationScope === undefined || event.isolationScope === scope;
|
|
49
|
+
}
|
|
50
|
+
/** The latest compaction and what follows it, or every visible event when there is none (ADK's getActiveEvents). */
|
|
51
|
+
export function activeEvents(events, scope) {
|
|
52
|
+
const visible = events.filter((e) => isVisible(e, scope));
|
|
53
|
+
const latest = visible.filter(isCompacted).pop();
|
|
54
|
+
if (!latest)
|
|
55
|
+
return visible;
|
|
56
|
+
return [latest, ...visible.filter((e) => !isCompacted(e) && e.timestamp > latest.endTime)];
|
|
57
|
+
}
|
|
58
|
+
export function isSegmentPrefix(current, target) {
|
|
59
|
+
return !target || target === current || (!!current && current.startsWith(`${target}.`));
|
|
60
|
+
}
|
|
61
|
+
function namesCall(event, name) {
|
|
62
|
+
return (event.content?.parts ?? []).some((p) => p.functionCall?.name === name || p.functionResponse?.name === name);
|
|
63
|
+
}
|
|
64
|
+
function shouldInclude(event, branch, scope) {
|
|
65
|
+
if (!event.content?.role || event.content.parts?.[0]?.text === '')
|
|
66
|
+
return false;
|
|
67
|
+
if (branch && event.branch && !isSegmentPrefix(branch, event.branch))
|
|
68
|
+
return false;
|
|
69
|
+
if (!isVisible(event, scope))
|
|
70
|
+
return false;
|
|
71
|
+
return !namesCall(event, REQUEST_CREDENTIAL_CALL) && !namesCall(event, REQUEST_CONFIRMATION_CALL) && !namesCall(event, REQUEST_INPUT_CALL);
|
|
72
|
+
}
|
|
73
|
+
function fromAnotherAgent(agentName, event) {
|
|
74
|
+
return !!agentName && event.author !== agentName && event.author !== 'user';
|
|
75
|
+
}
|
|
76
|
+
function clone(value) {
|
|
77
|
+
return value === undefined ? value : structuredClone(value);
|
|
78
|
+
}
|
|
79
|
+
function safeStringify(value) {
|
|
80
|
+
if (typeof value === 'string')
|
|
81
|
+
return value;
|
|
82
|
+
try {
|
|
83
|
+
const json = JSON.stringify(value);
|
|
84
|
+
if (typeof json === 'string')
|
|
85
|
+
return json;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// fall through to String()
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return String(value);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return UNSTRINGIFIABLE_VALUE;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Another agent's event, retold to this one as context in a user message. */
|
|
98
|
+
function retold(event) {
|
|
99
|
+
if (!event.content?.parts?.length)
|
|
100
|
+
return event;
|
|
101
|
+
const parts = [{ text: 'For context:' }];
|
|
102
|
+
for (const part of event.content.parts) {
|
|
103
|
+
if (part.text && !part.thought) {
|
|
104
|
+
parts.push({ text: `[${event.author}] said: ${part.text}` });
|
|
105
|
+
}
|
|
106
|
+
else if (part.functionCall) {
|
|
107
|
+
parts.push({ text: `[${event.author}] called tool \`${part.functionCall.name}\` with parameters: ${safeStringify(part.functionCall.args)}` });
|
|
108
|
+
}
|
|
109
|
+
else if (part.functionResponse) {
|
|
110
|
+
parts.push({ text: `[${event.author}] tool \`${part.functionResponse.name}\` returned result: ${safeStringify(part.functionResponse.response)}` });
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
parts.push(clone(part));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { ...event, author: 'user', content: { role: 'user', parts } };
|
|
117
|
+
}
|
|
118
|
+
function summaryOf(event) {
|
|
119
|
+
return { ...event, author: 'user', content: { role: 'user', parts: [{ text: `[Previous Context Summary]:\n${event.compactedContent}` }] } };
|
|
120
|
+
}
|
|
121
|
+
/** Several answer events as one, a later answer to a call replacing an earlier one in place. */
|
|
122
|
+
function mergeResponses(events) {
|
|
123
|
+
if (events.length === 0)
|
|
124
|
+
throw new Error('Cannot merge an empty list of events.');
|
|
125
|
+
const first = events[0];
|
|
126
|
+
const content = clone(first.content);
|
|
127
|
+
const parts = content?.parts;
|
|
128
|
+
if (!parts || parts.length === 0)
|
|
129
|
+
throw new Error('There should be at least one function_response part.');
|
|
130
|
+
const index = new Map();
|
|
131
|
+
parts.forEach((part, i) => {
|
|
132
|
+
if (part.functionResponse?.id)
|
|
133
|
+
index.set(part.functionResponse.id, i);
|
|
134
|
+
});
|
|
135
|
+
for (const event of events.slice(1)) {
|
|
136
|
+
if (!event.content?.parts)
|
|
137
|
+
throw new Error('There should be at least one function_response part.');
|
|
138
|
+
for (const part of event.content.parts) {
|
|
139
|
+
const copy = clone(part);
|
|
140
|
+
const id = copy.functionResponse?.id;
|
|
141
|
+
if (id) {
|
|
142
|
+
const at = index.get(id);
|
|
143
|
+
if (at !== undefined)
|
|
144
|
+
parts[at] = copy;
|
|
145
|
+
else {
|
|
146
|
+
parts.push(copy);
|
|
147
|
+
index.set(id, parts.length - 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
parts.push(copy);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { ...first, content };
|
|
156
|
+
}
|
|
157
|
+
/** The latest event answers calls: its answers, merged, sit right after the event that made those calls. */
|
|
158
|
+
function latestAnswerNextToItsCall(events) {
|
|
159
|
+
if (events.length === 0)
|
|
160
|
+
return events;
|
|
161
|
+
const latest = events[events.length - 1];
|
|
162
|
+
const responses = getFunctionResponses(latest);
|
|
163
|
+
if (!responses.length)
|
|
164
|
+
return events;
|
|
165
|
+
const ids = new Set(responses.filter((r) => !!r.id).map((r) => r.id));
|
|
166
|
+
const secondLatest = events.at(-2);
|
|
167
|
+
if (secondLatest && getFunctionCalls(secondLatest).some((c) => c.id && ids.has(c.id)))
|
|
168
|
+
return events;
|
|
169
|
+
let match;
|
|
170
|
+
for (let i = events.length - 2; i >= 0 && !match; i--) {
|
|
171
|
+
const calls = getFunctionCalls(events[i]);
|
|
172
|
+
if (!calls.length)
|
|
173
|
+
continue;
|
|
174
|
+
if (!calls.some((c) => c.id && ids.has(c.id)))
|
|
175
|
+
continue;
|
|
176
|
+
const callIds = new Set(calls.map((c) => c.id).filter((id) => !!id));
|
|
177
|
+
if (![...ids].every((id) => callIds.has(id))) {
|
|
178
|
+
throw new Error(`Last response event should only contain the responses for the function calls in the same function call event. Function call ids found : ${[...callIds].join(', ')}, function response ids provided: ${[...ids].join(', ')}`);
|
|
179
|
+
}
|
|
180
|
+
match = { at: i, ids: callIds };
|
|
181
|
+
}
|
|
182
|
+
if (!match)
|
|
183
|
+
throw new Error(`No function call event found for function responses ids: ${[...ids].join(', ')}`);
|
|
184
|
+
const answers = [];
|
|
185
|
+
for (let i = match.at + 1; i < events.length - 1; i++) {
|
|
186
|
+
const event = events[i];
|
|
187
|
+
if (getFunctionResponses(event).some((r) => r.id && match.ids.has(r.id)))
|
|
188
|
+
answers.push(event);
|
|
189
|
+
}
|
|
190
|
+
answers.push(latest);
|
|
191
|
+
return [...events.slice(0, match.at + 1), mergeResponses(answers)];
|
|
192
|
+
}
|
|
193
|
+
/** Each earlier call's answers, wherever they landed, sit right after the call. */
|
|
194
|
+
function answersNextToTheirCalls(events) {
|
|
195
|
+
const answeredAt = new Map();
|
|
196
|
+
events.forEach((event, i) => {
|
|
197
|
+
for (const r of getFunctionResponses(event))
|
|
198
|
+
if (r.id)
|
|
199
|
+
answeredAt.set(r.id, i);
|
|
200
|
+
});
|
|
201
|
+
const out = [];
|
|
202
|
+
for (const event of events) {
|
|
203
|
+
if (getFunctionResponses(event).length > 0)
|
|
204
|
+
continue;
|
|
205
|
+
const calls = getFunctionCalls(event);
|
|
206
|
+
out.push(event);
|
|
207
|
+
if (!calls.length)
|
|
208
|
+
continue;
|
|
209
|
+
const at = new Set();
|
|
210
|
+
for (const call of calls) {
|
|
211
|
+
const i = call.id ? answeredAt.get(call.id) : undefined;
|
|
212
|
+
if (i !== undefined)
|
|
213
|
+
at.add(i);
|
|
214
|
+
}
|
|
215
|
+
if (at.size === 1)
|
|
216
|
+
out.push(events[[...at][0]]);
|
|
217
|
+
else if (at.size > 1)
|
|
218
|
+
out.push(mergeResponses([...at].sort((a, b) => a - b).map((i) => events[i])));
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
function stripAdkCallIds(content) {
|
|
223
|
+
for (const part of content?.parts ?? []) {
|
|
224
|
+
if (part.functionCall?.id?.startsWith(ADK_CALL_ID_PREFIX))
|
|
225
|
+
part.functionCall.id = undefined;
|
|
226
|
+
if (part.functionResponse?.id?.startsWith(ADK_CALL_ID_PREFIX))
|
|
227
|
+
part.functionResponse.id = undefined;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** The contents of every visible event, projected for `agentName` (ADK's getContents). The compactor's estimate reads it too. */
|
|
231
|
+
export function contentsOf(events, agentName, branch, scope) {
|
|
232
|
+
const kept = [];
|
|
233
|
+
for (const event of events) {
|
|
234
|
+
if (!isVisible(event, scope))
|
|
235
|
+
continue;
|
|
236
|
+
if (isCompacted(event)) {
|
|
237
|
+
kept.push(summaryOf(event));
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (!shouldInclude(event, branch, scope))
|
|
241
|
+
continue;
|
|
242
|
+
kept.push(fromAnotherAgent(agentName, event) ? retold(event) : event);
|
|
243
|
+
}
|
|
244
|
+
const arranged = answersNextToTheirCalls(latestAnswerNextToItsCall(kept));
|
|
245
|
+
return arranged.map((event) => {
|
|
246
|
+
const content = clone(event.content);
|
|
247
|
+
stripAdkCallIds(content);
|
|
248
|
+
return content;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/** Where the current turn starts: the anchor, or earlier when its answers answer earlier calls. */
|
|
252
|
+
function turnStart(events, anchor) {
|
|
253
|
+
const answered = new Set();
|
|
254
|
+
for (const event of events.slice(anchor)) {
|
|
255
|
+
for (const part of event.content?.parts ?? [])
|
|
256
|
+
if (part.functionResponse?.id)
|
|
257
|
+
answered.add(part.functionResponse.id);
|
|
258
|
+
}
|
|
259
|
+
if (answered.size === 0)
|
|
260
|
+
return anchor;
|
|
261
|
+
let start = anchor;
|
|
262
|
+
for (let i = anchor - 1; i >= 0; i--) {
|
|
263
|
+
const calls = (events[i]?.content?.parts ?? []).flatMap((p) => (p.functionCall ? [p.functionCall] : []));
|
|
264
|
+
if (calls.some((c) => (c.id ? answered.has(c.id) : false)))
|
|
265
|
+
start = i;
|
|
266
|
+
}
|
|
267
|
+
return start;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* The genai contents a model request carries for `scope.agentName`, from
|
|
271
|
+
* the session's events (oldest first), as ADK's content processor builds
|
|
272
|
+
* them for the same session.
|
|
273
|
+
*/
|
|
274
|
+
export function projectHistory(events, scope) {
|
|
275
|
+
const active = activeEvents(events, scope.isolationScope);
|
|
276
|
+
if ((scope.includeContents ?? 'default') === 'default') {
|
|
277
|
+
return contentsOf(active, scope.agentName, scope.branch, scope.isolationScope);
|
|
278
|
+
}
|
|
279
|
+
for (let i = active.length - 1; i >= 0; i--) {
|
|
280
|
+
const event = active[i];
|
|
281
|
+
if (!shouldInclude(event, scope.branch, scope.isolationScope))
|
|
282
|
+
continue;
|
|
283
|
+
if (event.author === 'user' || fromAnotherAgent(scope.agentName, event)) {
|
|
284
|
+
return contentsOf(active.slice(turnStart(active, i)), scope.agentName, scope.branch, scope.isolationScope);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return [];
|
|
288
|
+
}
|
|
289
|
+
// ── Gemini code execution in the history ─────────────────────────────────────
|
|
290
|
+
/** ADK's BaseCodeExecutor delimiters: the first code block's, and the result's. */
|
|
291
|
+
const CODE_BLOCK = ['```tool_code\n', '\n```'];
|
|
292
|
+
const EXECUTION_RESULT = ['```tool_output\n', '\n```'];
|
|
293
|
+
/**
|
|
294
|
+
* An agent with `code_execution: gemini`: a content that ends in code
|
|
295
|
+
* becomes that code as a fenced block, and a content that is only a result
|
|
296
|
+
* becomes the result as a user message (ADK's convertCodeExecutionParts,
|
|
297
|
+
* which its code-execution processor applies to every content). In place.
|
|
298
|
+
*/
|
|
299
|
+
export function convertCodeExecutionParts(content) {
|
|
300
|
+
const parts = content.parts;
|
|
301
|
+
if (!parts?.length)
|
|
302
|
+
return;
|
|
303
|
+
const last = parts[parts.length - 1];
|
|
304
|
+
if (last.executableCode) {
|
|
305
|
+
parts[parts.length - 1] = { text: CODE_BLOCK[0] + (last.executableCode.code || '') + CODE_BLOCK[1] };
|
|
306
|
+
}
|
|
307
|
+
else if (parts.length === 1 && last.codeExecutionResult) {
|
|
308
|
+
const output = last.codeExecutionResult.output;
|
|
309
|
+
parts[parts.length - 1] = { text: output == null ? '' : EXECUTION_RESULT[0] + output + EXECUTION_RESULT[1] };
|
|
310
|
+
content.role = 'user';
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/runtime/native/interrupts.ts — approvals on the native loop: the
|
|
3
|
+
* answer to an `adk_request_confirmation` call runs or refuses the pinned
|
|
4
|
+
* call before the next model step (WS2-7a, ADR 0028, ADR 0077).
|
|
5
|
+
*
|
|
6
|
+
* WHY this file exists:
|
|
7
|
+
* A tool that requires approval does not run when called. The loop
|
|
8
|
+
* (agentLoop.ts) stores ADK's `adk_request_confirmation` call in place of
|
|
9
|
+
* the response, pinning the original call and its arguments, and the run
|
|
10
|
+
* ends paused. The person's answer arrives as the next user message: a
|
|
11
|
+
* function response to that call carrying `{ confirmed }`. On the ADK
|
|
12
|
+
* runtime, LlmAgent's request-confirmation processor reads it before every
|
|
13
|
+
* model step and runs the pinned call with the confirmation. This file is
|
|
14
|
+
* that processor, case for case, so an approval opened on either runtime
|
|
15
|
+
* resumes on the other and both store the same events.
|
|
16
|
+
*
|
|
17
|
+
* BEFORE EACH STEP, AS ADK'S PROCESSOR RUNS IT:
|
|
18
|
+
* 1. The answers: the function responses named `adk_request_confirmation`
|
|
19
|
+
* in the latest user event (events on another branch left out), each
|
|
20
|
+
* read as `{ confirmed, hint, payload }`, or as JSON under `response`.
|
|
21
|
+
* None: nothing happens, and the step goes on as usual.
|
|
22
|
+
* 2. The gates they answer: every `adk_request_confirmation` call whose id
|
|
23
|
+
* an answer names. A request authored by the user is refused
|
|
24
|
+
* (`untrusted_request`); one authored by another agent is skipped; one
|
|
25
|
+
* whose pinned call has no id or name is refused (`malformed_request`);
|
|
26
|
+
* one this agent has already answered the pinned call after is skipped,
|
|
27
|
+
* so a later step never runs the call again.
|
|
28
|
+
* 3. The binding. The pinned call must be one this agent made, by id
|
|
29
|
+
* (`unknown_original_call`); its tool must be the agent's
|
|
30
|
+
* (`unregistered_tool`); the name and the arguments must equal the
|
|
31
|
+
* call's as the model made it (`tool_name_mismatch`,
|
|
32
|
+
* `arguments_mismatch`); and the tool must require approval, or have
|
|
33
|
+
* asked for it (`confirmation_not_required`). A refusal throws ADK's
|
|
34
|
+
* IntentMismatchError text and nothing runs.
|
|
35
|
+
* 4. The bound calls run through the loop's own call path with the
|
|
36
|
+
* confirmation in their context: an approved call runs its tool, a
|
|
37
|
+
* refused one answers ADK's refusal. Their response is stored as its
|
|
38
|
+
* own event before the step builds its request, so the model reads it.
|
|
39
|
+
*
|
|
40
|
+
* Not here: a plain-text "yes" as an answer (ADK's plainTextToolConfirmation,
|
|
41
|
+
* which no surface turns on), and answers delivered by a remote peer (ADK's
|
|
42
|
+
* remoteDelivered, which no surface sets).
|
|
43
|
+
*
|
|
44
|
+
* QUESTIONS (ask_user, WS2-7b, ADR 0079) need no processor here. ADK runs its
|
|
45
|
+
* request-input processor (REQUEST_INPUT_LLM_REQUEST_PROCESSOR) next, before
|
|
46
|
+
* compaction, but it only re-runs a node tool (a workflow run as a tool) that
|
|
47
|
+
* paused on an `adk_request_input` call; for an agent that lists no node tool
|
|
48
|
+
* it returns before doing anything, and no native agent lists one (native
|
|
49
|
+
* refuses workflows). An `ask_user` call is an ordinary long-running call:
|
|
50
|
+
* the turn runner stores the person's next message as its function response
|
|
51
|
+
* (lib/runtime/questions.ts questionAnswerPart), and the step reads the call
|
|
52
|
+
* and the answer side by side from the history (history.ts), as ADK's
|
|
53
|
+
* content processor does. When workflows run natively (WS4), the node-tool
|
|
54
|
+
* resume is ported beside approvedCalls, in the same place in the order.
|
|
55
|
+
*
|
|
56
|
+
* CREDENTIALS (OAuth consent, WS6-3b, ADR 0085) run first, as ADK runs its
|
|
57
|
+
* auth preprocessor (AUTH_PREPROCESSOR) before request-confirmation.
|
|
58
|
+
* grantedCalls is that preprocessor, check for check, with one difference
|
|
59
|
+
* in what the answer carries:
|
|
60
|
+
* 1. The answers: the function responses named `adk_request_credential`
|
|
61
|
+
* in the last event with content, which must be the user's. None:
|
|
62
|
+
* nothing happens.
|
|
63
|
+
* 2. The requests they answer: `adk_request_credential` calls this agent
|
|
64
|
+
* made, by id. An answer naming no such request is ignored, as ADK
|
|
65
|
+
* ignores it.
|
|
66
|
+
* 3. The binding. ADK's answer carries the authorization response (a code
|
|
67
|
+
* or the redirect URL), which ADK exchanges in-process with the client
|
|
68
|
+
* secret its request event stored. Here the server's callback route has
|
|
69
|
+
* already exchanged the code and stored the grant
|
|
70
|
+
* (lib/tools/oauthConsent.ts), so the answer carries no credential:
|
|
71
|
+
* `{ credentialKey, granted: true }`, bound to the request by its
|
|
72
|
+
* credentialKey. An answer that does not bind is ignored.
|
|
73
|
+
* 4. The paused calls the bound requests name (`function_call_id`, ADK's
|
|
74
|
+
* toolset requests aside) run again, from the latest event that made
|
|
75
|
+
* them, through the loop's own call path. The tool now reads its grant
|
|
76
|
+
* through ctx.accessToken. The response is stored before the step
|
|
77
|
+
* builds its request. A later step finds the agent's own events last,
|
|
78
|
+
* so the call runs once.
|
|
79
|
+
*
|
|
80
|
+
* ADK stays out of this file: an ADK tool an agent still lists is asked
|
|
81
|
+
* whether it gates through its own checkRequireConfirmation, by shape.
|
|
82
|
+
*/
|
|
83
|
+
import type { ToolConfirmation } from '../../tools/tool.ts';
|
|
84
|
+
import type { TurnFunctionCall } from '../events.ts';
|
|
85
|
+
import type { Session } from '../sessions.ts';
|
|
86
|
+
import type { NativeAgent } from './request.ts';
|
|
87
|
+
/** Why a confirmation does not bind to the call it pins: ADK's IntentMismatchError reasons. */
|
|
88
|
+
export type IntentMismatchReason = 'untrusted_request' | 'malformed_request' | 'unknown_original_call' | 'unregistered_tool' | 'tool_name_mismatch' | 'arguments_mismatch' | 'confirmation_not_required';
|
|
89
|
+
/** A confirmation that does not bind to its pinned call: ADK's IntentMismatchError, by name and text. */
|
|
90
|
+
export declare class IntentMismatchError extends Error {
|
|
91
|
+
readonly reason: IntentMismatchReason;
|
|
92
|
+
readonly functionCallId?: string;
|
|
93
|
+
constructor(reason: IntentMismatchReason, functionCallId?: string);
|
|
94
|
+
}
|
|
95
|
+
/** The pinned calls an answer lets run, each with its confirmation, in the order the gates were stored. */
|
|
96
|
+
export interface ApprovedCalls {
|
|
97
|
+
calls: TurnFunctionCall[];
|
|
98
|
+
confirmations: Map<string, ToolConfirmation>;
|
|
99
|
+
/** The agent's tools by name, as the loop runs calls against them. */
|
|
100
|
+
tools: Map<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
interface Scope {
|
|
103
|
+
session: Session;
|
|
104
|
+
invocationId: string;
|
|
105
|
+
branch?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The pinned calls the latest user message approves or refuses, bound to
|
|
109
|
+
* the calls this agent made; undefined when it answers no open request.
|
|
110
|
+
* Throws IntentMismatchError when an answer does not bind (nothing runs).
|
|
111
|
+
* `contextFor` makes the context a tool's gate check reads.
|
|
112
|
+
*/
|
|
113
|
+
export declare function approvedCalls(agent: NativeAgent, scope: Scope, contextFor: (callId: string) => unknown): Promise<ApprovedCalls | undefined>;
|
|
114
|
+
/** The paused calls a granted consent lets run again, from the event that made them. */
|
|
115
|
+
export interface GrantedCalls {
|
|
116
|
+
calls: TurnFunctionCall[];
|
|
117
|
+
/** The agent's tools by name, as the loop runs calls against them. */
|
|
118
|
+
tools: Map<string, unknown>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The paused calls the latest user message resumes by answering this
|
|
122
|
+
* agent's credential requests; undefined when it answers none (ADK's
|
|
123
|
+
* AuthPreprocessor, the exchange left to the callback route).
|
|
124
|
+
*/
|
|
125
|
+
export declare function grantedCalls(agent: NativeAgent, scope: Scope): Promise<GrantedCalls | undefined>;
|
|
126
|
+
export {};
|