melchizedek-agents 0.9.6 → 0.16.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 +272 -0
- package/AGENT_SETUP.md +39 -23
- package/CHANGELOG.md +623 -0
- package/DOCUMENTATION.md +429 -109
- package/QUICKSTART.md +92 -18
- package/README.md +45 -5
- package/config/agents/README.md +46 -1
- package/config/agents/examples/ares.yaml +117 -40
- package/config/agents/examples/assistant.yaml +159 -0
- package/config/agents/examples/augustin.yaml +154 -51
- package/config/agents/examples/cartographers.yaml +91 -23
- package/config/agents/examples/claude.yaml +45 -6
- package/config/agents/examples/council.yaml +23 -8
- package/config/agents/examples/critic.yaml +57 -21
- package/config/agents/examples/delegation.yaml +76 -9
- package/config/agents/examples/hierarchical.yaml +55 -8
- package/config/agents/examples/image_production.yaml +54 -16
- package/config/agents/examples/librarian.yaml +70 -14
- package/config/agents/examples/model_zoo.yaml +105 -29
- package/config/agents/examples/patient_advocate.yaml +102 -34
- package/config/agents/examples/research.yaml +250 -0
- package/config/agents/examples/scribe.yaml +242 -0
- package/config/agents/examples/scriptorium.yaml +87 -24
- package/config/agents/examples/style_council.yaml +31 -9
- package/config/agents/examples/syndicate.yaml +29 -10
- package/config/agents/examples/tutor.yaml +10 -3
- package/config/agents/syndicateSchema.yaml +44 -9
- package/config/agents/templates/README.md +58 -0
- package/config/agents/templates/account_memory.yaml +153 -0
- package/config/agents/templates/case_desk.yaml +188 -0
- package/config/agents/templates/conversational.yaml +110 -0
- package/config/agents/templates/draft_review.yaml +130 -0
- package/config/agents/templates/intake_extractor.yaml +152 -0
- package/config/agents/templates/research_brief.yaml +169 -0
- package/config/agents/templates/research_desk.yaml +152 -0
- package/config/agents/templates/review_panel.yaml +188 -0
- package/config/agents/templates/support_triage.yaml +273 -0
- package/config/agents/templates/systems_operator.yaml +157 -0
- package/db/hardening.sql +202 -0
- package/db/memory_v2.sql +77 -0
- package/db/migrations/0001_base.sql +167 -0
- package/db/migrations/0002_erase_scope.sql +172 -0
- package/db/migrations/0003_postgres_storage.sql +58 -0
- package/db/migrations/0004_usage.sql +94 -0
- package/db/migrations/0005_agent_registry.sql +200 -0
- package/db/migrations/0006_task_leases.sql +25 -0
- package/db/migrations/0007_memory_commit.sql +134 -0
- package/db/migrations/0008_memory_retention.sql +37 -0
- package/db/migrations/0009_task_queue.sql +51 -0
- package/db/migrations/0010_erase_complete.sql +180 -0
- package/db/telemetry.sql +401 -0
- package/demo/README.md +77 -0
- package/demo/a2a_demo.mjs +95 -0
- package/dist/lib/a2a/app.d.ts +253 -0
- package/dist/lib/a2a/app.js +751 -0
- package/dist/lib/a2a/executor.d.ts +167 -0
- package/dist/lib/a2a/executor.js +548 -0
- package/dist/lib/a2a/identity.d.ts +122 -0
- package/dist/lib/a2a/identity.js +254 -0
- package/dist/lib/a2a/policy.d.ts +110 -0
- package/dist/lib/a2a/policy.js +178 -0
- package/dist/lib/a2a/redisLimits.d.ts +26 -0
- package/dist/lib/a2a/redisLimits.js +31 -0
- package/dist/lib/a2a/remoteAgent.d.ts +91 -0
- package/dist/lib/a2a/remoteAgent.js +268 -0
- package/dist/lib/a2a/turnLock.d.ts +28 -0
- package/dist/lib/a2a/turnLock.js +76 -0
- package/dist/lib/compile.d.ts +11 -1
- package/dist/lib/compile.js +109 -8
- package/dist/lib/config.d.ts +7 -7
- package/dist/lib/config.js +7 -7
- package/dist/lib/doctor.d.ts +145 -0
- package/dist/lib/doctor.js +517 -0
- package/dist/lib/grounding.d.ts +2 -2
- package/dist/lib/grounding.js +2 -2
- package/dist/lib/guards/index.d.ts +37 -0
- package/dist/lib/guards/index.js +66 -0
- package/dist/lib/guards/science.d.ts +117 -0
- package/dist/lib/guards/science.js +444 -0
- package/dist/lib/index.d.ts +37 -2
- package/dist/lib/index.js +35 -4
- package/dist/lib/loadEnv.d.ts +23 -11
- package/dist/lib/loadEnv.js +77 -29
- package/dist/lib/loadSyndicate.d.ts +97 -5
- package/dist/lib/loadSyndicate.js +176 -76
- package/dist/lib/memory/erase.d.ts +24 -0
- package/dist/lib/memory/erase.js +47 -0
- package/dist/lib/memory/namespace.d.ts +37 -0
- package/dist/lib/memory/namespace.js +74 -0
- package/dist/lib/memory/providers.d.ts +99 -0
- package/dist/lib/memory/providers.js +240 -0
- package/dist/lib/memory/store.d.ts +74 -0
- package/dist/lib/memory/store.js +108 -0
- package/dist/lib/memory/supabaseMemoryService.d.ts +69 -9
- package/dist/lib/memory/supabaseMemoryService.js +239 -125
- package/dist/lib/models/capabilities.d.ts +103 -0
- package/dist/lib/models/capabilities.js +291 -0
- package/dist/lib/models/claudeLlm.d.ts +10 -1
- package/dist/lib/models/claudeLlm.js +57 -27
- package/dist/lib/models/endpoints.d.ts +100 -0
- package/dist/lib/models/endpoints.js +282 -0
- package/dist/lib/models/gateway.d.ts +107 -0
- package/dist/lib/models/gateway.js +157 -0
- package/dist/lib/models/gatewayLlm.d.ts +57 -0
- package/dist/lib/models/gatewayLlm.js +105 -0
- package/dist/lib/models/gptLlm.d.ts +51 -2
- package/dist/lib/models/gptLlm.js +169 -26
- package/dist/lib/models/grokLlm.d.ts +19 -10
- package/dist/lib/models/grokLlm.js +29 -14
- package/dist/lib/models/ollamaLlm.d.ts +26 -0
- package/dist/lib/models/ollamaLlm.js +43 -1
- package/dist/lib/models/openAiCompatibleLlm.d.ts +67 -2
- package/dist/lib/models/openAiCompatibleLlm.js +200 -37
- package/dist/lib/models/providerMap.js +9 -2
- package/dist/lib/models/registry.d.ts +42 -5
- package/dist/lib/models/registry.js +140 -29
- package/dist/lib/models/retry.d.ts +146 -0
- package/dist/lib/models/retry.js +305 -0
- package/dist/lib/models/schemaNormalize.d.ts +20 -0
- package/dist/lib/models/schemaNormalize.js +43 -0
- package/dist/lib/net/addressGuard.d.ts +60 -0
- package/dist/lib/net/addressGuard.js +216 -0
- package/dist/lib/observability/embeddings.d.ts +11 -2
- package/dist/lib/observability/embeddings.js +18 -14
- package/dist/lib/observability/metrics.d.ts +36 -0
- package/dist/lib/observability/metrics.js +86 -0
- package/dist/lib/observability/redact.d.ts +29 -0
- package/dist/lib/observability/redact.js +102 -0
- package/dist/lib/observability/supabaseSpanExporter.d.ts +2 -0
- package/dist/lib/observability/supabaseSpanExporter.js +22 -6
- package/dist/lib/observability/tracer.d.ts +10 -0
- package/dist/lib/observability/tracer.js +70 -3
- package/dist/lib/persistence/supabaseProvider.d.ts +11 -0
- package/dist/lib/persistence/supabaseProvider.js +32 -6
- package/dist/lib/registry.d.ts +87 -0
- package/dist/lib/registry.js +146 -0
- package/dist/lib/runtime/approvals.d.ts +44 -0
- package/dist/lib/runtime/approvals.js +67 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +233 -0
- package/dist/lib/runtime/syndicateTurn.js +597 -0
- package/dist/lib/runtime/turnControl.d.ts +90 -0
- package/dist/lib/runtime/turnControl.js +131 -0
- package/dist/lib/session/supabaseSessionService.js +1 -1
- package/dist/lib/session/transcript.d.ts +9 -1
- package/dist/lib/session/transcript.js +15 -7
- package/dist/lib/skills.d.ts +50 -0
- package/dist/lib/skills.js +138 -0
- package/dist/lib/storage/postgres/index.d.ts +98 -0
- package/dist/lib/storage/postgres/index.js +146 -0
- package/dist/lib/storage/postgres/memoryStore.d.ts +11 -0
- package/dist/lib/storage/postgres/memoryStore.js +82 -0
- package/dist/lib/storage/postgres/sessionService.d.ts +51 -0
- package/dist/lib/storage/postgres/sessionService.js +225 -0
- package/dist/lib/storage/postgres/taskQueue.d.ts +22 -0
- package/dist/lib/storage/postgres/taskQueue.js +101 -0
- package/dist/lib/storage/postgres/taskStore.d.ts +55 -0
- package/dist/lib/storage/postgres/taskStore.js +168 -0
- package/dist/lib/storage/schema.d.ts +27 -0
- package/dist/lib/storage/schema.js +50 -0
- package/dist/lib/storage/schemaVersion.d.ts +25 -0
- package/dist/lib/storage/schemaVersion.js +46 -0
- package/dist/lib/syndicateSchema.d.ts +275 -0
- package/dist/lib/syndicateSchema.js +534 -0
- package/dist/lib/toolRegistry.d.ts +15 -0
- package/dist/lib/toolRegistry.js +59 -10
- package/dist/lib/tools/collectionsSearchTool.js +1 -1
- package/dist/lib/tools/mcpServe.d.ts +45 -0
- package/dist/lib/tools/mcpServe.js +88 -0
- package/dist/lib/tools/mcpToolFactory.d.ts +2 -0
- package/dist/lib/tools/mcpToolFactory.js +41 -21
- package/dist/lib/tools/science/corrections.d.ts +42 -0
- package/dist/lib/tools/science/corrections.js +115 -0
- package/dist/lib/tools/science/europepmc.d.ts +51 -0
- package/dist/lib/tools/science/europepmc.js +127 -0
- package/dist/lib/tools/science/http.d.ts +31 -0
- package/dist/lib/tools/science/http.js +104 -0
- package/dist/lib/tools/science/openalex.d.ts +30 -0
- package/dist/lib/tools/science/openalex.js +81 -0
- package/dist/lib/tools/science/trials.d.ts +58 -0
- package/dist/lib/tools/science/trials.js +203 -0
- package/dist/lib/tools/science/types.d.ts +69 -0
- package/dist/lib/tools/science/types.js +48 -0
- package/dist/lib/tools/scienceTools.d.ts +100 -0
- package/dist/lib/tools/scienceTools.js +277 -0
- package/dist/lib/tools/taskTools.d.ts +204 -0
- package/dist/lib/tools/taskTools.js +374 -0
- package/dist/lib/tools/toolContract.d.ts +17 -4
- package/dist/lib/tools/toolContract.js +13 -5
- package/dist/lib/tools/webExtractTool.d.ts +13 -10
- package/dist/lib/tools/webExtractTool.js +23 -55
- package/dist/lib/tools/webSearchTool.d.ts +2 -1
- package/dist/lib/tools/webSearchTool.js +4 -3
- package/dist/lib/tools/wikiTools.d.ts +2 -2
- package/dist/lib/tools/wikiTools.js +12 -12
- package/dist/lib/tools/xApiSearchTool.d.ts +194 -0
- package/dist/lib/tools/xApiSearchTool.js +583 -0
- package/dist/lib/tools/xSearchTool.js +1 -1
- package/dist/lib/wiki/agentRun.js +1 -1
- package/dist/lib/wiki/builder.js +21 -6
- package/dist/lib/wiki/dive.js +2 -2
- package/dist/lib/wiki/entities.d.ts +2 -2
- package/dist/lib/wiki/entities.js +7 -7
- package/dist/lib/wiki/extract.js +5 -1
- package/dist/lib/wiki/fill.js +5 -5
- package/dist/lib/wiki/format.d.ts +7 -5
- package/dist/lib/wiki/format.js +2 -2
- package/dist/lib/wiki/graph.js +2 -2
- package/dist/lib/wiki/lint.js +2 -2
- package/dist/lib/wiki/markdown.d.ts +1 -1
- package/dist/lib/wiki/markdown.js +6 -1
- package/dist/lib/wiki/pipeline.js +2 -2
- package/dist/lib/wiki/vault.js +2 -2
- package/dist/scripts/a2a_server.d.ts +17 -11
- package/dist/scripts/a2a_server.js +405 -1028
- package/dist/scripts/assistant_worker.d.ts +33 -0
- package/dist/scripts/assistant_worker.js +193 -0
- package/dist/scripts/db.d.ts +26 -0
- package/dist/scripts/db.js +179 -0
- package/dist/scripts/doctor.d.ts +23 -0
- package/dist/scripts/doctor.js +90 -0
- package/dist/scripts/init.d.ts +43 -0
- package/dist/scripts/init.js +152 -0
- package/dist/scripts/registry.d.ts +25 -0
- package/dist/scripts/registry.js +182 -0
- package/dist/scripts/skills_install.d.ts +7 -0
- package/dist/scripts/skills_install.js +137 -0
- package/dist/scripts/syndicate_chat.js +159 -276
- package/package.json +90 -16
- package/skills/README.md +74 -0
- package/skills/briefs/README.md +16 -0
- package/skills/briefs/_shared.md +16 -0
- package/skills/briefs/melchizedek-author.md +60 -0
- package/skills/briefs/melchizedek-memory.md +47 -0
- package/skills/briefs/melchizedek-models.md +47 -0
- package/skills/briefs/melchizedek-scribe.md +37 -0
- package/skills/briefs/melchizedek-serve.md +56 -0
- package/skills/briefs/melchizedek.md +69 -0
- package/skills/briefs/skills-readme.md +33 -0
- package/skills/melchizedek/SKILL.md +159 -0
- package/skills/melchizedek-author/SKILL.md +99 -0
- package/skills/melchizedek-author/templates/minimal.yaml +60 -0
- package/skills/melchizedek-memory/SKILL.md +138 -0
- package/skills/melchizedek-models/SKILL.md +137 -0
- package/skills/melchizedek-scribe/SKILL.md +72 -0
- package/skills/melchizedek-scribe/templates/brief.md +27 -0
- package/skills/melchizedek-serve/SKILL.md +119 -0
|
@@ -2,27 +2,22 @@
|
|
|
2
2
|
import { createInterface } from 'node:readline';
|
|
3
3
|
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { loadSyndicate, parseCliBindings } from
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import {
|
|
5
|
+
import { loadSyndicate, parseCliBindings } from '../lib/loadSyndicate.js';
|
|
6
|
+
import { isDispatchSyndicate } from '../lib/dispatch.js';
|
|
7
|
+
import { ingestTurnMemory, runSyndicateTurn } from '../lib/runtime/syndicateTurn.js';
|
|
8
|
+
import { InMemorySessionService, getFunctionCalls, getFunctionResponses, setLogLevel, LogLevel, } from '@google/adk';
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
|
-
|
|
11
|
-
import { resolveTools as resolveNamedTools } from "../lib/toolRegistry.js";
|
|
12
|
-
import { createMcpTools } from "../lib/tools/mcpToolFactory.js";
|
|
13
|
-
import { loadEnv } from "../lib/loadEnv.js";
|
|
10
|
+
import { loadEnv } from '../lib/loadEnv.js';
|
|
14
11
|
// ── LLM Provider Registration ─────────────────────────────────────────────────
|
|
15
12
|
// WHY: Import only — registration is deferred to main() so it runs AFTER
|
|
16
13
|
// loadEnv() has populated process.env from .env. If registered here at module
|
|
17
14
|
// load time, provider API keys would always be undefined and the non-Gemini
|
|
18
15
|
// adapters would never be added to the LLMRegistry, causing "Model not
|
|
19
16
|
// found" errors. registerAvailableProviders() gates each adapter on its key.
|
|
20
|
-
import { registerAvailableProviders, providerForModel, providerKeyPresent, PROVIDERS, } from
|
|
17
|
+
import { registerAvailableProviders, providerForModel, providerKeyPresent, PROVIDERS, } from '../lib/models/registry.js';
|
|
21
18
|
// ── Persistence Factory ───────────────────────────────────────────────────────
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
// import with a sibling provider module that exports the same interface.
|
|
25
|
-
import { hasSupabaseCredentials, createSupabaseServices, } from "../lib/persistence/supabaseProvider.js";
|
|
19
|
+
// Supabase-specific initialization lives in supabaseProvider.ts.
|
|
20
|
+
import { hasSupabaseCredentials, createSupabaseServices, } from '../lib/persistence/supabaseProvider.js';
|
|
26
21
|
// Silence ADK verbose INFO logging natively
|
|
27
22
|
setLogLevel(LogLevel.WARN);
|
|
28
23
|
const c = {
|
|
@@ -63,7 +58,7 @@ function detectPersistenceConfig(memorySystem) {
|
|
|
63
58
|
function banner(config, persistence, bindings = {}, sessionId) {
|
|
64
59
|
console.log('');
|
|
65
60
|
console.log(`${c.cyan}${c.bold} ╔══════════════════════════════════════╗${c.reset}`);
|
|
66
|
-
console.log(`${c.cyan}${c.bold} ║
|
|
61
|
+
console.log(`${c.cyan}${c.bold} ║ Melchizedek Syndicate ║${c.reset}`);
|
|
67
62
|
console.log(`${c.cyan}${c.bold} ╚══════════════════════════════════════╝${c.reset}`);
|
|
68
63
|
console.log(`${c.dim} Syndicate : ${c.yellow}${config.syndicate_name}${c.reset}`);
|
|
69
64
|
console.log(`${c.dim} Orchestrator : ${c.yellow}${config.orchestrator.name} (${config.orchestrator.model})${c.reset}`);
|
|
@@ -176,144 +171,32 @@ async function main() {
|
|
|
176
171
|
const STREAM_REPLIES = process.env.CHAT_STREAMING !== 'false';
|
|
177
172
|
// ── Detect persistence mode ──────────────────────────────────────────────
|
|
178
173
|
const persistence = detectPersistenceConfig(config.memory_system);
|
|
179
|
-
// ──
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
for (const subConfig of config.subagents) {
|
|
186
|
-
const tools = resolveTools(subConfig.tools);
|
|
187
|
-
if (subConfig.mcp_server_url) {
|
|
188
|
-
const mcpTools = await createMcpTools(subConfig.mcp_server_url);
|
|
189
|
-
for (const mcpTool of mcpTools) {
|
|
190
|
-
if (!tools.some(t => t.name === mcpTool.name)) {
|
|
191
|
-
tools.push(mcpTool);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
const agent = new LlmAgent({
|
|
196
|
-
name: subConfig.name,
|
|
197
|
-
model: subConfig.model,
|
|
198
|
-
instruction: subConfig.instruction,
|
|
199
|
-
description: subConfig.description,
|
|
200
|
-
tools: tools.length > 0 ? tools : undefined,
|
|
201
|
-
inputSchema: {
|
|
202
|
-
type: "OBJECT",
|
|
203
|
-
properties: {
|
|
204
|
-
query: {
|
|
205
|
-
type: "STRING",
|
|
206
|
-
description: `The search query or instruction for ${subConfig.name}`
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
required: ["query"]
|
|
210
|
-
},
|
|
211
|
-
outputSchema: subConfig.outputSchema,
|
|
212
|
-
generateContentConfig: {
|
|
213
|
-
...subConfig.generateContentConfig,
|
|
214
|
-
toolConfig: {
|
|
215
|
-
...subConfig.generateContentConfig?.toolConfig,
|
|
216
|
-
includeServerSideToolInvocations: true
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
orchestratorTools.push(new AgentTool({ agent }));
|
|
221
|
-
}
|
|
222
|
-
// 2. Build ADK Orchestrator and provide subagents + its own direct tools
|
|
223
|
-
// WHY: The orchestrator may declare its own tools in YAML (e.g. generate_image,
|
|
224
|
-
// load_memory, preload_memory) in addition to — or instead of — subagent AgentTools.
|
|
225
|
-
const orchestratorDirectTools = resolveTools(config.orchestrator.tools);
|
|
226
|
-
const allOrchestratorTools = [...orchestratorTools, ...orchestratorDirectTools];
|
|
227
|
-
const orchestrator = new LlmAgent({
|
|
228
|
-
name: config.orchestrator.name,
|
|
229
|
-
model: config.orchestrator.model,
|
|
230
|
-
instruction: config.orchestrator.instruction,
|
|
231
|
-
description: 'The master orchestrator.',
|
|
232
|
-
tools: allOrchestratorTools.length > 0 ? allOrchestratorTools : undefined,
|
|
233
|
-
outputSchema: config.orchestrator.outputSchema,
|
|
234
|
-
generateContentConfig: {
|
|
235
|
-
...config.orchestrator.generateContentConfig,
|
|
236
|
-
toolConfig: {
|
|
237
|
-
...config.orchestrator.generateContentConfig?.toolConfig,
|
|
238
|
-
includeServerSideToolInvocations: true
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
// 3. Build the runner.
|
|
243
|
-
//
|
|
244
|
-
// WHY: We now construct a full `Runner` (not InMemoryRunner) when cloud
|
|
245
|
-
// credentials are available. Runner accepts explicit sessionService and
|
|
246
|
-
// memoryService instances, enabling persistent state across process restarts
|
|
247
|
-
// (Firebase) and semantic long-term memory (Vertex). When no cloud credentials
|
|
248
|
-
// are detected, we fall back to InMemoryRunner for local development.
|
|
249
|
-
const appName = config.syndicate_name || 'melchizedek-syndicate';
|
|
250
|
-
let runner;
|
|
251
|
-
// ── Runner Construction ───────────────────────────────────────────────────
|
|
252
|
-
// WHY: The persistence provider is resolved here, not inline. Supabase init,
|
|
253
|
-
// service construction, and credential handling all live in supabaseProvider.ts.
|
|
254
|
-
// To swap backends, update the import at the top of this file — nothing here
|
|
255
|
-
// needs to change.
|
|
174
|
+
// ── Persistence ───────────────────────────────────────────────────────────
|
|
175
|
+
// The same services the server would use for this syndicate's
|
|
176
|
+
// memory_system: Supabase sessions (and the memory service for
|
|
177
|
+
// long-term) when configured, process memory otherwise.
|
|
178
|
+
let sessionService = new InMemorySessionService();
|
|
179
|
+
let memoryService;
|
|
256
180
|
if (persistence.sessionService === 'supabase') {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
// pgvector). All Supabase-specific init is delegated to the factory.
|
|
261
|
-
const { sessionService, memoryService } = await createSupabaseServices({
|
|
262
|
-
// Empty only when an all-local syndicate runs keyless — in that case
|
|
263
|
-
// detectPersistenceConfig has already forced withMemory to false.
|
|
181
|
+
const services = await createSupabaseServices({
|
|
182
|
+
// Empty only when an all-local syndicate runs keyless — then
|
|
183
|
+
// detectPersistenceConfig has already left the memory service off.
|
|
264
184
|
apiKey: apiKey ?? '',
|
|
265
185
|
withMemory: persistence.memoryService === 'supabase-vector',
|
|
266
186
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
appName,
|
|
270
|
-
sessionService,
|
|
271
|
-
memoryService,
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
// ── In-Memory Mode ────────────────────────────────────────────────────
|
|
276
|
-
// WHY: No Supabase credentials found. Use InMemorySessionService for
|
|
277
|
-
// session state (volatile, process-scoped). Memory service is omitted
|
|
278
|
-
// since it requires Supabase to function.
|
|
279
|
-
const sessionService = new InMemorySessionService();
|
|
280
|
-
runner = new Runner({
|
|
281
|
-
agent: orchestrator,
|
|
282
|
-
appName,
|
|
283
|
-
sessionService,
|
|
284
|
-
memoryService: undefined,
|
|
285
|
-
});
|
|
187
|
+
sessionService = services.sessionService;
|
|
188
|
+
memoryService = services.memoryService;
|
|
286
189
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
// sessionService was wired into the runner.
|
|
290
|
-
await runner.sessionService.createSession({
|
|
291
|
-
appName,
|
|
292
|
-
userId: SESSION_USER_ID,
|
|
293
|
-
sessionId: SESSION_ID,
|
|
294
|
-
state: {}
|
|
295
|
-
});
|
|
296
|
-
// Now that the session is live, render the startup banner with the session ID.
|
|
190
|
+
const appName = config.syndicate_name || 'melchizedek-syndicate';
|
|
191
|
+
await sessionService.createSession({ appName, userId: SESSION_USER_ID, sessionId: SESSION_ID, state: {} });
|
|
297
192
|
banner(config, persistence, mergedBindings, SESSION_ID);
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
const originalWarn = console.warn;
|
|
306
|
-
console.warn = (...args) => {
|
|
307
|
-
if (typeof args[0] === 'string' && args[0].includes('[ADK]') && args[0].includes('Event from an unknown agent'))
|
|
308
|
-
return;
|
|
309
|
-
originalWarn(...args);
|
|
310
|
-
};
|
|
311
|
-
const originalLog = console.log;
|
|
312
|
-
console.log = (...args) => {
|
|
313
|
-
if (typeof args[0] === 'string' && args[0].includes('[ADK]'))
|
|
314
|
-
return;
|
|
315
|
-
originalLog(...args);
|
|
316
|
-
};
|
|
193
|
+
// Plan-dispatch, nested yaml_reference syndicates, guards and the
|
|
194
|
+
// max_steps cap run here exactly as the A2A server runs them: both call
|
|
195
|
+
// lib/runtime/syndicateTurn.ts. (This file used to build its own graph
|
|
196
|
+
// and silently ran dispatch syndicates in DELEGATE mode.)
|
|
197
|
+
if (isDispatchSyndicate(config)) {
|
|
198
|
+
console.log(`${c.dim} Mode : plan-dispatch (classifier → one route answers)${c.reset}\n`);
|
|
199
|
+
}
|
|
317
200
|
// Strip --bind/--bindings/--syndicate pairs AND bare '--' separators from
|
|
318
201
|
// argv to get the actual user query. The bare '--' leaks in when npm passes
|
|
319
202
|
// the named syndicate shortcuts (e.g. syndicate:image) to the node process.
|
|
@@ -330,103 +213,44 @@ async function main() {
|
|
|
330
213
|
}
|
|
331
214
|
}
|
|
332
215
|
const cliInput = queryParts.join(' ');
|
|
216
|
+
// Ctrl+C during a turn cancels the turn; Ctrl+C at the prompt exits.
|
|
217
|
+
let activeTurn;
|
|
333
218
|
async function runChat(trimmed) {
|
|
219
|
+
const printer = makePrinter();
|
|
220
|
+
activeTurn = new AbortController();
|
|
334
221
|
try {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
let stream = runner.runAsync({
|
|
222
|
+
const result = await runSyndicateTurn({
|
|
223
|
+
config,
|
|
224
|
+
parts: [{ text: trimmed }],
|
|
225
|
+
appName,
|
|
340
226
|
userId: SESSION_USER_ID,
|
|
341
227
|
sessionId: SESSION_ID,
|
|
342
|
-
|
|
228
|
+
sessionService,
|
|
229
|
+
memoryService,
|
|
230
|
+
compile: {
|
|
231
|
+
onUnknownTool: (name) => console.warn(`${c.yellow}⚠ Unknown tool: '${name}' — skipping.${c.reset}`),
|
|
232
|
+
log: (message) => console.log(`${c.dim} ${message}${c.reset}`),
|
|
233
|
+
},
|
|
343
234
|
// SSE makes the adapters emit display-only partials as tokens
|
|
344
|
-
// land, so thinking and the reply appear as they are produced
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
235
|
+
// land, so thinking and the reply appear as they are produced.
|
|
236
|
+
streaming: STREAM_REPLIES,
|
|
237
|
+
signal: activeTurn.signal,
|
|
238
|
+
trace: { bindings: mergedBindings },
|
|
239
|
+
events: {
|
|
240
|
+
onEvent: printer.onEvent,
|
|
241
|
+
onProgress: (text) => {
|
|
242
|
+
if (text.startsWith('Routed to') || text.startsWith('Guard '))
|
|
243
|
+
console.log(`\n${c.dim}[${text}]${c.reset}`);
|
|
244
|
+
},
|
|
245
|
+
},
|
|
354
246
|
});
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const isPartial = event.partial === true;
|
|
363
|
-
const calls = getFunctionCalls(event);
|
|
364
|
-
if (calls && calls.length > 0) {
|
|
365
|
-
for (const call of calls) {
|
|
366
|
-
if (call.name === 'transfer_to_agent') {
|
|
367
|
-
console.log(`\n${c.dim}[${event.author} is delegating to subagent: ${JSON.stringify(call.args)}]${c.reset}`);
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
console.log(`\n${c.dim}[${event.author} is calling tool: ${call.name}]${c.reset}`);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
const responses = getFunctionResponses(event);
|
|
375
|
-
if (responses && responses.length > 0) {
|
|
376
|
-
for (const response of responses) {
|
|
377
|
-
console.log(`\n${c.dim}[Received response from: ${response.name}]${c.reset}`);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
// WHY: LlmResponse can carry errorCode/errorMessage (e.g. billing errors,
|
|
381
|
-
// rate limits, invalid model) without raising a JS exception. Without this
|
|
382
|
-
// check those errors are silently dropped, producing a blank response.
|
|
383
|
-
// NOTE: 'STOP' is a normal finishReason mapped to errorCode by ADK when content is empty.
|
|
384
|
-
const evAny = event;
|
|
385
|
-
if ((evAny.errorCode || evAny.errorMessage) && evAny.errorCode !== 'STOP') {
|
|
386
|
-
console.error(`\n${c.yellow}⚠ [${evAny.errorCode ?? 'ERROR'}] ${evAny.errorMessage ?? ''}${c.reset}`);
|
|
387
|
-
}
|
|
388
|
-
if (event.content && event.content.parts) {
|
|
389
|
-
for (const part of event.content.parts) {
|
|
390
|
-
if (part.thought) {
|
|
391
|
-
if (currentMode !== 'thinking') {
|
|
392
|
-
console.log(`\n${c.cyan}✦ ${event.author} is thinking...${c.reset}`);
|
|
393
|
-
currentMode = 'thinking';
|
|
394
|
-
}
|
|
395
|
-
process.stdout.write(c.dim + part.text + c.reset);
|
|
396
|
-
}
|
|
397
|
-
else if (part.text) {
|
|
398
|
-
if (!isPartial && streamedText)
|
|
399
|
-
continue;
|
|
400
|
-
if (currentMode === 'thinking') {
|
|
401
|
-
console.log(`\n\n${c.magenta}${c.bold}${event.author}${c.reset} › `);
|
|
402
|
-
currentMode = 'text';
|
|
403
|
-
}
|
|
404
|
-
else if (currentMode === 'none') {
|
|
405
|
-
process.stdout.write(`\n${c.magenta}${c.bold}${event.author}${c.reset} › `);
|
|
406
|
-
currentMode = 'text';
|
|
407
|
-
}
|
|
408
|
-
process.stdout.write(part.text);
|
|
409
|
-
if (isPartial)
|
|
410
|
-
streamedText = true;
|
|
411
|
-
}
|
|
412
|
-
else if (part.inlineData) {
|
|
413
|
-
// WHY: Image generation models return binary data as base64-encoded
|
|
414
|
-
// inlineData parts. We detect these, decode them, and save to disk
|
|
415
|
-
// so the user gets a real file rather than raw base64 in the terminal.
|
|
416
|
-
const { mimeType, data } = part.inlineData;
|
|
417
|
-
const ext = mimeType?.split('/')[1] ?? 'png';
|
|
418
|
-
const outputDir = join(process.cwd(), 'outputs');
|
|
419
|
-
mkdirSync(outputDir, { recursive: true });
|
|
420
|
-
const filename = `image_${Date.now()}.${ext}`;
|
|
421
|
-
const filepath = join(outputDir, filename);
|
|
422
|
-
writeFileSync(filepath, Buffer.from(data, 'base64'));
|
|
423
|
-
process.stdout.write(`\n${c.green}✓ Image saved → outputs/${filename}${c.reset}\n`);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
// A complete event ends the turn; the next one starts fresh.
|
|
428
|
-
if (!isPartial)
|
|
429
|
-
streamedText = false;
|
|
247
|
+
printer.finish();
|
|
248
|
+
if (result.relayFallback) {
|
|
249
|
+
// The orchestrator's relay came back empty; show what shipped.
|
|
250
|
+
console.log(`\n${c.dim}[relay fallback — the specialist's answer]${c.reset}\n${result.text}`);
|
|
251
|
+
}
|
|
252
|
+
if (result.status !== 'completed') {
|
|
253
|
+
console.error(`\n${c.yellow}⚠ [${result.error?.code ?? 'ERROR'}] ${result.error?.message ?? ''}${c.reset}`);
|
|
430
254
|
}
|
|
431
255
|
console.log('\n');
|
|
432
256
|
}
|
|
@@ -434,14 +258,36 @@ async function main() {
|
|
|
434
258
|
const msg = err instanceof Error ? err.message : String(err);
|
|
435
259
|
console.error(`\n${c.yellow}⚠ Error: ${msg}${c.reset}\n`);
|
|
436
260
|
}
|
|
261
|
+
finally {
|
|
262
|
+
activeTurn = undefined;
|
|
263
|
+
}
|
|
437
264
|
}
|
|
265
|
+
const ingest = async () => {
|
|
266
|
+
if (!memoryService)
|
|
267
|
+
return;
|
|
268
|
+
try {
|
|
269
|
+
const ingested = await ingestTurnMemory({
|
|
270
|
+
memoryService,
|
|
271
|
+
sessionService,
|
|
272
|
+
appName,
|
|
273
|
+
userId: SESSION_USER_ID,
|
|
274
|
+
sessionId: SESSION_ID,
|
|
275
|
+
extractionRules: config.memory_extraction_rules,
|
|
276
|
+
extractionModel: config.memory_extraction_model,
|
|
277
|
+
});
|
|
278
|
+
console.log(ingested
|
|
279
|
+
? `${c.green} ✓ Session ingested into long-term memory.${c.reset}`
|
|
280
|
+
: `${c.dim} No session data to ingest.${c.reset}`);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
console.error(`${c.yellow} ⚠ Memory ingestion failed: ${err instanceof Error ? err.message : String(err)}${c.reset}`);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
438
286
|
if (cliInput) {
|
|
439
287
|
console.log(`${c.green}${c.bold}You${c.reset} › ${cliInput}`);
|
|
440
288
|
await runChat(cliInput);
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
// contribute to the agent's knowledge base.
|
|
444
|
-
await ingestSessionMemory(runner, appName, SESSION_USER_ID, SESSION_ID);
|
|
289
|
+
// A one-shot query still contributes to long-term memory.
|
|
290
|
+
await ingest();
|
|
445
291
|
return;
|
|
446
292
|
}
|
|
447
293
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -453,12 +299,9 @@ async function main() {
|
|
|
453
299
|
return;
|
|
454
300
|
}
|
|
455
301
|
if (['exit', 'quit', 'bye'].includes(trimmed.toLowerCase())) {
|
|
456
|
-
//
|
|
457
|
-
// memory. This is the critical moment where ephemeral session data
|
|
458
|
-
// becomes persistent knowledge — the Runner's memoryService
|
|
459
|
-
// extracts facts, embeds them, and stores them for future retrieval.
|
|
302
|
+
// Session end is where the conversation becomes long-term memory.
|
|
460
303
|
console.log(`\n${c.dim} Ingesting session into long-term memory...${c.reset}`);
|
|
461
|
-
await
|
|
304
|
+
await ingest();
|
|
462
305
|
console.log(`${c.cyan} Goodbye! 👋${c.reset}\n`);
|
|
463
306
|
rl.close();
|
|
464
307
|
return;
|
|
@@ -467,46 +310,86 @@ async function main() {
|
|
|
467
310
|
ask();
|
|
468
311
|
});
|
|
469
312
|
};
|
|
470
|
-
// WHY: If the user forcefully exits the CLI (Ctrl+C), we must intercept the
|
|
471
|
-
// termination signal to ensure the current session is gracefully ingested
|
|
472
|
-
// into long-term memory before the process dies.
|
|
473
313
|
process.on('SIGINT', async () => {
|
|
314
|
+
if (activeTurn) {
|
|
315
|
+
console.log(`\n${c.dim} Canceling the turn...${c.reset}`);
|
|
316
|
+
activeTurn.abort();
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
474
319
|
console.log(`\n${c.dim} Intercepted exit signal. Ingesting session into long-term memory...${c.reset}`);
|
|
475
|
-
await
|
|
320
|
+
await ingest();
|
|
476
321
|
console.log(`${c.cyan} Goodbye! 👋${c.reset}\n`);
|
|
477
322
|
process.exit(0);
|
|
478
323
|
});
|
|
479
324
|
ask();
|
|
480
325
|
}
|
|
481
326
|
/**
|
|
482
|
-
*
|
|
327
|
+
* Prints a turn's events as they arrive: tool calls, thinking (dimmed), the
|
|
328
|
+
* reply streamed token by token, and inline images saved to outputs/.
|
|
483
329
|
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
* it to the memory service's extraction pipeline. This is where session
|
|
488
|
-
* transcripts get compressed into semantic facts and stored for future recall.
|
|
330
|
+
* Under SSE the reply arrives as partials and is then REPEATED whole on the
|
|
331
|
+
* final event (the one ADK persists to session history): show the partials,
|
|
332
|
+
* skip the repeat.
|
|
489
333
|
*/
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
334
|
+
function makePrinter() {
|
|
335
|
+
let currentMode = 'none';
|
|
336
|
+
let streamedText = false;
|
|
337
|
+
return {
|
|
338
|
+
onEvent(event) {
|
|
339
|
+
const e = event;
|
|
340
|
+
const isPartial = e.partial === true;
|
|
341
|
+
for (const call of getFunctionCalls(event) ?? []) {
|
|
342
|
+
if (call.name === 'transfer_to_agent') {
|
|
343
|
+
console.log(`\n${c.dim}[${e.author} is delegating to subagent: ${JSON.stringify(call.args)}]${c.reset}`);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
console.log(`\n${c.dim}[${e.author} is calling tool: ${call.name}]${c.reset}`);
|
|
347
|
+
}
|
|
348
|
+
currentMode = 'none';
|
|
349
|
+
}
|
|
350
|
+
for (const response of getFunctionResponses(event) ?? []) {
|
|
351
|
+
console.log(`\n${c.dim}[Received response from: ${response.name}]${c.reset}`);
|
|
352
|
+
currentMode = 'none';
|
|
353
|
+
}
|
|
354
|
+
for (const part of event.content?.parts ?? []) {
|
|
355
|
+
const p = part;
|
|
356
|
+
if (p.thought) {
|
|
357
|
+
if (currentMode !== 'thinking') {
|
|
358
|
+
console.log(`\n${c.cyan}✦ ${e.author} is thinking...${c.reset}`);
|
|
359
|
+
currentMode = 'thinking';
|
|
360
|
+
}
|
|
361
|
+
process.stdout.write(c.dim + p.text + c.reset);
|
|
362
|
+
}
|
|
363
|
+
else if (p.text) {
|
|
364
|
+
if (!isPartial && streamedText)
|
|
365
|
+
continue;
|
|
366
|
+
if (currentMode !== 'text') {
|
|
367
|
+
process.stdout.write(`\n${c.magenta}${c.bold}${e.author}${c.reset} › `);
|
|
368
|
+
currentMode = 'text';
|
|
369
|
+
}
|
|
370
|
+
process.stdout.write(p.text);
|
|
371
|
+
if (isPartial)
|
|
372
|
+
streamedText = true;
|
|
373
|
+
}
|
|
374
|
+
else if (p.inlineData) {
|
|
375
|
+
// Image models return base64 inlineData; save it as a file.
|
|
376
|
+
const { mimeType, data } = p.inlineData;
|
|
377
|
+
const ext = mimeType?.split('/')[1] ?? 'png';
|
|
378
|
+
const outputDir = join(process.cwd(), 'outputs');
|
|
379
|
+
mkdirSync(outputDir, { recursive: true });
|
|
380
|
+
const filename = `image_${Date.now()}.${ext}`;
|
|
381
|
+
writeFileSync(join(outputDir, filename), Buffer.from(data, 'base64'));
|
|
382
|
+
process.stdout.write(`\n${c.green}✓ Image saved → outputs/${filename}${c.reset}\n`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// A complete event ends that agent's turn; the next starts fresh.
|
|
386
|
+
if (!isPartial)
|
|
387
|
+
streamedText = false;
|
|
388
|
+
},
|
|
389
|
+
finish() {
|
|
390
|
+
currentMode = 'none';
|
|
391
|
+
},
|
|
392
|
+
};
|
|
510
393
|
}
|
|
511
394
|
main().catch((err) => {
|
|
512
395
|
if (err.code !== 'ERR_USE_AFTER_CLOSE') {
|