Skip to content

agent-expert

Expert agent — answers questions about AGENTS using docs search

agent-expert is the AGENTS developer assistant. It connects to a broker, registers interactive tools (e.g., ask-agents, write-tdd), uses vault-backed secrets for model calls, consults a remote memory service to recall prior conversations, and exposes an HTTP chat UI. Its purpose is to let developers ask questions about the AGENTS platform, generate tests/TDDs, and provide guidance backed by searchable docs and prior conversation context.

  • Core runtime: KadiClient (from @kadi.build/core) is the primary runtime object. agent-expert constructs a KadiClient and uses it to:
    • load native abilities (secret-ability) to fetch secrets from vaults,
    • register broker tools via registerTools(…),
    • call remote services via client.invokeRemote (memory-recall, memory-store).
  • Secrets: secret-ability is loaded as a native ability at startup and used to populate an in-process secretCache.
  • Memory: For context-aware answers, agent-expert invokes the remote memory service with “memory-recall” and archives exchanges with “memory-store”.
  • Broker: agent-expert connects to a WebSocket broker (configured in agent.json/config.toml) to advertise tools and receive broker-invoked actions.
  • HTTP: startServer(…) launches an HTTP server and chat UI (the server implementation is provided in server.js). The server can operate in HTTP-only mode if broker connection fails.
  • Lifecycle: graceful shutdown listens for SIGINT, disconnects client, and exits.

Data flow summary:

  1. Startup: load agent.json, build KadiClient, fetch secrets into secretCache via secret-ability.
  2. Register tools with the broker (registerTools).
  3. Connect to broker (client.connect). If unavailable, server remains HTTP-only.
  4. Interactions: user request -> HTTP UI or broker tool -> agent handlers use recallRelevantContext to fetch memory context, call models (via secrets in secretCache), respond, then call storeExchange to persist a short memory fragment.

Below are functions and the key broker registration entry-point used by the package (all names are taken from the source):

NameTypeDescriptionKey parameters
registerToolsfunction (imported from ./tools.js)Registers broker-facing tools (commented to include tools such as ask-agents, write-tdd, etc.). This is where most broker tool handlers are attached.(client: KadiClient, secretCache: Record<string,string>)
secretCacheexported constantRuntime in-memory cache of secrets loaded at startup from secret-ability. Populated with keys attempted in startup.Record<string,string>
recallRelevantContext(client, question)async functionQueries the remote memory service (“memory-recall”) to fetch recent relevant conversation fragments, returns an aggregated markdown block or null.(client: KadiClient, question: string) -> Promise<string
storeExchange(client, question, answer, conversationId?)functionStores a short summary (“Q: … A: …”) of an exchange via the remote memory service (“memory-store”).(client: KadiClient, question: string, answer: string, conversationId?: string)
startServerfunction (imported from ./server.js)Starts the HTTP chat server used for the UI. Called with the KadiClient and a port.(client: KadiClient, port: number, brokerConnectedFn: ()=>boolean)

Notes:

  • registerTools is the central place where broker tools are provided. The source comment explicitly mentions it registers “ask-agents” and “write-tdd” among others — see tools.js for the exact tool signatures and parameter schemas.
  • The secret-ability native ability is invoked with invoke(‘get’, { vault, key }) to fetch secrets. See the code example below.

agent-expert reads configuration from config.toml and environment variables. Important fields:

From config.toml:

  • [agent]
    • ID = “agent-expert”
    • VERSION = “0.1.0”
  • [logging]
    • LEVEL = “debug”
  • [broker.remote]
    • URL = “wss://broker.dadavidtseng.com/kadi”
    • NETWORKS = [“global”]
  • [secrets]
    • VAULTS = [“model-manager”, “anthropic”, “arcadedb”]
    • KEYS = [“MODEL_MANAGER_API_KEY”, “MODEL_MANAGER_BASE_URL”, “ARCADE_USERNAME”, “ARCADE_PASSWORD”]
  • [arcadedb]
    • HOST, PORT, USERNAME, DATABASE — arcadedb connection metadata used by the logging/archival backend.

From agent.json (deployment-related / build):

  • abilities: “secret-ability”, “ability-log” — abilities the agent expects to use.
  • brokers.remote: “wss://broker.dadavidtseng.com/kadi” — default remote broker used when running deployed.
  • deploy.*.secrets.vaults: defines required secret keys per vault. Example vaults:
    • model-manager: required [“MODEL_MANAGER_API_KEY”,“MODEL_MANAGER_BASE_URL”]
    • arcadedb: required [“ARCADE_USERNAME”,“ARCADE_PASSWORD”]
  • deploy.*.secrets.delivery = “broker” — indicates secret delivery via broker when deployed.

Environment variables:

  • BROKER_URL — override broker URL (index.ts checks process.env.BROKER_URL).
  • PORT — port for the HTTP server (defaults to 3500).
  • NODE_ENV — build/runtime environment; deployment sets NODE_ENV=production.
  • ARCADE_HOST / ARCADE_PORT — used by deployed service for ArcadeDB access (set in deploy config).
  • In containerized deploy command examples, ARCADE_HOST, ARCADE_PORT are provided via env.

Secrets / vaults

  • agent-expert attempts to load secrets via secret-ability at startup and fills secretCache with any found secrets.
  • The startup sequence attempts to read keys named ‘MM-1_API_KEY’ and ‘MEMORY_API_KEY’ from vaults ‘model-manager’ and ‘anthropic’ (see code example). Ensure these keys exist in your vault configuration if you expect model/memory integration to work.

Secrets usage (as seen in code):

  • client.loadNative(‘secret-ability’) -> secrets.invoke(‘get’, { vault, key }) -> result.value

Secrets delivery on deploy

  • The agent.json deployment section indicates secrets are delivered via the broker when deployed to Akash (delivery: “broker”).

Key snippets taken directly from the source to show initialization, secret loading, memory recall/storage patterns.

// src/index.ts (startup, secret loading, broker connect, server start)
import { KadiClient } from '@kadi.build/core';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { startServer } from './server.js';
import { registerTools } from './tools.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
// ── Load agent.json ───────────────────────────────────────────────────
function loadAgentJson(): Record<string, any> {
try {
return JSON.parse(readFileSync(join(__dirname, '..', 'agent.json'), 'utf-8'));
} catch {
return JSON.parse(readFileSync(join(__dirname, '..', '..', 'agent.json'), 'utf-8'));
}
}
const agentJson = loadAgentJson();
const brokerUrl = process.env.BROKER_URL ?? agentJson.brokers?.local ?? 'ws://localhost:8080/kadi';
// ── Create client ─────────────────────────────────────────────────────
const client = new KadiClient({
name: agentJson.name ?? 'agent-expert',
version: agentJson.version ?? '1.0.0',
description: agentJson.description,
defaultBroker: 'default',
brokers: {
default: { url: brokerUrl, networks: agentJson.networks ?? ['default'] },
},
});
// ── Load secrets ──────────────────────────────────────────────────────
export const secretCache: Record<string, string> = {};
try {
const secrets = await client.loadNative('secret-ability');
console.log('[agent-expert] secret-ability loaded');
for (const key of ['MM-1_API_KEY', 'MEMORY_API_KEY']) {
for (const vault of ['model-manager', 'anthropic']) {
try {
const result = await secrets.invoke('get', { vault, key }) as { value?: string };
if (result?.value) {
secretCache[key] = result.value;
console.log(`[agent-expert] ${key} loaded from vault "${vault}"`);
break;
}
} catch { /* not in this vault */ }
}
}
await secrets.disconnect();
} catch {
console.warn('[agent-expert] secret-ability not available — model calls may fail');
}
// ── Register broker tools ─────────────────────────────────────────────
registerTools(client, secretCache);
// ── Connect to broker ─────────────────────────────────────────────────
let brokerConnected = false;
try {
await client.connect();
brokerConnected = true;
console.log(`[agent-expert] Connected to broker: ${brokerUrl}`);
} catch (err) {
console.warn(`[agent-expert] Broker connection failed: ${(err as Error).message}`);
console.warn('[agent-expert] Running in HTTP-only mode (no broker tools)');
}
// ── Start HTTP server ─────────────────────────────────────────────────
const port = Number(process.env.PORT) || 3500;
startServer(client, port, () => brokerConnected);
// ── Graceful shutdown ─────────────────────────────────────────────────
process.on('SIGINT', () => {
console.log('[agent-expert] Shutting down…');
client.disconnect?.().catch(() => {});
process.exit(0);
});
// src/lib/conversation-memory.ts (recall + store helpers)
import type { KadiClient } from '@kadi.build/core';
const AGENT_ID = 'agent-expert';
const RECALL_TIMEOUT_MS = 2000;
const ARCHIVAL_THRESHOLD = 20;
interface MemoryResult {
content: string;
score?: number;
properties?: Record<string, unknown>;
}
interface RecallResponse {
results: MemoryResult[];
}
export async function recallRelevantContext(
client: KadiClient,
question: string,
): Promise<string | null> {
try {
const result = await Promise.race([
client.invokeRemote<RecallResponse>('memory-recall', {
query: question,
agent: AGENT_ID,
limit: 5,
mode: 'hybrid',
}),
new Promise<null>((_, reject) =>
setTimeout(() => reject(new Error('timeout')), RECALL_TIMEOUT_MS),
),
]);
if (!result?.results?.length) return null;
const fragments = result.results
.filter((r) => r.score && r.score > 0.3)
.slice(0, 3)
.map((r) => r.content);
if (fragments.length === 0) return null;
return `## Prior Conversations\n${fragments.join('\n---\n')}`;
} catch {
return null;
}
}
export function storeExchange(
client: KadiClient,
question: string,
answer: string,
conversationId?: string,
): void {
const content = `Q: ${question}\nA: ${answer.slice(0, 2000)}`;
client.invokeRemote('memory-store', {
content,
agent: AGENT_ID,
conversationId,
});
}

Usage patterns:

  • Use recallRelevantContext before calling a model to include prior context in the prompt.
  • After producing an answer, call storeExchange to persist a brief memory fragment.

Direct runtime dependencies (from package manifest):

  • @kadi.build/core — Kadi runtime (KadiClient, client.invokeRemote, loadNative, etc.)
  • express — used by the HTTP server (server.js)

Dev dependencies:

  • @types/express, @types/node, typescript, tsx

Abilities required/requested (from agent.json):

  • secret-ability — used at startup via client.loadNative(‘secret-ability’) to read vault secrets via secrets.invoke(‘get’, { vault, key }).
    • The code expects to retrieve at least MM-1_API_KEY and MEMORY_API_KEY from vaults model-manager or anthropic.
  • ability-log — declared in agent.json; may be used by tools for structured logging (not shown in the excerpt).

What depends on agent-expert

  • Consumers: other agents or UIs in the AGENTS ecosystem may call its broker tools once registered. The registerTools implementation publishes tools (ask-agents, write-tdd, …) that other agents or clients can invoke via the broker.
  • The deployment configuration expects interactions with a model-manager service (vault-provided credentials) and ArcadeDB for logs (arcadedb vault).
  • Tools are wired in registerTools(client, secretCache). For changing tool signatures or adding tools, edit tools.js and ensure registration happens after secretCache is populated.
  • Secret loading is best-effort: missing secret-ability will not fatal-start the agent, but model/memory calls will likely fail. Ensure the runtime environment or broker-delivered secrets match the vault/key names attempted in startup.
  • Memory calls are made through client.invokeRemote(‘memory-recall’|‘memory-store’). Ensure the memory service is available on the target broker/networks.
  • The HTTP server is started via startServer(client, port, () => brokerConnected) — the third argument is a function that indicates whether broker-based tools are available; server-side UI can use that to enable/disable broker actions.

If you need details on the registered tools’ exact RPC schemas, inspect tools.js (registerTools) and the server implementation (server.js) in this package.