@slashfi/agents-sdk 0.21.0 → 0.23.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/src/adk.ts ADDED
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * ADK CLI — Agent Development Kit
4
+ *
5
+ * Unified CLI for building, testing, and publishing agent definitions.
6
+ *
7
+ * Commands:
8
+ * codegen Generate agent definitions from an MCP server (full codegen)
9
+ * introspect Introspect an MCP server → agent.json (lightweight)
10
+ * pack Generate publishable @agentdef/* package from agent.json
11
+ * publish Pack + npm publish to @agentdef/*
12
+ * use Execute a tool on a generated agent
13
+ * list List all generated agents
14
+ *
15
+ * @example
16
+ * ```bash
17
+ * # Full codegen from MCP server
18
+ * adk codegen --server 'npx @mcp/notion' --name notion --out ./agents/@notion
19
+ *
20
+ * # Lightweight introspect → agent.json
21
+ * adk introspect --server 'npx @notionhq/notion-mcp-server' --name notion
22
+ *
23
+ * # Build + publish
24
+ * adk pack
25
+ * adk publish
26
+ *
27
+ * # Use a tool
28
+ * adk use notion search_pages '{"query": "hello"}'
29
+ * adk use notion --list
30
+ * ```
31
+ */
32
+
33
+ import { existsSync, readdirSync } from "node:fs";
34
+ import { join, resolve } from "node:path";
35
+ import { codegen, listAgentTools, useAgent } from "./codegen.js";
36
+ import type { CodegenManifest } from "./codegen.js";
37
+ import { pack, publish } from "./pack.js";
38
+
39
+ const args = process.argv.slice(2);
40
+ const command = args[0];
41
+
42
+ // ============================================
43
+ // Helpers
44
+ // ============================================
45
+
46
+ function getArg(flag: string): string | undefined {
47
+ const idx = args.indexOf(flag);
48
+ if (idx === -1 || idx + 1 >= args.length) return undefined;
49
+ return args[idx + 1];
50
+ }
51
+
52
+ function hasFlag(flag: string): boolean {
53
+ return args.includes(flag);
54
+ }
55
+
56
+ function getAgentsDir(): string {
57
+ return resolve(process.env.AGENTS_SDK_DIR ?? "./agents");
58
+ }
59
+
60
+ function findAgentDir(name: string): string | null {
61
+ const agentsDir = getAgentsDir();
62
+
63
+ const exactPath = resolve(name);
64
+ if (existsSync(join(exactPath, ".codegen-manifest.json"))) return exactPath;
65
+
66
+ const withAt = join(agentsDir, `@${name}`);
67
+ if (existsSync(join(withAt, ".codegen-manifest.json"))) return withAt;
68
+
69
+ const withoutAt = join(agentsDir, name);
70
+ if (existsSync(join(withoutAt, ".codegen-manifest.json"))) return withoutAt;
71
+
72
+ return null;
73
+ }
74
+
75
+ function printUsage() {
76
+ console.log(`
77
+ adk — Agent Development Kit
78
+
79
+ Usage:
80
+ adk codegen [options] Generate agent from MCP server (full codegen)
81
+ adk introspect [options] Introspect MCP server → agent.json
82
+ adk pack [options] Generate publishable package from agent.json
83
+ adk publish [options] Pack + npm publish to @agentdef/*
84
+ adk use <agent> [options] Execute a tool on a generated agent
85
+ adk list List all generated agents
86
+
87
+ Codegen options:
88
+ --server <source> MCP server (command string or URL)
89
+ --name <name> Agent name (default: derived from server)
90
+ --out <dir> Output directory (default: ./agents/@<name>)
91
+ --path <path> Agent path override
92
+ --no-cli Skip CLI generation
93
+ --no-types Skip TypeScript interface generation
94
+ --visibility <level> Agent visibility (public|internal|private)
95
+
96
+ Introspect options:
97
+ --server <cmd> MCP server command to introspect
98
+ --name <name> Agent name for output
99
+ --out <path> Output path (default: ./<name>.json)
100
+
101
+ Pack / Publish options:
102
+ --agent <path> Path to agent.json (default: ./agent.json)
103
+ --out <dir> Output directory (default: ./dist)
104
+ --scope <scope> npm scope (default: @agentdef)
105
+ --previous <path> Previous agent.json for diff
106
+ --dry-run Don't actually publish (publish only)
107
+ --tag <tag> npm dist-tag (default: latest)
108
+ --access <level> npm access: public | restricted (default: public)
109
+
110
+ Use options:
111
+ adk use <agent> <tool> [params_json]
112
+ adk use <agent> --list List tools on the agent
113
+
114
+ Examples:
115
+ adk codegen --server 'npx @mcp/notion' --name notion
116
+ adk introspect --server 'npx @notionhq/notion-mcp-server' --name notion
117
+ adk pack --agent ./agent.json
118
+ adk publish --dry-run
119
+ adk use notion search_pages '{"query": "hello"}'
120
+ `);
121
+ }
122
+
123
+ // ============================================
124
+ // Commands
125
+ // ============================================
126
+
127
+ async function runCodegen() {
128
+ const server = getArg("--server");
129
+ const name = getArg("--name");
130
+ const outDir = getArg("--out");
131
+ const agentPath = getArg("--path");
132
+ const visibility = getArg("--visibility") as
133
+ | "public"
134
+ | "internal"
135
+ | "private"
136
+ | undefined;
137
+ const noCli = hasFlag("--no-cli");
138
+ const noTypes = hasFlag("--no-types");
139
+
140
+ if (!server) {
141
+ console.error(
142
+ "Error: --server is required.\n" +
143
+ " Example: adk codegen --server 'npx @mcp/notion' --name notion",
144
+ );
145
+ process.exit(1);
146
+ }
147
+
148
+ const resolvedOutDir =
149
+ outDir ??
150
+ join(
151
+ getAgentsDir(),
152
+ `@${(name ?? "mcp-agent").toLowerCase().replace(/[^a-z0-9-]/g, "-")}`,
153
+ );
154
+
155
+ console.log(`Connecting to MCP server: ${server}`);
156
+ console.log(`Output: ${resolvedOutDir}\n`);
157
+
158
+ try {
159
+ const result = await codegen({
160
+ server,
161
+ outDir: resolvedOutDir,
162
+ agentPath,
163
+ name,
164
+ cli: !noCli,
165
+ types: !noTypes,
166
+ visibility,
167
+ });
168
+
169
+ console.log(
170
+ `\x1b[32m\u2713\x1b[0m Generated ${result.toolCount} tools from ${result.serverInfo.name ?? "MCP server"}`,
171
+ );
172
+ console.log("\nFiles:");
173
+ for (const f of result.files) {
174
+ console.log(` ${f}`);
175
+ }
176
+ console.log(
177
+ `\nUse: adk use ${name ?? result.serverInfo.name ?? "<agent>"} --list`,
178
+ );
179
+ } catch (err) {
180
+ console.error(
181
+ `\x1b[31mError:\x1b[0m ${err instanceof Error ? err.message : String(err)}`,
182
+ );
183
+ process.exit(1);
184
+ }
185
+ }
186
+
187
+ async function runIntrospect() {
188
+ const server = getArg("--server");
189
+ const name = getArg("--name");
190
+ const out = getArg("--out") || (name ? `./${name}.json` : undefined);
191
+
192
+ if (!server || !name) {
193
+ console.error(
194
+ "Usage: adk introspect --server <cmd> --name <name> [--out <path>]",
195
+ );
196
+ process.exit(1);
197
+ }
198
+
199
+ const { introspectMcp } = await import("./introspect.js");
200
+ await introspectMcp({ server, name, out });
201
+ }
202
+
203
+ function runPack() {
204
+ const agentFile = getArg("--agent") || "./agent.json";
205
+ const outDir = getArg("--out") || "./dist";
206
+ const scope = getArg("--scope") || "@agentdef";
207
+ const previousAgentFile = getArg("--previous");
208
+
209
+ if (!existsSync(resolve(agentFile))) {
210
+ console.error(`agent.json not found at ${resolve(agentFile)}`);
211
+ console.error("Run 'adk introspect' first, or specify --agent <path>");
212
+ process.exit(1);
213
+ }
214
+
215
+ const result = pack({ agentFile, outDir, scope, previousAgentFile });
216
+ console.log(`\n\u2705 Packed ${result.packageName}@${result.version}`);
217
+ console.log(` Hash: ${result.hash}`);
218
+ console.log(` Tools: ${result.meta.toolCount}`);
219
+ console.log(` Size: ${(result.meta.sizeBytes / 1024).toFixed(1)}KB`);
220
+ console.log(` Output: ${result.packageDir}`);
221
+ if (result.meta.changes) {
222
+ const c = result.meta.changes;
223
+ if (c.toolsAdded.length > 0)
224
+ console.log(` Added: ${c.toolsAdded.join(", ")}`);
225
+ if (c.toolsRemoved.length > 0)
226
+ console.log(` Removed: ${c.toolsRemoved.join(", ")}`);
227
+ if (c.toolsModified.length > 0)
228
+ console.log(` Modified: ${c.toolsModified.join(", ")}`);
229
+ }
230
+ }
231
+
232
+ function runPublish() {
233
+ const agentFile = getArg("--agent") || "./agent.json";
234
+ const outDir = getArg("--out") || "./dist";
235
+ const scope = getArg("--scope") || "@agentdef";
236
+ const previousAgentFile = getArg("--previous");
237
+ const dryRun = hasFlag("--dry-run");
238
+ const tag = getArg("--tag");
239
+ const access = getArg("--access") as "public" | "restricted" | undefined;
240
+ const registry = getArg("--registry");
241
+
242
+ if (!existsSync(resolve(agentFile))) {
243
+ console.error(`agent.json not found at ${resolve(agentFile)}`);
244
+ console.error("Run 'adk introspect' first, or specify --agent <path>");
245
+ process.exit(1);
246
+ }
247
+
248
+ try {
249
+ const result = publish({
250
+ agentFile,
251
+ outDir,
252
+ scope,
253
+ previousAgentFile,
254
+ dryRun,
255
+ tag,
256
+ access,
257
+ registry,
258
+ });
259
+ console.log(
260
+ `\n\u2705 Published ${result.packageName}@${result.version} (hash: ${result.hash})`,
261
+ );
262
+ } catch (err) {
263
+ console.error(err instanceof Error ? err.message : String(err));
264
+ process.exit(1);
265
+ }
266
+ }
267
+
268
+ async function runUse() {
269
+ const agentName = args[1];
270
+
271
+ if (!agentName) {
272
+ console.error(
273
+ "Error: agent name required.\n" +
274
+ " Example: adk use notion search_pages '{...}'",
275
+ );
276
+ process.exit(1);
277
+ }
278
+
279
+ const agentDir = findAgentDir(agentName);
280
+ if (!agentDir) {
281
+ console.error(
282
+ `Error: agent '${agentName}' not found.\n` +
283
+ ` Looked in: ${getAgentsDir()}\n` +
284
+ ` Generate first: adk codegen --server '...' --name ${agentName}`,
285
+ );
286
+ process.exit(1);
287
+ }
288
+
289
+ if (hasFlag("--list")) {
290
+ const tools = listAgentTools(agentDir);
291
+ console.log(`Tools for ${agentName}:\n`);
292
+ for (const t of tools) {
293
+ console.log(` ${t.name.padEnd(30)} ${t.description ?? ""}`);
294
+ }
295
+ return;
296
+ }
297
+
298
+ const toolName = args[2];
299
+ if (!toolName) {
300
+ console.error(
301
+ `Error: tool name required.\n Example: adk use ${agentName} <tool> [params]\n List tools: adk use ${agentName} --list`,
302
+ );
303
+ process.exit(1);
304
+ }
305
+
306
+ const paramsStr = args[3];
307
+ const params = paramsStr ? JSON.parse(paramsStr) : {};
308
+
309
+ try {
310
+ const result = await useAgent({
311
+ agentDir,
312
+ tool: toolName,
313
+ params,
314
+ });
315
+ console.log(JSON.stringify(result, null, 2));
316
+ } catch (err) {
317
+ console.error(
318
+ `\x1b[31mError:\x1b[0m ${err instanceof Error ? err.message : String(err)}`,
319
+ );
320
+ process.exit(1);
321
+ }
322
+ }
323
+
324
+ function runList() {
325
+ const agentsDir = getAgentsDir();
326
+
327
+ if (!existsSync(agentsDir)) {
328
+ console.log("No generated agents found.");
329
+ return;
330
+ }
331
+
332
+ const entries = readdirSync(agentsDir);
333
+ const agents: { name: string; tools: number; server?: string }[] = [];
334
+
335
+ for (const entry of entries) {
336
+ const manifestPath = join(agentsDir, entry, ".codegen-manifest.json");
337
+ if (existsSync(manifestPath)) {
338
+ try {
339
+ const manifest: CodegenManifest = JSON.parse(
340
+ require("node:fs").readFileSync(manifestPath, "utf-8"),
341
+ );
342
+ agents.push({
343
+ name: manifest.agentPath,
344
+ tools: manifest.tools.length,
345
+ server: manifest.serverInfo.name,
346
+ });
347
+ } catch {
348
+ agents.push({ name: entry, tools: 0 });
349
+ }
350
+ }
351
+ }
352
+
353
+ if (agents.length === 0) {
354
+ console.log("No generated agents found.");
355
+ return;
356
+ }
357
+
358
+ console.log("Generated agents:\n");
359
+ for (const a of agents) {
360
+ console.log(
361
+ ` ${a.name.padEnd(25)} ${String(a.tools).padEnd(5)} tools${a.server ? ` (${a.server})` : ""}`,
362
+ );
363
+ }
364
+ }
365
+
366
+ // ============================================
367
+ // Main
368
+ // ============================================
369
+
370
+ switch (command) {
371
+ case "codegen":
372
+ await runCodegen();
373
+ break;
374
+ case "introspect":
375
+ await runIntrospect();
376
+ break;
377
+ case "pack":
378
+ runPack();
379
+ break;
380
+ case "publish":
381
+ runPublish();
382
+ break;
383
+ case "use":
384
+ await runUse();
385
+ break;
386
+ case "list":
387
+ runList();
388
+ break;
389
+ case "--help":
390
+ case "-h":
391
+ case undefined:
392
+ printUsage();
393
+ break;
394
+ default:
395
+ console.error(`Unknown command: ${command}`);
396
+ printUsage();
397
+ process.exit(1);
398
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Agent Callback — Deferred call_agent execution with triggers.
3
+ *
4
+ * An agent_callback is a call_agent command with an optional trigger.
5
+ * When the trigger fires (e.g., user submits a form), template references
6
+ * like {{trigger.variable_name}} are resolved with the trigger's values
7
+ * and the call_agent command is executed.
8
+ *
9
+ * This module provides the unopinionated contract:
10
+ * - Trigger schema (extensible discriminated union)
11
+ * - Template resolution
12
+ * - Store interface
13
+ * - Validation utilities
14
+ *
15
+ * No platform-specific code (no Slack, no CockroachDB, no Atlas).
16
+ */
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Trigger Schema
20
+ // ---------------------------------------------------------------------------
21
+
22
+ /**
23
+ * Base trigger type. Implementations extend this with specific trigger sources
24
+ * (e.g., slack_block_kit, webhook, timer).
25
+ *
26
+ * The `type` field discriminates between trigger sources.
27
+ * Additional fields are trigger-specific.
28
+ */
29
+ export interface AgentCallbackTrigger {
30
+ type: string;
31
+ [key: string]: unknown;
32
+ }
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Callback Status
36
+ // ---------------------------------------------------------------------------
37
+
38
+ export type AgentCallbackStatus =
39
+ | 'pending'
40
+ | 'completed'
41
+ | 'expired'
42
+ | 'cancelled';
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Callback Entry
46
+ // ---------------------------------------------------------------------------
47
+
48
+ /**
49
+ * A stored agent_callback — a call_agent command waiting for its trigger to fire.
50
+ */
51
+ export interface AgentCallbackEntry<TMetadata = Record<string, unknown>> {
52
+ id: string;
53
+ status: AgentCallbackStatus;
54
+ /** The call_agent command. Params may contain {{trigger.x}} templates. */
55
+ callback: Record<string, unknown>;
56
+ /** Trigger definition — how values are collected. */
57
+ trigger?: AgentCallbackTrigger;
58
+ /** Implementation-specific context (e.g., creator branch, user ID). */
59
+ metadata?: TMetadata;
60
+ /** Resolved values from the trigger, keyed by variable name. */
61
+ resolvedValues?: Record<string, string>;
62
+ /** Callback expires after this time. */
63
+ expiresAt?: Date;
64
+ createdAt: Date;
65
+ completedAt?: Date;
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Create Options
70
+ // ---------------------------------------------------------------------------
71
+
72
+ export interface CreateAgentCallbackOptions<TMetadata = Record<string, unknown>> {
73
+ /** The call_agent command. May contain {{trigger.x}} template references in params. */
74
+ callback: Record<string, unknown>;
75
+ /** Trigger definition. */
76
+ trigger?: AgentCallbackTrigger;
77
+ /** Implementation-specific context. */
78
+ metadata?: TMetadata;
79
+ /** TTL in milliseconds (default: implementation-defined). */
80
+ ttlMs?: number;
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // Resolve Options
85
+ // ---------------------------------------------------------------------------
86
+
87
+ export interface ResolveAgentCallbackOptions {
88
+ /** The callback ID to resolve. */
89
+ id: string;
90
+ /** Values from the trigger source, keyed by variable name. */
91
+ values: Record<string, string>;
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Store Interface
96
+ // ---------------------------------------------------------------------------
97
+
98
+ /**
99
+ * Agent Callback Store — persistence layer for deferred call_agent commands.
100
+ * Implementations can use any backing store (CockroachDB, SQLite, in-memory, etc.).
101
+ */
102
+ export interface AgentCallbackStore<TMetadata = Record<string, unknown>> {
103
+ create(options: CreateAgentCallbackOptions<TMetadata>): Promise<string>;
104
+ get(id: string): Promise<AgentCallbackEntry<TMetadata> | null>;
105
+ resolve(options: ResolveAgentCallbackOptions): Promise<AgentCallbackEntry<TMetadata>>;
106
+ cancel(id: string): Promise<boolean>;
107
+ listPending(limit?: number): Promise<AgentCallbackEntry<TMetadata>[]>;
108
+ expireStale(): Promise<number>;
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Template Resolution
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /**
116
+ * Resolve {{trigger.variable}} references in an object tree.
117
+ * Scans all string values and replaces {{trigger.x}} with the
118
+ * corresponding value from triggerValues.
119
+ *
120
+ * Unresolved references are left as-is.
121
+ */
122
+ export function resolveCallbackTemplates<T>(
123
+ obj: T,
124
+ triggerValues: Record<string, string>,
125
+ ): T {
126
+ if (typeof obj === 'string') {
127
+ return obj.replace(/\{\{trigger\.(\w+)\}\}/g, (_match, varName: string) => {
128
+ return triggerValues[varName] ?? `{{trigger.${varName}}}`;
129
+ }) as T;
130
+ }
131
+ if (Array.isArray(obj)) {
132
+ return obj.map((item) => resolveCallbackTemplates(item, triggerValues)) as T;
133
+ }
134
+ if (obj !== null && typeof obj === 'object') {
135
+ const result: Record<string, unknown> = {};
136
+ for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {
137
+ result[key] = resolveCallbackTemplates(val, triggerValues);
138
+ }
139
+ return result as T;
140
+ }
141
+ return obj;
142
+ }
143
+
144
+ /**
145
+ * Validate that all {{trigger.x}} references in a callback have
146
+ * corresponding variables in the provided set.
147
+ * Returns array of unresolved variable names, or empty if valid.
148
+ */
149
+ export function validateCallbackTemplates(
150
+ callback: Record<string, unknown>,
151
+ knownVariables: string[],
152
+ ): string[] {
153
+ const definedVars = new Set(knownVariables);
154
+ const referencedVars: string[] = [];
155
+
156
+ const scanForRefs = (obj: unknown): void => {
157
+ if (typeof obj === 'string') {
158
+ const matches = obj.matchAll(/\{\{trigger\.(\w+)\}\}/g);
159
+ for (const match of matches) {
160
+ referencedVars.push(match[1]);
161
+ }
162
+ } else if (Array.isArray(obj)) {
163
+ obj.forEach(scanForRefs);
164
+ } else if (obj !== null && typeof obj === 'object') {
165
+ Object.values(obj as Record<string, unknown>).forEach(scanForRefs);
166
+ }
167
+ };
168
+
169
+ scanForRefs(callback);
170
+
171
+ return referencedVars.filter((v) => !definedVars.has(v));
172
+ }
package/src/index.ts CHANGED
@@ -314,3 +314,46 @@ export type {
314
314
  AgentClient,
315
315
  CreateClientOptions,
316
316
  } from "./client.js";
317
+
318
+ // ============================================
319
+ // JSONC Parser
320
+ // ============================================
321
+
322
+ export { parseJsonc, readJsoncFile } from "./jsonc.js";
323
+
324
+ // ============================================
325
+ // Pack & Publish
326
+ // ============================================
327
+
328
+ export { pack, publish } from "./pack.js";
329
+ export type {
330
+ PackOptions,
331
+ PackResult,
332
+ PublishOptions,
333
+ VersionMeta,
334
+ VersionChanges,
335
+ } from "./pack.js";
336
+
337
+ // ============================================
338
+ // Introspect
339
+ // ============================================
340
+
341
+ export { introspectMcp } from "./introspect.js";
342
+ export type { IntrospectOptions } from "./introspect.js";
343
+
344
+ // ============================================
345
+ // Agent Callbacks (deferred call_agent commands)
346
+ // ============================================
347
+
348
+ export {
349
+ resolveCallbackTemplates,
350
+ validateCallbackTemplates,
351
+ } from "./callback/index.js";
352
+ export type {
353
+ AgentCallbackTrigger,
354
+ AgentCallbackStatus,
355
+ AgentCallbackEntry,
356
+ AgentCallbackStore,
357
+ CreateAgentCallbackOptions,
358
+ ResolveAgentCallbackOptions,
359
+ } from "./callback/index.js";