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.
Files changed (106) hide show
  1. package/CHANGELOG.md +252 -76
  2. package/DOCUMENTATION.md +44 -15
  3. package/QUICKSTART.md +9 -6
  4. package/README.md +6 -9
  5. package/config/agents/syndicateSchema.yaml +3 -0
  6. package/dist/lib/adkPeer.d.ts +63 -0
  7. package/dist/lib/adkPeer.js +240 -0
  8. package/dist/lib/compile.d.ts +40 -2
  9. package/dist/lib/compile.js +80 -12
  10. package/dist/lib/compileAdk.d.ts +7 -4
  11. package/dist/lib/compileAdk.js +22 -12
  12. package/dist/lib/compileNative.d.ts +28 -2
  13. package/dist/lib/compileNative.js +60 -4
  14. package/dist/lib/doctor.d.ts +28 -1
  15. package/dist/lib/doctor.js +63 -2
  16. package/dist/lib/index.d.ts +4 -0
  17. package/dist/lib/index.js +5 -0
  18. package/dist/lib/models/adapterResolver.d.ts +13 -11
  19. package/dist/lib/models/adapterResolver.js +11 -10
  20. package/dist/lib/models/adkGeminiAdapter.js +2 -0
  21. package/dist/lib/models/adkShim.d.ts +1 -1
  22. package/dist/lib/models/adkShim.js +1 -1
  23. package/dist/lib/models/capabilities.js +13 -10
  24. package/dist/lib/models/claudeLlm.js +2 -2
  25. package/dist/lib/models/fallback.d.ts +1 -1
  26. package/dist/lib/models/fallback.js +1 -1
  27. package/dist/lib/models/gatewayLlm.js +2 -2
  28. package/dist/lib/models/geminiAdapter.d.ts +6 -4
  29. package/dist/lib/models/geminiAdapter.js +28 -5
  30. package/dist/lib/models/geminiState.d.ts +21 -0
  31. package/dist/lib/models/geminiState.js +26 -0
  32. package/dist/lib/models/genaiMapping.d.ts +24 -4
  33. package/dist/lib/models/genaiMapping.js +109 -6
  34. package/dist/lib/models/gptLlm.js +2 -2
  35. package/dist/lib/models/grokLlm.js +2 -2
  36. package/dist/lib/models/kimiLlm.js +2 -2
  37. package/dist/lib/models/ollamaLlm.js +2 -2
  38. package/dist/lib/models/registry.d.ts +5 -1
  39. package/dist/lib/models/registry.js +17 -12
  40. package/dist/lib/models/tracedGemini.d.ts +1 -1
  41. package/dist/lib/models/tracedGemini.js +1 -1
  42. package/dist/lib/runtime/adkSessionBridge.d.ts +1 -1
  43. package/dist/lib/runtime/adkSessionBridge.js +1 -1
  44. package/dist/lib/runtime/approvals.d.ts +5 -0
  45. package/dist/lib/runtime/approvals.js +39 -8
  46. package/dist/lib/runtime/credentials.d.ts +3 -1
  47. package/dist/lib/runtime/credentials.js +6 -1
  48. package/dist/lib/runtime/native/agentLoop.d.ts +7 -1
  49. package/dist/lib/runtime/native/agentLoop.js +31 -13
  50. package/dist/lib/runtime/native/delegate.d.ts +53 -0
  51. package/dist/lib/runtime/native/delegate.js +91 -8
  52. package/dist/lib/runtime/native/interrupts.d.ts +6 -3
  53. package/dist/lib/runtime/native/interrupts.js +23 -4
  54. package/dist/lib/runtime/native/request.d.ts +1 -1
  55. package/dist/lib/runtime/native/request.js +1 -1
  56. package/dist/lib/runtime/native/selfCorrection.d.ts +64 -6
  57. package/dist/lib/runtime/native/selfCorrection.js +82 -8
  58. package/dist/lib/runtime/native/step.d.ts +14 -6
  59. package/dist/lib/runtime/native/step.js +34 -10
  60. package/dist/lib/runtime/runtimeFlag.d.ts +13 -5
  61. package/dist/lib/runtime/runtimeFlag.js +14 -6
  62. package/dist/lib/runtime/syndicateTurn.d.ts +15 -10
  63. package/dist/lib/runtime/syndicateTurn.js +59 -32
  64. package/dist/lib/runtime/valueDepth.d.ts +23 -0
  65. package/dist/lib/runtime/valueDepth.js +41 -0
  66. package/dist/lib/session/supabaseSessionService.d.ts +1 -1
  67. package/dist/lib/session/supabaseSessionService.js +1 -1
  68. package/dist/lib/session/transcript.d.ts +1 -1
  69. package/dist/lib/session/transcript.js +5 -3
  70. package/dist/lib/storage/postgres/sessionService.d.ts +1 -1
  71. package/dist/lib/storage/postgres/sessionService.js +1 -1
  72. package/dist/lib/syndicateSchema.d.ts +2 -0
  73. package/dist/lib/syndicateSchema.js +27 -6
  74. package/dist/lib/toolRegistry.d.ts +12 -1
  75. package/dist/lib/toolRegistry.js +42 -2
  76. package/dist/lib/tools/adkTool.d.ts +1 -1
  77. package/dist/lib/tools/adkTool.js +1 -1
  78. package/dist/lib/tools/collectionsSearchTool.d.ts +1 -1
  79. package/dist/lib/tools/collectionsSearchTool.js +1 -1
  80. package/dist/lib/tools/generateImageTool.d.ts +1 -1
  81. package/dist/lib/tools/inspectImageTool.d.ts +1 -1
  82. package/dist/lib/tools/urlContextTool.d.ts +1 -1
  83. package/dist/lib/tools/urlContextTool.js +1 -1
  84. package/dist/lib/tools/webExtractTool.d.ts +1 -1
  85. package/dist/lib/tools/webSearchTool.d.ts +1 -1
  86. package/dist/lib/tools/webSearchTool.js +1 -1
  87. package/dist/lib/tools/xApiSearchTool.d.ts +1 -1
  88. package/dist/lib/tools/xSearchTool.d.ts +1 -1
  89. package/dist/lib/tools/xSearchTool.js +1 -1
  90. package/dist/lib/wiki/agentRun.d.ts +1 -1
  91. package/dist/lib/wiki/agentRun.js +2 -1
  92. package/dist/lib/workflow/agentNode.d.ts +21 -4
  93. package/dist/lib/workflow/agentNode.js +96 -5
  94. package/dist/lib/workflow/graph.d.ts +5 -4
  95. package/dist/lib/workflow/graph.js +14 -4
  96. package/dist/lib/workflow/resume.d.ts +10 -9
  97. package/dist/lib/workflow/resume.js +13 -12
  98. package/dist/lib/workflow/scheduler.d.ts +10 -7
  99. package/dist/lib/workflow/scheduler.js +15 -2
  100. package/dist/lib/workflow.d.ts +33 -11
  101. package/dist/lib/workflow.js +49 -27
  102. package/dist/lib/workflowConfig.d.ts +4 -0
  103. package/dist/lib/workflowConfig.js +4 -0
  104. package/dist/scripts/doctor.d.ts +3 -1
  105. package/dist/scripts/doctor.js +4 -2
  106. package/package.json +6 -3
@@ -17,6 +17,27 @@
17
17
  export declare const GEMINI_PROVIDER = "gemini";
18
18
  /** The providerState kind for a Gemini thought signature; the payload is the signature. */
19
19
  export declare const THOUGHT_SIGNATURE_KIND = "thought_signature";
20
+ /**
21
+ * The thought signature Gemini documents for a function call that has no
22
+ * real one. Gemini 3 rejects a current-turn function call without a
23
+ * signature; this value passes its validator (ADR 0065).
24
+ */
25
+ export declare const PLACEHOLDER_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
26
+ /**
27
+ * The providerState kind the Gemini adapter writes for the Gemini parts the
28
+ * contract has no type for (code execution, server-side invocations),
29
+ * carried whole on the next output part (ADR 0065). The payload is
30
+ * `{ before: <the genai parts>, signature?: <the part's own signature> }`.
31
+ * The genai mapping writes them back out as the parts they were (ADR 0100).
32
+ */
33
+ export declare const CARRIED_PARTS_KIND = "carried_parts";
34
+ /**
35
+ * The providerState kind the genai mapping writes for a genai part the
36
+ * contract cannot hold exactly; the payload is the part, verbatim.
37
+ */
38
+ export declare const GENAI_PART_KIND = "genai_part";
39
+ /** A Gemini part the contract has no type for and Gemini wants back within the turn: code execution, a server-side invocation. */
40
+ export declare function isCarriedWirePart(part: unknown): boolean;
20
41
  /**
21
42
  * The prefix of an id the genai mapping (lib/models/genaiMapping.ts) made
22
43
  * for a call or result that had none. Such an id never goes on the wire: the
@@ -17,6 +17,32 @@
17
17
  export const GEMINI_PROVIDER = 'gemini';
18
18
  /** The providerState kind for a Gemini thought signature; the payload is the signature. */
19
19
  export const THOUGHT_SIGNATURE_KIND = 'thought_signature';
20
+ /**
21
+ * The thought signature Gemini documents for a function call that has no
22
+ * real one. Gemini 3 rejects a current-turn function call without a
23
+ * signature; this value passes its validator (ADR 0065).
24
+ */
25
+ export const PLACEHOLDER_THOUGHT_SIGNATURE = 'skip_thought_signature_validator';
26
+ /**
27
+ * The providerState kind the Gemini adapter writes for the Gemini parts the
28
+ * contract has no type for (code execution, server-side invocations),
29
+ * carried whole on the next output part (ADR 0065). The payload is
30
+ * `{ before: <the genai parts>, signature?: <the part's own signature> }`.
31
+ * The genai mapping writes them back out as the parts they were (ADR 0100).
32
+ */
33
+ export const CARRIED_PARTS_KIND = 'carried_parts';
34
+ /**
35
+ * The providerState kind the genai mapping writes for a genai part the
36
+ * contract cannot hold exactly; the payload is the part, verbatim.
37
+ */
38
+ export const GENAI_PART_KIND = 'genai_part';
39
+ /** A Gemini part the contract has no type for and Gemini wants back within the turn: code execution, a server-side invocation. */
40
+ export function isCarriedWirePart(part) {
41
+ if (typeof part !== 'object' || part === null)
42
+ return false;
43
+ const p = part;
44
+ return p.executableCode !== undefined || p.codeExecutionResult !== undefined || p.toolCall !== undefined || p.toolResponse !== undefined;
45
+ }
20
46
  /**
21
47
  * The prefix of an id the genai mapping (lib/models/genaiMapping.ts) made
22
48
  * for a call or result that had none. Such an id never goes on the wire: the
@@ -152,7 +152,7 @@
152
152
  import type { Content, ContentUnion, GenerateContentConfig, GenerateContentResponseUsageMetadata, Part as GenaiPart } from '@google/genai';
153
153
  import type { LlmRequest, LlmResponse } from '@google/adk';
154
154
  import type { Message, ModelRequest, ModelResponse, NativeTool, Part, ReasoningSetting, Sampling, ToolChoice, Usage } from './contract.ts';
155
- import { GEMINI_PROVIDER, MINTED_CALL_ID_PREFIX, THOUGHT_SIGNATURE_KIND } from './geminiState.ts';
155
+ import { GEMINI_PROVIDER, GENAI_PART_KIND, MINTED_CALL_ID_PREFIX, THOUGHT_SIGNATURE_KIND } from './geminiState.ts';
156
156
  /**
157
157
  * The provider id the mapping writes its own state under (only a Gemini
158
158
  * adapter replays it), and the providerState kind for a Gemini
@@ -160,8 +160,8 @@ import { GEMINI_PROVIDER, MINTED_CALL_ID_PREFIX, THOUGHT_SIGNATURE_KIND } from '
160
160
  * lib/models/geminiState.ts, so the Gemini adapter spells them the same.
161
161
  */
162
162
  export { GEMINI_PROVIDER, THOUGHT_SIGNATURE_KIND };
163
- /** providerState kind for a genai part the contract cannot hold exactly; the payload is the part. */
164
- export declare const GENAI_PART_KIND = "genai_part";
163
+ /** providerState kind for a genai part the contract cannot hold exactly; the payload is the part (defined in lib/models/geminiState.ts). */
164
+ export { GENAI_PART_KIND };
165
165
  /** The prefix of an id the mapping made for a call or result that had none (defined in lib/models/geminiState.ts). */
166
166
  export { MINTED_CALL_ID_PREFIX };
167
167
  /** A conversation as the contract holds it: the system prompt and the messages. */
@@ -193,6 +193,15 @@ export declare function systemText(instruction: ContentUnion | undefined): strin
193
193
  export declare function contentsToMessages(contents: readonly Content[], systemInstruction?: ContentUnion): ContractHistory;
194
194
  /** One contract part as the genai part it maps to. */
195
195
  export declare function partToGenai(part: Part): GenaiPart;
196
+ /**
197
+ * Contract parts as genai parts. A part the Gemini adapter wrote with
198
+ * `carried_parts` state (code execution, server-side invocations, ADR 0065)
199
+ * becomes the genai parts it carries, verbatim, then itself with its own
200
+ * signature as `thoughtSignature`. An empty text part that only carried them
201
+ * is left out unless it has a signature. So a stored event holds the parts as
202
+ * Gemini sent them, as ADK's Gemini stores them (ADR 0100).
203
+ */
204
+ export declare function partsToGenai(parts: readonly Part[]): GenaiPart[];
196
205
  /** One contract message as a genai content. */
197
206
  export declare function messageToContent(message: Message): Content;
198
207
  /** The inverse of contentsToMessages; the system instruction comes back as a string. */
@@ -240,8 +249,19 @@ export declare function llmRequestToModelRequest(llmRequest: LlmRequest, options
240
249
  export declare function usageToMetadata(usage: Usage): GenerateContentResponseUsageMetadata;
241
250
  /** Gemini's usageMetadata as contract Usage, or undefined when it counts nothing. */
242
251
  export declare function usageFromMetadata(meta: GenerateContentResponseUsageMetadata | undefined): Usage | undefined;
252
+ /** What a call's arguments become when they nest past MAX_VALUE_DEPTH (contractOutputPart). */
253
+ export declare const TOO_DEEP_ARGUMENTS = "[arguments nested deeper than 64 levels were dropped]";
254
+ /**
255
+ * `response` with each part held to the contract (contractOutputPart): the
256
+ * same object when every part already is, else a copy with the parts
257
+ * checked. Used by modelResponseToLlmResponse (the ADK shim's mapping) and
258
+ * by the native step, so both runtimes read and store the same answer. A
259
+ * response that is not an object is returned as it is: the caller fails on
260
+ * it as on any adapter that broke the contract.
261
+ */
262
+ export declare function contractModelResponse<R extends ModelResponse>(response: R): R;
243
263
  /** A ModelResponse as the LlmResponse ADK expects from a model (see the header). */
244
- export declare function modelResponseToLlmResponse(response: ModelResponse): LlmResponse;
264
+ export declare function modelResponseToLlmResponse(unchecked: ModelResponse): LlmResponse;
245
265
  /** The native tools in `tools` that Gemini has no tool for, each once: modelRequestToLlmRequest leaves them out. */
246
266
  export declare function nativeToolsWithoutGeminiTool(tools?: readonly NativeTool[]): NativeTool[];
247
267
  /** A ModelRequest as the LlmRequest ADK's path builds for a Gemini model (see the header for what does not come back the same). */
@@ -151,9 +151,10 @@
151
151
  */
152
152
  import { FinishReason, FunctionCallingConfigMode } from '@google/genai';
153
153
  import { ERROR_RETRYABLE_KEY, ERROR_STATUS_KEY, errorText, withRetryVerdict } from './errorResponse.js';
154
- import { GEMINI_PROVIDER, MINTED_CALL_ID_PREFIX, THOUGHT_SIGNATURE_KIND } from './geminiState.js';
154
+ import { CARRIED_PARTS_KIND, GEMINI_PROVIDER, GENAI_PART_KIND, MINTED_CALL_ID_PREFIX, THOUGHT_SIGNATURE_KIND } from './geminiState.js';
155
155
  import { reasoningConfig } from './reasoning.js';
156
156
  import { contractToolDeclaration, nativeToolOf, toContractJsonSchema } from './schemaNormalize.js';
157
+ import { MAX_VALUE_DEPTH, nestedDeeperThan } from '../runtime/valueDepth.js';
157
158
  /**
158
159
  * The provider id the mapping writes its own state under (only a Gemini
159
160
  * adapter replays it), and the providerState kind for a Gemini
@@ -161,8 +162,8 @@ import { contractToolDeclaration, nativeToolOf, toContractJsonSchema } from './s
161
162
  * lib/models/geminiState.ts, so the Gemini adapter spells them the same.
162
163
  */
163
164
  export { GEMINI_PROVIDER, THOUGHT_SIGNATURE_KIND };
164
- /** providerState kind for a genai part the contract cannot hold exactly; the payload is the part. */
165
- export const GENAI_PART_KIND = 'genai_part';
165
+ /** providerState kind for a genai part the contract cannot hold exactly; the payload is the part (defined in lib/models/geminiState.ts). */
166
+ export { GENAI_PART_KIND };
166
167
  /** The prefix of an id the mapping made for a call or result that had none (defined in lib/models/geminiState.ts). */
167
168
  export { MINTED_CALL_ID_PREFIX };
168
169
  const ALLOWED = {
@@ -431,9 +432,39 @@ export function partToGenai(part) {
431
432
  }
432
433
  return out;
433
434
  }
435
+ /**
436
+ * Contract parts as genai parts. A part the Gemini adapter wrote with
437
+ * `carried_parts` state (code execution, server-side invocations, ADR 0065)
438
+ * becomes the genai parts it carries, verbatim, then itself with its own
439
+ * signature as `thoughtSignature`. An empty text part that only carried them
440
+ * is left out unless it has a signature. So a stored event holds the parts as
441
+ * Gemini sent them, as ADK's Gemini stores them (ADR 0100).
442
+ */
443
+ export function partsToGenai(parts) {
444
+ const out = [];
445
+ for (const part of parts) {
446
+ const state = part.providerState;
447
+ if (state?.provider !== GEMINI_PROVIDER || state.kind !== CARRIED_PARTS_KIND || !isObject(state.payload)) {
448
+ out.push(partToGenai(part));
449
+ continue;
450
+ }
451
+ const { before, signature } = state.payload;
452
+ if (Array.isArray(before))
453
+ for (const carried of before)
454
+ if (isObject(carried))
455
+ out.push({ ...carried });
456
+ const { providerState: _carried, ...bare } = part;
457
+ const own = typeof signature === 'string' && signature ? signature : undefined;
458
+ if (bare.type === 'text' && !bare.text && !own)
459
+ continue;
460
+ const genai = partToGenai(bare);
461
+ out.push(own ? { ...genai, thoughtSignature: own } : genai);
462
+ }
463
+ return out;
464
+ }
434
465
  /** One contract message as a genai content. */
435
466
  export function messageToContent(message) {
436
- return { role: GENAI_ROLE[message.role], parts: message.parts.map(partToGenai) };
467
+ return { role: GENAI_ROLE[message.role], parts: partsToGenai(message.parts) };
437
468
  }
438
469
  /** The inverse of contentsToMessages; the system instruction comes back as a string. */
439
470
  export function messagesToContents(history) {
@@ -626,9 +657,81 @@ const GEMINI_FINISH_REASONS = new Set(Object.values(FinishReason).filter((r) =>
626
657
  function finishReasonOfCode(code) {
627
658
  return code !== undefined && GEMINI_FINISH_REASONS.has(code) ? code : undefined;
628
659
  }
660
+ /** What a call's arguments become when they nest past MAX_VALUE_DEPTH (contractOutputPart). */
661
+ export const TOO_DEEP_ARGUMENTS = `[arguments nested deeper than ${MAX_VALUE_DEPTH} levels were dropped]`;
662
+ /**
663
+ * One part of a model's answer as the contract allows it (lib/models/
664
+ * contract.ts), or undefined to drop it. The contract binds an adapter, and
665
+ * a model's answer is untrusted input to the runtime that stores it, so the
666
+ * runtimes hold an adapter to it here rather than store a part no reader
667
+ * expects (WS5-5, wiki/operations/native-loop-security.md):
668
+ * - a part that is not an object, of no known kind, or a tool result (an
669
+ * answer holds none) is dropped;
670
+ * - a text or thinking part whose text is not a string is dropped;
671
+ * - a tool call's name and id that are not strings become `''` (the
672
+ * runtime then mints the id), and its arguments that are not an object
673
+ * become `{}` when absent or null, else `{ raw: <value> }`, the
674
+ * contract's form for arguments that do not parse;
675
+ * - arguments that nest deeper than MAX_VALUE_DEPTH levels (lib/runtime/
676
+ * valueDepth.ts) become `{ raw: TOO_DEEP_ARGUMENTS }`: every later
677
+ * reader of the session would overflow its stack on them;
678
+ * - a blob needs a string mimeType and a string `data` or `url`;
679
+ * - a Gemini part carried whole must be an object.
680
+ * A part the contract allows is returned as it is, so a well-behaved
681
+ * adapter's answer maps exactly as before.
682
+ */
683
+ function contractOutputPart(part) {
684
+ if (!isObject(part))
685
+ return undefined;
686
+ const state = part.providerState;
687
+ if (state?.provider === GEMINI_PROVIDER && state.kind === GENAI_PART_KIND && !isObject(state.payload))
688
+ return undefined;
689
+ switch (part.type) {
690
+ case 'text':
691
+ case 'thinking':
692
+ return typeof part.text === 'string' ? part : undefined;
693
+ case 'toolCall': {
694
+ const { name, id, args } = part;
695
+ const tooDeep = nestedDeeperThan(args);
696
+ if (typeof name === 'string' && typeof id === 'string' && (isObject(args) || Array.isArray(args)) && !tooDeep)
697
+ return part;
698
+ const checkedArgs = tooDeep
699
+ ? { raw: TOO_DEEP_ARGUMENTS }
700
+ : isObject(args) || Array.isArray(args)
701
+ ? args
702
+ : args === undefined || args === null
703
+ ? {}
704
+ : { raw: args };
705
+ return { ...part, type: 'toolCall', name: typeof name === 'string' ? name : '', id: typeof id === 'string' ? id : '', args: checkedArgs };
706
+ }
707
+ case 'blob':
708
+ return typeof part.mimeType === 'string' && (typeof part.data === 'string' || typeof part.url === 'string') ? part : undefined;
709
+ default:
710
+ return undefined;
711
+ }
712
+ }
713
+ /**
714
+ * `response` with each part held to the contract (contractOutputPart): the
715
+ * same object when every part already is, else a copy with the parts
716
+ * checked. Used by modelResponseToLlmResponse (the ADK shim's mapping) and
717
+ * by the native step, so both runtimes read and store the same answer. A
718
+ * response that is not an object is returned as it is: the caller fails on
719
+ * it as on any adapter that broke the contract.
720
+ */
721
+ export function contractModelResponse(response) {
722
+ if (!isObject(response))
723
+ return response;
724
+ const raw = response.parts;
725
+ const parts = Array.isArray(raw) ? raw : [];
726
+ const checked = parts.map(contractOutputPart);
727
+ if (Array.isArray(raw) && checked.every((p, i) => p === parts[i]))
728
+ return response;
729
+ return { ...response, parts: checked.filter((p) => p !== undefined) };
730
+ }
629
731
  /** A ModelResponse as the LlmResponse ADK expects from a model (see the header). */
630
- export function modelResponseToLlmResponse(response) {
631
- const parts = response.parts.map(partToGenai);
732
+ export function modelResponseToLlmResponse(unchecked) {
733
+ const response = contractModelResponse(unchecked);
734
+ const parts = partsToGenai(response.parts);
632
735
  if (response.partial)
633
736
  return { content: { role: 'model', parts }, partial: true };
634
737
  const finishReason = finishReasonOfCode(response.error?.code) ?? FINISH_REASONS[response.finishReason];
@@ -33,7 +33,7 @@
33
33
  * The LlmRequest builders below (buildResponsesInput, buildResponsesTools)
34
34
  * are the adapter's own, run on the LlmRequest mapped to the contract.
35
35
  */
36
- import { LLMRegistry } from '@google/adk';
36
+ import { requireAdk } from '../adkPeer.js';
37
37
  import { AdkShim } from './adkShim.js';
38
38
  import { llmRequestToModelRequest, reasoningOf } from './genaiMapping.js';
39
39
  import { GptAdapter, responsesFunctionTools, responsesInput, responsesServerTools } from './gptAdapter.js';
@@ -125,5 +125,5 @@ export class GptLlm extends AdkShim {
125
125
  * registerAvailableProviders() when OPENAI_API_KEY is present.
126
126
  */
127
127
  export function registerGptLlm() {
128
- LLMRegistry.register(GptLlm);
128
+ requireAdk("Registering a model class with ADK's LLMRegistry").LLMRegistry.register(GptLlm);
129
129
  }
@@ -34,7 +34,7 @@
34
34
  * "grok-4.3"), which may differ from the requested id; invalid ids are
35
35
  * rejected with "Model not found".
36
36
  */
37
- import { LLMRegistry } from '@google/adk';
37
+ import { requireAdk } from '../adkPeer.js';
38
38
  import { GptLlm } from './gptLlm.js';
39
39
  import { GrokAdapter } from './grokAdapter.js';
40
40
  export { DEFAULT_GROK_TIMEOUT_MS, grokTimeoutMs } from './grokAdapter.js';
@@ -52,5 +52,5 @@ export class GrokLlm extends GptLlm {
52
52
  * registerAvailableProviders() when XAI_API_KEY is present.
53
53
  */
54
54
  export function registerGrokLlm() {
55
- LLMRegistry.register(GrokLlm);
55
+ requireAdk("Registering a model class with ADK's LLMRegistry").LLMRegistry.register(GrokLlm);
56
56
  }
@@ -15,7 +15,7 @@
15
15
  * this path an agent's older spelling `reasoningEffort: max` still reaches
16
16
  * K3, which the contract has no level for.
17
17
  */
18
- import { LLMRegistry } from '@google/adk';
18
+ import { requireAdk } from '../adkPeer.js';
19
19
  import { KimiAdapter } from './kimiAdapter.js';
20
20
  import { OpenAiCompatibleLlm } from './openAiCompatibleLlm.js';
21
21
  export { MOONSHOT_BASE_URL, isKimiK3, wantsReasoningReplay } from './kimiAdapter.js';
@@ -31,5 +31,5 @@ export class KimiLlm extends OpenAiCompatibleLlm {
31
31
  // ── Registration helper ───────────────────────────────────────────────────────
32
32
  /** Registers KimiLlm with the ADK LLMRegistry (called when MOONSHOT_API_KEY is set). */
33
33
  export function registerKimiLlm() {
34
- LLMRegistry.register(KimiLlm);
34
+ requireAdk("Registering a model class with ADK's LLMRegistry").LLMRegistry.register(KimiLlm);
35
35
  }
@@ -12,7 +12,7 @@
12
12
  * supportedModels are what they were before the adapter moved onto the
13
13
  * contract, so lib/models/registry.ts and every caller are unchanged.
14
14
  */
15
- import { LLMRegistry } from '@google/adk';
15
+ import { requireAdk } from '../adkPeer.js';
16
16
  import { OllamaAdapter } from './ollamaAdapter.js';
17
17
  import { OpenAiCompatibleLlm } from './openAiCompatibleLlm.js';
18
18
  // ── OllamaLlm ─────────────────────────────────────────────────────────────────
@@ -38,5 +38,5 @@ export class OllamaLlm extends OpenAiCompatibleLlm {
38
38
  * OLLAMA_UNREACHABLE message that says how to start it.
39
39
  */
40
40
  export function registerOllamaLlm() {
41
- LLMRegistry.register(OllamaLlm);
41
+ requireAdk("Registering a model class with ADK's LLMRegistry").LLMRegistry.register(OllamaLlm);
42
42
  }
@@ -84,6 +84,10 @@ export declare function providerStatuses(): ProviderStatus[];
84
84
  * provider whose direct key is ABSENT is served by the gateway stand-in
85
85
  * instead (lib/models/gateway.ts — the fallback rule). Returns the
86
86
  * per-provider statuses so entrypoints can log which models are routable.
87
+ *
88
+ * Without @google/adk installed there is no LLMRegistry to register into:
89
+ * the native runtime resolves every id through resolveAdapter, so nothing is
90
+ * registered and the statuses are returned as they are (ADR 0102).
87
91
  */
88
92
  export declare function registerAvailableProviders(log?: (msg: string) => void): ProviderStatus[];
89
93
  export interface ResolveModelOptions {
@@ -109,7 +113,7 @@ export interface ResolveModelOptions {
109
113
  * `defaultProvider` (or Gemini).
110
114
  */
111
115
  export declare function resolveModel(model: string | undefined, options?: ResolveModelOptions): BaseLlm;
112
- /** GEMINI_ADAPTER (`adk`, the default here, or `engine`). Any other value is a configuration error. */
116
+ /** GEMINI_ADAPTER (`engine`, the default, or `adk`). Any other value is a configuration error. */
113
117
  export declare function geminiAdapterChoice(env?: NodeJS.ProcessEnv): GeminiAdapterChoice;
114
118
  /**
115
119
  * Resolves a model id to the engine's own ModelAdapter (ADR 0048), from the
@@ -40,7 +40,7 @@
40
40
  * Register BEFORE constructing any agent: the LLMRegistry caches model→class
41
41
  * resolution (LRU), so late registration can be masked by stale cache hits.
42
42
  */
43
- import { Gemini, LLMRegistry } from '@google/adk';
43
+ import { Gemini, adkInstalled, requireAdk } from '../adkPeer.js';
44
44
  import { endpointFromEnv, endpointProblems, providerReady } from './endpoints.js';
45
45
  import { DEFAULT_CLAUDE_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_GPT_MODEL, DEFAULT_GROK_MODEL, DEFAULT_KIMI_MODEL, DEFAULT_OLLAMA_MODEL, } from '../config.js';
46
46
  import { AdkGeminiAdapter } from './adkGeminiAdapter.js';
@@ -98,7 +98,7 @@ export function providerStatuses() {
98
98
  });
99
99
  }
100
100
  const REGISTRARS = {
101
- gemini: () => LLMRegistry.register(TracedGemini),
101
+ gemini: () => requireAdk("Registering a model class with ADK's LLMRegistry").LLMRegistry.register(TracedGemini),
102
102
  anthropic: registerClaudeLlm,
103
103
  openai: registerGptLlm,
104
104
  xai: registerGrokLlm,
@@ -113,13 +113,13 @@ const REGISTRARS = {
113
113
  * are registered at import time — replacing them is how TracedGemini works
114
114
  * too.
115
115
  */
116
- const PATTERNS = {
116
+ const PATTERNS = () => ({
117
117
  gemini: Gemini.supportedModels,
118
118
  anthropic: ClaudeLlm.supportedModels,
119
119
  openai: GptLlm.supportedModels,
120
120
  xai: GrokLlm.supportedModels,
121
121
  moonshot: KimiLlm.supportedModels,
122
- };
122
+ });
123
123
  let providersRegistered = false;
124
124
  /**
125
125
  * Registers every adapter whose credentials exist into the ADK LLMRegistry
@@ -129,14 +129,18 @@ let providersRegistered = false;
129
129
  * provider whose direct key is ABSENT is served by the gateway stand-in
130
130
  * instead (lib/models/gateway.ts — the fallback rule). Returns the
131
131
  * per-provider statuses so entrypoints can log which models are routable.
132
+ *
133
+ * Without @google/adk installed there is no LLMRegistry to register into:
134
+ * the native runtime resolves every id through resolveAdapter, so nothing is
135
+ * registered and the statuses are returned as they are (ADR 0102).
132
136
  */
133
137
  export function registerAvailableProviders(log) {
134
138
  const statuses = providerStatuses();
135
- if (!providersRegistered) {
139
+ if (!providersRegistered && adkInstalled()) {
136
140
  providersRegistered = true;
137
141
  for (const status of statuses) {
138
142
  if (status.transport === 'gateway' && status.provider !== 'ollama') {
139
- registerGatewayLlm(PATTERNS[status.provider]);
143
+ registerGatewayLlm(PATTERNS()[status.provider]);
140
144
  }
141
145
  else if (status.available || status.provider === 'gemini') {
142
146
  REGISTRARS[status.provider]();
@@ -213,16 +217,17 @@ export function resolveModel(model, options = {}) {
213
217
  return route.transport === 'gateway' ? new GatewayLlm({ model: resolved }) : ADK_LLM[route.provider](route);
214
218
  }
215
219
  // ── Contract adapters (the native runtime) ───────────────────────────────────
216
- /** GEMINI_ADAPTER (`adk`, the default here, or `engine`). Any other value is a configuration error. */
220
+ /** GEMINI_ADAPTER (`engine`, the default, or `adk`). Any other value is a configuration error. */
217
221
  export function geminiAdapterChoice(env = process.env) {
218
- return geminiAdapterSetting(env) ?? 'adk';
222
+ return geminiAdapterSetting(env) ?? 'engine';
219
223
  }
220
224
  /**
221
225
  * The contract resolver with the temporary AdkGeminiAdapter (ADR 0060): a
222
- * Gemini id gets it until gate G3, or the engine's GeminiAdapter when
223
- * GEMINI_ADAPTER=engine or `{ gemini: 'engine' }` asks for it. The
224
- * ADK-free resolver in lib/models/adapterResolver.ts, which
225
- * `melchizedek-agents/model` exports, defaults to GeminiAdapter (ADR 0068).
226
+ * Gemini id gets the engine's GeminiAdapter (the default since 0.20.0, ADR
227
+ * 0100), or AdkGeminiAdapter when GEMINI_ADAPTER=adk or `{ gemini: 'adk' }`
228
+ * asks for it, for one release, which needs @google/adk. The ADK-free
229
+ * resolver in lib/models/adapterResolver.ts, which `melchizedek-agents/model`
230
+ * exports, refuses `adk` (ADR 0068).
226
231
  */
227
232
  const WITH_ADK_GEMINI = adapterResolver((r) => new AdkGeminiAdapter(r));
228
233
  /**
@@ -11,7 +11,7 @@
11
11
  * cycle. registry.ts re-exports it, so `TracedGemini` imports from there keep
12
12
  * working.
13
13
  */
14
- import { Gemini } from '@google/adk';
14
+ import { Gemini } from '../adkPeer.ts';
15
15
  import type { GeminiParams, LlmRequest, LlmResponse } from '@google/adk';
16
16
  import type { ProviderEndpoint } from './endpoints.ts';
17
17
  export declare class TracedGemini extends Gemini {
@@ -11,7 +11,7 @@
11
11
  * cycle. registry.ts re-exports it, so `TracedGemini` imports from there keep
12
12
  * working.
13
13
  */
14
- import { Gemini } from '@google/adk';
14
+ import { Gemini } from '../adkPeer.js';
15
15
  import { endpointFromEnv, platformModel } from './endpoints.js';
16
16
  import { llmRequestToModelRequest } from './genaiMapping.js';
17
17
  import { errorStatus, retryUntilFirstYield } from './retry.js';
@@ -31,7 +31,7 @@
31
31
  * This module loads ADK at run time, so it is not one of the engine's leaf
32
32
  * modules (tests/events.test.ts names those).
33
33
  */
34
- import { BaseSessionService } from '@google/adk';
34
+ import { BaseSessionService } from '../adkPeer.ts';
35
35
  import type { CreateSessionRequest as AdkCreateSessionRequest, DeleteSessionRequest as AdkDeleteSessionRequest, Event as AdkEvent, GetSessionRequest as AdkGetSessionRequest, ListSessionsRequest as AdkListSessionsRequest, ListSessionsResponse as AdkListSessionsResponse, Session as AdkSession } from '@google/adk';
36
36
  import type { TurnEvent } from './events.ts';
37
37
  import type { CreateSessionRequest, GetSessionOptions, ListSessionsRequest, ListSessionsResult, Session, SessionKey, SessionService } from './sessions.ts';
@@ -31,7 +31,7 @@
31
31
  * This module loads ADK at run time, so it is not one of the engine's leaf
32
32
  * modules (tests/events.test.ts names those).
33
33
  */
34
- import { BaseSessionService } from '@google/adk';
34
+ import { BaseSessionService } from '../adkPeer.js';
35
35
  import { applyEvent, listPage, listWindow, selectEvents, withoutTempKeys } from './sessions.js';
36
36
  /** Every method the engine's interface names. */
37
37
  export function isSessionService(value) {
@@ -29,6 +29,11 @@ export interface PendingApproval {
29
29
  * The approval request still waiting for an answer, or undefined. A request
30
30
  * the user moved on from (a new text message after it) is not pending: its
31
31
  * call never runs.
32
+ *
33
+ * In a workflow (ADR 0098) the walk stores each node's input as a user text
34
+ * turn, so a sibling node's input may follow a gated node's request. The
35
+ * walk's pause record, stored last, names every interrupt it left open: a
36
+ * request it names is pending whatever node turns came after it.
32
37
  */
33
38
  export declare function pendingApproval(events: readonly Event[]): PendingApproval | undefined;
34
39
  /** Index of the user message that started the turn holding request `id` (its events are replayed raw on resume). */
@@ -4,15 +4,53 @@ const partsOf = (e) => (e.content?.parts ?? []);
4
4
  function hasUserText(e) {
5
5
  return e.author === 'user' && partsOf(e).some((p) => typeof p.text === 'string' && p.text.trim() && !p.thought);
6
6
  }
7
+ /**
8
+ * A workflow's pause record (lib/workflow/pause.ts workflowPauseEvent, ADK's
9
+ * recordInputForResume): no content, every interrupt the walk left open in
10
+ * `longRunningToolIds`, at the workflow's own path. Its ids, else undefined.
11
+ */
12
+ function workflowPauseIds(e) {
13
+ if (e.author === 'user' || partsOf(e).length > 0)
14
+ return undefined;
15
+ const path = e.nodeInfo?.path;
16
+ if (typeof path !== 'string' || !path || path.includes('.'))
17
+ return undefined;
18
+ return e.longRunningToolIds?.length ? [...e.longRunningToolIds] : undefined;
19
+ }
20
+ const requestOf = (e, call) => {
21
+ const original = call.args?.originalFunctionCall ?? {};
22
+ return {
23
+ id: call.id,
24
+ agent: e.author ?? '',
25
+ tool: String(original.name ?? ''),
26
+ args: (original.args ?? {}),
27
+ ...(original.id ? { callId: String(original.id) } : {}),
28
+ };
29
+ };
7
30
  /**
8
31
  * The approval request still waiting for an answer, or undefined. A request
9
32
  * the user moved on from (a new text message after it) is not pending: its
10
33
  * call never runs.
34
+ *
35
+ * In a workflow (ADR 0098) the walk stores each node's input as a user text
36
+ * turn, so a sibling node's input may follow a gated node's request. The
37
+ * walk's pause record, stored last, names every interrupt it left open: a
38
+ * request it names is pending whatever node turns came after it.
11
39
  */
12
40
  export function pendingApproval(events) {
13
41
  const answered = new Set();
14
42
  for (let i = events.length - 1; i >= 0; i--) {
15
43
  const e = events[i];
44
+ const paused = workflowPauseIds(e);
45
+ if (paused) {
46
+ const open = new Set(paused.filter((id) => !answered.has(id)));
47
+ for (let j = i - 1; j >= 0 && open.size > 0; j--) {
48
+ const request = partsOf(events[j]).find((p) => p.functionCall?.name === APPROVAL_REQUEST && open.has(p.functionCall.id));
49
+ if (request)
50
+ return requestOf(events[j], request.functionCall);
51
+ }
52
+ return undefined;
53
+ }
16
54
  if (hasUserText(e))
17
55
  return undefined;
18
56
  for (const p of partsOf(e)) {
@@ -23,14 +61,7 @@ export function pendingApproval(events) {
23
61
  const call = p.functionCall;
24
62
  if (call?.name !== APPROVAL_REQUEST || !call.id || answered.has(call.id))
25
63
  continue;
26
- const original = call.args?.originalFunctionCall ?? {};
27
- return {
28
- id: call.id,
29
- agent: e.author ?? '',
30
- tool: String(original.name ?? ''),
31
- args: (original.args ?? {}),
32
- ...(original.id ? { callId: String(original.id) } : {}),
33
- };
64
+ return requestOf(e, call);
34
65
  }
35
66
  }
36
67
  return undefined;
@@ -46,7 +46,9 @@ export declare function consentFrom(author: string | undefined, call: {
46
46
  /**
47
47
  * The consent request still waiting in a session, or undefined. One that is
48
48
  * answered (a function response with its id), or that the person moved past
49
- * with a text message, is not pending.
49
+ * with a text message, is not pending. Only an agent asks: a request in an
50
+ * event the user authored is none, so a forged request never makes the next
51
+ * message a grant (as pendingQuestion reads ask_user).
50
52
  */
51
53
  export declare function pendingConsent(events: readonly Event[]): PendingConsent | undefined;
52
54
  /**
@@ -27,7 +27,9 @@ export function consentFrom(author, call) {
27
27
  /**
28
28
  * The consent request still waiting in a session, or undefined. One that is
29
29
  * answered (a function response with its id), or that the person moved past
30
- * with a text message, is not pending.
30
+ * with a text message, is not pending. Only an agent asks: a request in an
31
+ * event the user authored is none, so a forged request never makes the next
32
+ * message a grant (as pendingQuestion reads ask_user).
31
33
  */
32
34
  export function pendingConsent(events) {
33
35
  const answered = new Set();
@@ -39,6 +41,9 @@ export function pendingConsent(events) {
39
41
  if (p.functionResponse?.name === CREDENTIAL_REQUEST && p.functionResponse.id)
40
42
  answered.add(p.functionResponse.id);
41
43
  }
44
+ // Only an agent asks: a credential request in an event the user authored is none, as pendingQuestion reads ask_user (WS5-5).
45
+ if (e.author === 'user')
46
+ continue;
42
47
  for (const p of partsOf(e)) {
43
48
  const call = p.functionCall;
44
49
  if (call?.name !== CREDENTIAL_REQUEST || !call.id || answered.has(call.id))
@@ -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
@@ -189,6 +193,8 @@ export interface AgentLoopEnd {
189
193
  * when it does not validate).
190
194
  */
191
195
  export declare function saveOutput(agent: Pick<NativeAgent, 'name' | 'outputKey' | 'outputSchema'>, event: TurnEvent): void;
196
+ /** What a tool's result becomes when it nests past MAX_VALUE_DEPTH (ADR 0101). */
197
+ export declare const TOO_DEEP_RESULT: (toolName: string) => string;
192
198
  /**
193
199
  * Runs `agent` on the session in `ctx` until its answer is final, yielding
194
200
  * every event in order: partials as they arrive (never stored), then each