melchizedek-agents 0.9.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 (111) hide show
  1. package/AGENT_SETUP.md +182 -0
  2. package/DOCUMENTATION.md +614 -0
  3. package/LICENSE +21 -0
  4. package/QUICKSTART.md +164 -0
  5. package/README.md +151 -0
  6. package/config/agents/README.md +27 -0
  7. package/config/agents/examples/ares.yaml +74 -0
  8. package/config/agents/examples/cartographers.yaml +115 -0
  9. package/config/agents/examples/claude.yaml +17 -0
  10. package/config/agents/examples/council.yaml +65 -0
  11. package/config/agents/examples/critic.yaml +74 -0
  12. package/config/agents/examples/delegation.yaml +29 -0
  13. package/config/agents/examples/hierarchical.yaml +26 -0
  14. package/config/agents/examples/image_production.yaml +112 -0
  15. package/config/agents/examples/librarian.yaml +75 -0
  16. package/config/agents/examples/model_zoo.yaml +81 -0
  17. package/config/agents/examples/patient_advocate.yaml +107 -0
  18. package/config/agents/examples/scriptorium.yaml +96 -0
  19. package/config/agents/examples/style_council.yaml +83 -0
  20. package/config/agents/examples/syndicate.yaml +34 -0
  21. package/config/agents/examples/tutor.yaml +66 -0
  22. package/config/agents/syndicateSchema.yaml +291 -0
  23. package/dist/lib/config.d.ts +108 -0
  24. package/dist/lib/config.js +110 -0
  25. package/dist/lib/dispatch.d.ts +133 -0
  26. package/dist/lib/dispatch.js +182 -0
  27. package/dist/lib/grounding.d.ts +28 -0
  28. package/dist/lib/grounding.js +61 -0
  29. package/dist/lib/index.d.ts +14 -0
  30. package/dist/lib/index.js +13 -0
  31. package/dist/lib/loadEnv.d.ts +21 -0
  32. package/dist/lib/loadEnv.js +58 -0
  33. package/dist/lib/loadSyndicate.d.ts +177 -0
  34. package/dist/lib/loadSyndicate.js +264 -0
  35. package/dist/lib/memory/supabaseMemoryService.d.ts +118 -0
  36. package/dist/lib/memory/supabaseMemoryService.js +499 -0
  37. package/dist/lib/models/claudeLlm.d.ts +93 -0
  38. package/dist/lib/models/claudeLlm.js +332 -0
  39. package/dist/lib/models/gptLlm.d.ts +110 -0
  40. package/dist/lib/models/gptLlm.js +497 -0
  41. package/dist/lib/models/grokLlm.d.ts +64 -0
  42. package/dist/lib/models/grokLlm.js +86 -0
  43. package/dist/lib/models/ollamaLlm.d.ts +105 -0
  44. package/dist/lib/models/ollamaLlm.js +134 -0
  45. package/dist/lib/models/openAiCompatibleLlm.d.ts +125 -0
  46. package/dist/lib/models/openAiCompatibleLlm.js +331 -0
  47. package/dist/lib/models/providerMap.d.ts +28 -0
  48. package/dist/lib/models/providerMap.js +45 -0
  49. package/dist/lib/models/registry.d.ts +71 -0
  50. package/dist/lib/models/registry.js +159 -0
  51. package/dist/lib/models/schemaNormalize.d.ts +22 -0
  52. package/dist/lib/models/schemaNormalize.js +58 -0
  53. package/dist/lib/observability/supabaseSpanExporter.d.ts +35 -0
  54. package/dist/lib/observability/supabaseSpanExporter.js +115 -0
  55. package/dist/lib/observability/tracer.d.ts +44 -0
  56. package/dist/lib/observability/tracer.js +242 -0
  57. package/dist/lib/persistence/supabaseProvider.d.ts +44 -0
  58. package/dist/lib/persistence/supabaseProvider.js +88 -0
  59. package/dist/lib/session/supabaseSessionService.d.ts +26 -0
  60. package/dist/lib/session/supabaseSessionService.js +193 -0
  61. package/dist/lib/session/transcript.d.ts +193 -0
  62. package/dist/lib/session/transcript.js +309 -0
  63. package/dist/lib/toolRegistry.d.ts +15 -0
  64. package/dist/lib/toolRegistry.js +58 -0
  65. package/dist/lib/tools/collectionsSearchTool.d.ts +55 -0
  66. package/dist/lib/tools/collectionsSearchTool.js +86 -0
  67. package/dist/lib/tools/generateImageTool.d.ts +16 -0
  68. package/dist/lib/tools/generateImageTool.js +119 -0
  69. package/dist/lib/tools/inspectImageTool.d.ts +21 -0
  70. package/dist/lib/tools/inspectImageTool.js +97 -0
  71. package/dist/lib/tools/mcpToolFactory.d.ts +2 -0
  72. package/dist/lib/tools/mcpToolFactory.js +116 -0
  73. package/dist/lib/tools/toolContract.d.ts +78 -0
  74. package/dist/lib/tools/toolContract.js +129 -0
  75. package/dist/lib/tools/webExtractTool.d.ts +70 -0
  76. package/dist/lib/tools/webExtractTool.js +461 -0
  77. package/dist/lib/tools/webSearchTool.d.ts +53 -0
  78. package/dist/lib/tools/webSearchTool.js +126 -0
  79. package/dist/lib/tools/wikiTools.d.ts +112 -0
  80. package/dist/lib/tools/wikiTools.js +697 -0
  81. package/dist/lib/tools/xSearchTool.d.ts +61 -0
  82. package/dist/lib/tools/xSearchTool.js +125 -0
  83. package/dist/lib/wiki/agentRun.d.ts +39 -0
  84. package/dist/lib/wiki/agentRun.js +79 -0
  85. package/dist/lib/wiki/builder.d.ts +71 -0
  86. package/dist/lib/wiki/builder.js +165 -0
  87. package/dist/lib/wiki/dive.d.ts +56 -0
  88. package/dist/lib/wiki/dive.js +208 -0
  89. package/dist/lib/wiki/entities.d.ts +220 -0
  90. package/dist/lib/wiki/entities.js +661 -0
  91. package/dist/lib/wiki/extract.d.ts +97 -0
  92. package/dist/lib/wiki/extract.js +305 -0
  93. package/dist/lib/wiki/fill.d.ts +37 -0
  94. package/dist/lib/wiki/fill.js +175 -0
  95. package/dist/lib/wiki/format.d.ts +132 -0
  96. package/dist/lib/wiki/format.js +177 -0
  97. package/dist/lib/wiki/graph.d.ts +80 -0
  98. package/dist/lib/wiki/graph.js +147 -0
  99. package/dist/lib/wiki/lint.d.ts +58 -0
  100. package/dist/lib/wiki/lint.js +267 -0
  101. package/dist/lib/wiki/markdown.d.ts +127 -0
  102. package/dist/lib/wiki/markdown.js +336 -0
  103. package/dist/lib/wiki/pipeline.d.ts +69 -0
  104. package/dist/lib/wiki/pipeline.js +119 -0
  105. package/dist/lib/wiki/vault.d.ts +84 -0
  106. package/dist/lib/wiki/vault.js +194 -0
  107. package/dist/scripts/a2a_server.d.ts +2 -0
  108. package/dist/scripts/a2a_server.js +997 -0
  109. package/dist/scripts/syndicate_chat.d.ts +2 -0
  110. package/dist/scripts/syndicate_chat.js +485 -0
  111. package/package.json +96 -0
@@ -0,0 +1,177 @@
1
+ import type { DispatchConfig } from './dispatch.ts';
2
+ export type { DispatchConfig } from './dispatch.ts';
3
+ export interface ThinkingConfig {
4
+ /** Token budget for internal reasoning. 0 = off, -1 = dynamic. */
5
+ thinkingBudget?: number;
6
+ /** Stream the thinking trace in the response. */
7
+ includeThoughts?: boolean;
8
+ }
9
+ export interface SafetySetting {
10
+ category: string;
11
+ threshold: string;
12
+ }
13
+ /**
14
+ * Maps to GenerateContentConfig from @google/genai.
15
+ * Controls model sampling and generation behaviour.
16
+ * ⚠ Do NOT set `tools` here — use the top-level `tools` field on the agent.
17
+ * ⚠ Do NOT set `thinkingConfig` via the tools param — use this block.
18
+ */
19
+ export interface GenerateContentConfig {
20
+ temperature?: number;
21
+ topP?: number;
22
+ topK?: number;
23
+ maxOutputTokens?: number;
24
+ stopSequences?: string[];
25
+ candidateCount?: number;
26
+ presencePenalty?: number;
27
+ frequencyPenalty?: number;
28
+ seed?: number;
29
+ responseMimeType?: string;
30
+ safetySettings?: SafetySetting[];
31
+ thinkingConfig?: ThinkingConfig;
32
+ }
33
+ /**
34
+ * Maps to LlmAgentConfig (+ BaseAgentConfig) from @google/adk.
35
+ */
36
+ export interface AgentYamlConfig {
37
+ /** Unique agent name. Must be a valid JS identifier. Cannot be "user". */
38
+ name: string;
39
+ /** One-line description used by other agents to decide when to delegate here. */
40
+ description?: string;
41
+ /** Gemini model identifier. Subagents inherit this if not set. */
42
+ model: string;
43
+ /** System prompt / persona. Maps to LlmAgentConfig.instruction. */
44
+ instruction: string;
45
+ /**
46
+ * Global instruction applied to ALL agents in the tree.
47
+ * Only the root agent's value takes effect.
48
+ */
49
+ globalInstruction?: string;
50
+ /**
51
+ * Named built-in tools to attach to this agent.
52
+ * Supported: "google_search", "code_execution"
53
+ * Maps to LlmAgentConfig.tools[].
54
+ */
55
+ tools?: string[];
56
+ /**
57
+ * Whether conversation history is included in model requests.
58
+ * "default" = stateful, "none" = stateless.
59
+ * Maps to LlmAgentConfig.includeContents.
60
+ */
61
+ includeContents?: 'default' | 'none';
62
+ /** Prevents LLM from transferring control back to parent agent. */
63
+ disallowTransferToParent?: boolean;
64
+ /** Prevents LLM from transferring control to peer agents. */
65
+ disallowTransferToPeers?: boolean;
66
+ /**
67
+ * Saves final reply to session state under this key.
68
+ * Useful for chaining agents.
69
+ */
70
+ outputKey?: string;
71
+ /**
72
+ * Model generation config (temperature, topP, safety, thinking, etc.)
73
+ * Maps to LlmAgentConfig.generateContentConfig.
74
+ */
75
+ generateContentConfig?: GenerateContentConfig;
76
+ /**
77
+ * Output schema for structured JSON responses.
78
+ * Maps to LlmAgentConfig.outputSchema.
79
+ */
80
+ outputSchema?: Record<string, unknown>;
81
+ mcp_server_url?: string;
82
+ orchestration?: {
83
+ role?: 'primary' | 'sub-agent';
84
+ delegates?: string[];
85
+ };
86
+ }
87
+ export interface SubagentYamlConfig extends Partial<AgentYamlConfig> {
88
+ /** Required when used as AgentTool — orchestrator reads this to delegate. */
89
+ name: string;
90
+ description: string;
91
+ yaml_reference?: string;
92
+ }
93
+ export type VariableMap = Record<string, string | number | boolean>;
94
+ export interface SyndicateYamlConfig {
95
+ syndicate_name: string;
96
+ orchestrator: AgentYamlConfig;
97
+ subagents: SubagentYamlConfig[];
98
+ /**
99
+ * Opts this syndicate into PLAN-DISPATCH orchestration: the orchestrator is
100
+ * a tool-less classifier whose `outputSchema` names one route, and code runs
101
+ * that subagent directly so its output is the answer — no relay turn.
102
+ * Absent = classic DELEGATE mode (subagents as AgentTools).
103
+ * See lib/dispatch.ts for the full contract.
104
+ */
105
+ dispatch?: DispatchConfig;
106
+ /** Optional default variable definitions for {{token}} interpolation. */
107
+ variables?: VariableMap;
108
+ /** Defines the persistence and semantic memory layer for the syndicate. */
109
+ memory_system?: 'internal-only' | 'session-only' | 'long-term';
110
+ /**
111
+ * Domain rules appended to the shared fact-extraction prompt for THIS
112
+ * syndicate only. The prompt is global (every long-term memory consumer
113
+ * shares it), so anything domain-specific — "never store a market quote" —
114
+ * belongs here rather than edited into the prompt itself.
115
+ * Read by the A2A server at ingestion time; ignored without `long-term`.
116
+ */
117
+ memory_extraction_rules?: string;
118
+ /** Optional hard technical limit for maximum ADK runner loops (LLM -> Tool cycles). */
119
+ max_steps?: number;
120
+ }
121
+ /**
122
+ * Options for `loadSyndicate()`.
123
+ */
124
+ export interface LoadSyndicateOptions {
125
+ /** Variable bindings that resolve `{{token}}` placeholders in the YAML. */
126
+ bindings?: VariableMap;
127
+ /**
128
+ * Direct property overrides applied after binding interpolation.
129
+ */
130
+ overrides?: Partial<SyndicateYamlConfig>;
131
+ /**
132
+ * Root directory the loader is jailed to. Resolution order:
133
+ * this option → `MELCHIZEDEK_AGENTS_DIR` env → `<cwd>/config/agents`.
134
+ * Lets a consumer repo keep its syndicates anywhere (the engine is a
135
+ * package, not a place). NOTE: nested `yaml_reference:` loads made by
136
+ * callers that don't thread options through resolve via env/default,
137
+ * so a non-default root is best set process-wide via the env var.
138
+ */
139
+ agentsDir?: string;
140
+ }
141
+ /**
142
+ * Parse CLI variable bindings into a VariableMap.
143
+ *
144
+ * Supports two formats:
145
+ * --bindings '{"focus_area": "AI Regulation", "headline_count": 10}'
146
+ * --bind key=value (individual overrides, take priority over --bindings)
147
+ */
148
+ export declare function parseCliBindings(argv: string[]): VariableMap;
149
+ /**
150
+ * Load and fully resolve a syndicate YAML definition.
151
+ *
152
+ * @param filename YAML filename inside `config/agents/`. Production
153
+ * syndicates live at the root; the shipped starter-pack examples live in
154
+ * `config/agents/examples/`. A bare filename is resolved against the root
155
+ * first, then `examples/`, so callers (and nested `yaml_reference`s) never
156
+ * need to know which side of the split a file is on.
157
+ * @param options Bindings and optional overrides
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const config = loadSyndicate('syndicate.yaml', {
162
+ * bindings: { headline_count: 10 },
163
+ * overrides: { syndicate_name: 'Custom Council' },
164
+ * });
165
+ * ```
166
+ */
167
+ export declare function loadSyndicate(filename: string, options?: LoadSyndicateOptions): SyndicateYamlConfig;
168
+ /**
169
+ * Validates registry configurations against strict ADK and project constraints.
170
+ */
171
+ export declare function validateRegistryConfig(config: Record<string, any>): void;
172
+ /**
173
+ * Loads a syndicate configuration from the Supabase registry.
174
+ * @param registryId The document ID in the adk_agent_registry table
175
+ * @param options Load options (bindings and overrides)
176
+ */
177
+ export declare function loadSyndicateFromRegistry(registryId: string, options?: LoadSyndicateOptions): Promise<SyndicateYamlConfig>;
@@ -0,0 +1,264 @@
1
+ import { parse } from 'yaml';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { createClient } from '@supabase/supabase-js';
5
+ import { hasSupabaseCredentials } from "./persistence/supabaseProvider.js";
6
+ // ── Variable Injection (private) ──────────────────────────
7
+ const TOKEN_RE = /\{\{(\w+)\}\}/g;
8
+ const FULL_TOKEN_RE = /^\{\{(\w+)\}\}$/;
9
+ function interpolateString(value, vars) {
10
+ const fullMatch = value.match(FULL_TOKEN_RE);
11
+ if (fullMatch) {
12
+ const key = fullMatch[1];
13
+ if (key in vars)
14
+ return vars[key];
15
+ return value;
16
+ }
17
+ return value.replace(TOKEN_RE, (_match, key) => {
18
+ if (key in vars)
19
+ return String(vars[key]);
20
+ return _match;
21
+ });
22
+ }
23
+ function interpolateDeep(obj, vars) {
24
+ if (typeof obj === 'string') {
25
+ return interpolateString(obj, vars);
26
+ }
27
+ if (Array.isArray(obj)) {
28
+ return obj.map((item) => interpolateDeep(item, vars));
29
+ }
30
+ if (obj !== null && typeof obj === 'object') {
31
+ const result = {};
32
+ for (const [key, value] of Object.entries(obj)) {
33
+ result[key] = interpolateDeep(value, vars);
34
+ }
35
+ return result;
36
+ }
37
+ return obj;
38
+ }
39
+ function findUnresolvedTokens(obj) {
40
+ const tokens = new Set();
41
+ function walk(val) {
42
+ if (typeof val === 'string') {
43
+ let m;
44
+ TOKEN_RE.lastIndex = 0;
45
+ while ((m = TOKEN_RE.exec(val)) !== null) {
46
+ tokens.add(m[1]);
47
+ }
48
+ }
49
+ else if (Array.isArray(val)) {
50
+ val.forEach(walk);
51
+ }
52
+ else if (val !== null && typeof val === 'object') {
53
+ Object.values(val).forEach(walk);
54
+ }
55
+ }
56
+ walk(obj);
57
+ return [...tokens];
58
+ }
59
+ function applyOverrides(config, overrides) {
60
+ return { ...config, ...overrides };
61
+ }
62
+ /**
63
+ * Bindings every load starts from, resolved at load time so date-anchored
64
+ * prompts never ship a stale hardcoded date. YAML `variables` and caller
65
+ * `bindings` both take precedence.
66
+ */
67
+ function defaultBindings() {
68
+ return {
69
+ current_date: new Date().toLocaleDateString('en-US', {
70
+ year: 'numeric',
71
+ month: 'long',
72
+ day: 'numeric',
73
+ timeZone: 'America/New_York',
74
+ }),
75
+ };
76
+ }
77
+ // ── CLI Binding Parser ────────────────────────────────────
78
+ function coerce(raw) {
79
+ if (raw === 'true')
80
+ return true;
81
+ if (raw === 'false')
82
+ return false;
83
+ const num = Number(raw);
84
+ if (!isNaN(num) && raw !== '')
85
+ return num;
86
+ return raw;
87
+ }
88
+ /**
89
+ * Parse CLI variable bindings into a VariableMap.
90
+ *
91
+ * Supports two formats:
92
+ * --bindings '{"focus_area": "AI Regulation", "headline_count": 10}'
93
+ * --bind key=value (individual overrides, take priority over --bindings)
94
+ */
95
+ export function parseCliBindings(argv) {
96
+ let vars = {};
97
+ for (let i = 0; i < argv.length; i++) {
98
+ if (argv[i] === '--bindings' && i + 1 < argv.length) {
99
+ try {
100
+ const parsed = JSON.parse(argv[i + 1]);
101
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
102
+ for (const [key, val] of Object.entries(parsed)) {
103
+ if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
104
+ vars[key] = val;
105
+ }
106
+ }
107
+ }
108
+ }
109
+ catch {
110
+ console.warn(`⚠ Failed to parse --bindings JSON: ${argv[i + 1]}`);
111
+ }
112
+ i++;
113
+ continue;
114
+ }
115
+ if (argv[i] === '--bind' && i + 1 < argv.length) {
116
+ const pair = argv[i + 1];
117
+ const eq = pair.indexOf('=');
118
+ if (eq === -1)
119
+ continue;
120
+ const key = pair.slice(0, eq).trim();
121
+ const raw = pair.slice(eq + 1).trim();
122
+ vars[key] = coerce(raw);
123
+ i++;
124
+ }
125
+ }
126
+ return vars;
127
+ }
128
+ // ── Primary Entrypoint ────────────────────────────────────
129
+ /**
130
+ * Load and fully resolve a syndicate YAML definition.
131
+ *
132
+ * @param filename YAML filename inside `config/agents/`. Production
133
+ * syndicates live at the root; the shipped starter-pack examples live in
134
+ * `config/agents/examples/`. A bare filename is resolved against the root
135
+ * first, then `examples/`, so callers (and nested `yaml_reference`s) never
136
+ * need to know which side of the split a file is on.
137
+ * @param options Bindings and optional overrides
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * const config = loadSyndicate('syndicate.yaml', {
142
+ * bindings: { headline_count: 10 },
143
+ * overrides: { syndicate_name: 'Custom Council' },
144
+ * });
145
+ * ```
146
+ */
147
+ export function loadSyndicate(filename, options = {}) {
148
+ const { bindings = {}, overrides } = options;
149
+ // Security: confine reads to the agents root. `filename` can originate from
150
+ // a network-controlled path segment (see a2a_server dynamic routes), so we
151
+ // must reject any value that resolves outside that root (e.g. "../../.env").
152
+ // The root itself is trusted configuration: option → env → cwd default.
153
+ const agentsDir = path.resolve(options.agentsDir ??
154
+ process.env.MELCHIZEDEK_AGENTS_DIR ??
155
+ path.join(process.cwd(), 'config', 'agents'));
156
+ let filePath = path.resolve(agentsDir, filename);
157
+ if (filePath !== agentsDir && !filePath.startsWith(agentsDir + path.sep)) {
158
+ throw new Error(`Invalid syndicate path: '${filename}' resolves outside the agents directory`);
159
+ }
160
+ // Starter-pack fallback: a name not found at the root is looked up in
161
+ // examples/ (still inside the jail), so moving a YAML between the two
162
+ // never breaks a bare-id A2A route or an npm script.
163
+ if (!fs.existsSync(filePath)) {
164
+ const inExamples = path.resolve(agentsDir, 'examples', filename);
165
+ if (inExamples.startsWith(agentsDir + path.sep) && fs.existsSync(inExamples)) {
166
+ filePath = inExamples;
167
+ }
168
+ }
169
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
170
+ const raw = parse(fileContent);
171
+ const merged = {
172
+ ...defaultBindings(),
173
+ ...(raw.variables ?? {}),
174
+ ...bindings,
175
+ };
176
+ const hasVars = Object.keys(merged).length > 0;
177
+ const interpolated = hasVars ? interpolateDeep(raw, merged) : raw;
178
+ if (hasVars) {
179
+ const unresolved = findUnresolvedTokens(interpolated);
180
+ if (unresolved.length > 0) {
181
+ console.warn(`⚠ Unresolved template variables: ${unresolved.map(t => `{{${t}}}`).join(', ')}`);
182
+ }
183
+ }
184
+ const { variables: _stripped, ...clean } = interpolated;
185
+ let config = clean;
186
+ if (overrides) {
187
+ config = applyOverrides(config, overrides);
188
+ }
189
+ return config;
190
+ }
191
+ // ── Registry Validation (Step 7) ──────────────────────────
192
+ const VALID_SYSTEM_TOOLS = [
193
+ 'google_search',
194
+ 'generate_image',
195
+ 'load_memory',
196
+ 'preload_memory',
197
+ 'get_company_fundamentals',
198
+ 'get_macro_metrics',
199
+ 'get_technical_indicators'
200
+ ];
201
+ /**
202
+ * Validates registry configurations against strict ADK and project constraints.
203
+ */
204
+ export function validateRegistryConfig(config) {
205
+ // 1. Enforce native property mapping rules
206
+ if ('options' in config) {
207
+ throw new Error(`[Validation Error] Legacy 'options' object found. Model parameters must reside in 'generateContentConfig'.`);
208
+ }
209
+ // 2. Validate declared tool references (Relaxed for MCP compatibility)
210
+ // We no longer strictly validate against VALID_SYSTEM_TOOLS because tools may be
211
+ // dynamically provided by an external MCP server.
212
+ // 3. Ensure core ADK properties exist
213
+ if (!config.name || (!config.yaml_reference && (!config.model || !config.instruction))) {
214
+ throw new Error(`[Validation Error] Configuration fails validation against ADK LlmAgentConfig specifications.`);
215
+ }
216
+ }
217
+ /**
218
+ * Loads a syndicate configuration from the Supabase registry.
219
+ * @param registryId The document ID in the adk_agent_registry table
220
+ * @param options Load options (bindings and overrides)
221
+ */
222
+ export async function loadSyndicateFromRegistry(registryId, options = {}) {
223
+ if (!hasSupabaseCredentials()) {
224
+ throw new Error('Supabase credentials not available for registry loading.');
225
+ }
226
+ const supabaseUrl = process.env.SUPABASE_URL;
227
+ const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
228
+ const supabase = createClient(supabaseUrl, supabaseKey);
229
+ const { data, error } = await supabase
230
+ .from('adk_agent_registry')
231
+ .select('yaml_content')
232
+ .eq('id', registryId)
233
+ .single();
234
+ if (error || !data) {
235
+ throw new Error(`Registry document ${registryId} not found`);
236
+ }
237
+ const raw = data.yaml_content;
238
+ // Validate orchestrator
239
+ validateRegistryConfig(raw.orchestrator);
240
+ // Validate subagents
241
+ for (const sub of raw.subagents || []) {
242
+ validateRegistryConfig(sub);
243
+ }
244
+ const { bindings = {}, overrides } = options;
245
+ const merged = {
246
+ ...defaultBindings(),
247
+ ...(raw.variables ?? {}),
248
+ ...bindings,
249
+ };
250
+ const hasVars = Object.keys(merged).length > 0;
251
+ const interpolated = hasVars ? interpolateDeep(raw, merged) : raw;
252
+ if (hasVars) {
253
+ const unresolved = findUnresolvedTokens(interpolated);
254
+ if (unresolved.length > 0) {
255
+ console.warn(`⚠ Unresolved template variables: ${unresolved.map((t) => `{{${t}}}`).join(', ')}`);
256
+ }
257
+ }
258
+ const { variables: _stripped, ...clean } = interpolated;
259
+ let config = clean;
260
+ if (overrides) {
261
+ config = applyOverrides(config, overrides);
262
+ }
263
+ return config;
264
+ }
@@ -0,0 +1,118 @@
1
+ import type { BaseMemoryService, SearchMemoryRequest, SearchMemoryResponse, Session } from '@google/adk';
2
+ import type { SupabaseClient } from '@supabase/supabase-js';
3
+ /**
4
+ * Harness-injected blocks the Discord surface prefixes to a user message
5
+ * (nihilistic-penguin stocks.py): the date marker and the portfolio payload.
6
+ * Neither is something the USER said. Stripped from the memory SEARCH query
7
+ * (a portfolio JSON would dominate the embedding and drown the question) and
8
+ * from the extraction transcript (a snapshot that changes daily would be
9
+ * re-stored as "what the user holds" every session — the analyst reads the
10
+ * live payload directly, memory only needs what the user SAID about it).
11
+ */
12
+ export declare function stripHarnessBlocks(text: string): string;
13
+ /**
14
+ * Cosine similarity at or above which a new record is treated as a restatement
15
+ * of one already stored, and dropped.
16
+ *
17
+ * Deliberately high. The extraction model rephrases the same fact on every
18
+ * pass ("The user has $831 in a forgotten Roth IRA" / "…an $831 balance in a
19
+ * forgotten Roth IRA account"), and those land around 0.95+; genuinely
20
+ * different facts about one subject ("NVDA closed at $217.50" vs "Wells Fargo
21
+ * put a $315 target on NVDA") sit far below. Tuning this DOWN risks silently
22
+ * discarding a real fact, which is the worse failure — a duplicate only costs
23
+ * a shortlist slot.
24
+ */
25
+ export declare const MEMORY_DEDUP_SIMILARITY = 0.93;
26
+ /**
27
+ * Which of a session's events still need ingesting.
28
+ *
29
+ * The A2A server is stateless, so `addSessionToMemory` runs after EVERY task
30
+ * with the WHOLE session — an N-turn conversation was being extracted N times
31
+ * over a growing transcript. That produced 13 EPISODE records narrating the
32
+ * same session and one balance stored six ways. A high-water mark per session
33
+ * means each turn is distilled once.
34
+ */
35
+ export declare function eventsToIngest<T>(events: T[], alreadyIngested: number): T[];
36
+ /**
37
+ * True when a candidate record restates something already stored.
38
+ *
39
+ * Same tag is required: an EPISODE narrating a discussion of the user's Roth
40
+ * IRA is not the FACT of its balance, however close the wording sits. Retired
41
+ * rows never suppress a new one — a superseded record is history, and the
42
+ * fact it was corrected to may legitimately be re-asserted later.
43
+ */
44
+ export declare function isSemanticDuplicate(candidate: {
45
+ tag: string;
46
+ }, matches: Array<{
47
+ tag: string | null;
48
+ status: string | null;
49
+ similarity: number;
50
+ }>, threshold?: number): boolean;
51
+ export declare class SupabaseVectorMemoryService implements BaseMemoryService {
52
+ private genai;
53
+ private supabase;
54
+ /**
55
+ * How many of each session's events have already been distilled.
56
+ * Keyed `{userKey}::{sessionId}`.
57
+ *
58
+ * In-process on purpose: a restart resets it, so the next turn re-reads
59
+ * that session once. Semantic dedup absorbs that batch, which is why this
60
+ * needs no table of its own — the two fixes cover each other's edges.
61
+ */
62
+ private ingestedEventCount;
63
+ constructor(config: {
64
+ apiKey: string;
65
+ }, supabaseClient: SupabaseClient);
66
+ /**
67
+ * @param extractionRules Optional domain rules appended to the shared
68
+ * extraction prompt for THIS consumer only. The prompt is global (the
69
+ * patient advocate uses the same one), so anything domain-specific —
70
+ * "never store a market quote" — arrives here rather than being edited
71
+ * into it. Declared as `memory_extraction_rules` on the served syndicate.
72
+ */
73
+ addSessionToMemory(session: Session, extractionRules?: string): Promise<void>;
74
+ /**
75
+ * Right-to-erasure: permanently deletes every stored fact for a user key
76
+ * (`{appName}/{userId}`). Returns the number of rows removed. Throws on
77
+ * database error so callers can surface the failure — a silent no-op on
78
+ * a deletion request is unacceptable for user data.
79
+ *
80
+ * NOTE: this clears `adk_memory_facts` only. Session transcripts live in
81
+ * `adk_sessions` and must be cleared separately if full erasure is needed.
82
+ */
83
+ deleteUserMemory(userKey: string): Promise<number>;
84
+ searchMemory(request: SearchMemoryRequest): Promise<SearchMemoryResponse>;
85
+ private extractRecords;
86
+ /**
87
+ * Parses one structured record line. Lines that don't match the format
88
+ * are dropped (extraction is best-effort; a malformed line must never
89
+ * poison the store). Header fields are optional individually — a bare
90
+ * `[FACT] text` line still parses, with defaults.
91
+ */
92
+ private parseRecord;
93
+ private embedTexts;
94
+ private serializeEvents;
95
+ /**
96
+ * Inserts new records and returns a map of record line → inserted row id
97
+ * (needed to link superseded rows to their replacement).
98
+ */
99
+ private upsertToSupabase;
100
+ /**
101
+ * The record evolves: a stored record carrying a `supersedes` header
102
+ * retires the older rows it corrects. Candidates are found by embedding
103
+ * the superseded quote and similarity-searching the user's ACTIVE rows;
104
+ * a candidate is retired when it is both semantically close and shares
105
+ * an index key with the correction (or is a near-exact match). Retired
106
+ * rows are kept — marked status='superseded' and linked via
107
+ * superseded_by — so history and contradictions stay inspectable.
108
+ */
109
+ private applySupersessions;
110
+ /**
111
+ * Hybrid recall: candidates come from pgvector cosine similarity, then
112
+ * are re-ranked by the query's connection to the record's index keys and
113
+ * dates. Active records outrank retired ones; retired records that still
114
+ * surface are relabeled so the model sees them as history, never as the
115
+ * current state.
116
+ */
117
+ private searchSupabase;
118
+ }