create-bk-agent 0.16.1 → 0.17.1
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/README.md +93 -29
- package/dist/src/add/detector.js +80 -0
- package/dist/src/add/index.js +67 -0
- package/dist/src/add/modules/mcp-client.js +65 -0
- package/dist/src/add/modules/server.js +100 -0
- package/dist/src/add/modules/specialist.js +98 -0
- package/dist/src/add/modules/tools.js +102 -0
- package/dist/src/add/patcher.js +168 -0
- package/dist/src/index.js +55 -9
- package/dist/src/prompts.js +102 -25
- package/dist/src/scaffold.js +95 -21
- package/package.json +1 -1
- package/templates/base/autonomous/.env.example +4 -0
- package/templates/base/autonomous/package.json +23 -0
- package/templates/base/autonomous/src/index.ts +67 -0
- package/templates/base/autonomous/tsconfig.json +17 -0
- package/templates/base/coding/.env.example +4 -0
- package/templates/base/coding/AGENT.md +13 -0
- package/templates/base/coding/package.json +24 -0
- package/templates/base/coding/src/index.ts +42 -0
- package/templates/base/coding/tsconfig.json +17 -0
- package/templates/base/minimal/.env.example +1 -0
- package/templates/base/minimal/package.json +23 -0
- package/templates/base/minimal/src/index.ts +43 -0
- package/templates/base/minimal/tsconfig.json +17 -0
- package/templates/base/multi-agent/.env.example +7 -0
- package/templates/base/multi-agent/package.json +26 -0
- package/templates/base/multi-agent/src/agents/orchestrator.ts +22 -0
- package/templates/base/multi-agent/src/agents/specialist-a.ts +17 -0
- package/templates/base/multi-agent/src/agents/specialist-b.ts +16 -0
- package/templates/base/multi-agent/src/config.ts +19 -0
- package/templates/base/multi-agent/src/engine.ts +49 -0
- package/templates/base/multi-agent/src/providers/openai-compatible.ts +104 -0
- package/templates/base/multi-agent/src/tools/example-tools.ts +28 -0
- package/templates/base/multi-agent/tsconfig.json +17 -0
- package/templates/base/single/.env.example +4 -0
- package/templates/base/single/package.json +26 -0
- package/templates/base/single/src/agents/my-agent.ts +18 -0
- package/templates/base/single/src/config.ts +18 -0
- package/templates/base/single/src/engine.ts +44 -0
- package/templates/base/single/src/providers/openai-compatible.ts +104 -0
- package/templates/base/single/src/tools/example-tools.ts +28 -0
- package/templates/base/single/tsconfig.json +17 -0
- package/templates/deploy/hybrid/src/index.ts +27 -0
- package/templates/deploy/hybrid/src/server.ts +26 -0
- package/templates/deploy/mcp-http/src/server.ts +26 -0
- package/templates/deploy/mcp-stdio/src/server.ts +25 -0
- package/templates/deploy/repl/src/index.ts +27 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
|
|
3
|
+
export interface Config {
|
|
4
|
+
llmApiKey: string;
|
|
5
|
+
llmBaseUrl: string;
|
|
6
|
+
llmModel: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function loadConfig(): Config {
|
|
10
|
+
const llmApiKey = process.env.LLM_API_KEY ?? '';
|
|
11
|
+
if (!llmApiKey) throw new Error('Missing LLM_API_KEY in environment');
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
llmApiKey,
|
|
15
|
+
llmBaseUrl: process.env.LLM_BASE_URL ?? 'https://api.openai.com/v1',
|
|
16
|
+
llmModel: process.env.LLM_MODEL ?? 'gpt-4o',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentEngine,
|
|
3
|
+
AgentRegistry,
|
|
4
|
+
ToolRegistry,
|
|
5
|
+
ProviderRegistry,
|
|
6
|
+
CallbackTransport,
|
|
7
|
+
} from '@backendkit-labs/agent-core';
|
|
8
|
+
import { OpenAICompatibleProvider } from './providers/openai-compatible';
|
|
9
|
+
import { loadConfig } from './config';
|
|
10
|
+
import { MY_AGENT_PROFILE } from './agents/my-agent';
|
|
11
|
+
import { exampleAction, exampleQuery } from './tools/example-tools';
|
|
12
|
+
|
|
13
|
+
export function createEngine(transport: CallbackTransport): AgentEngine {
|
|
14
|
+
const config = loadConfig();
|
|
15
|
+
|
|
16
|
+
const tools = new ToolRegistry();
|
|
17
|
+
tools
|
|
18
|
+
.register(exampleAction)
|
|
19
|
+
.register(exampleQuery);
|
|
20
|
+
// TODO: register more tools
|
|
21
|
+
|
|
22
|
+
const agents = new AgentRegistry();
|
|
23
|
+
agents.register(MY_AGENT_PROFILE);
|
|
24
|
+
// TODO: register more specialists
|
|
25
|
+
|
|
26
|
+
const providers = new ProviderRegistry();
|
|
27
|
+
providers.register('default', new OpenAICompatibleProvider({
|
|
28
|
+
apiKey: config.llmApiKey,
|
|
29
|
+
baseUrl: config.llmBaseUrl,
|
|
30
|
+
model: config.llmModel,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
return new AgentEngine({
|
|
34
|
+
model: { provider: 'default', id: config.llmModel },
|
|
35
|
+
agents,
|
|
36
|
+
tools,
|
|
37
|
+
providers,
|
|
38
|
+
defaultProvider: 'default',
|
|
39
|
+
defaultAgentId: 'my-agent',
|
|
40
|
+
transport,
|
|
41
|
+
maxIterations: 20,
|
|
42
|
+
iterationMode: 'auto',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import type { LLMProvider, LLMMessage, LLMStreamCallbacks, ToolDefinition } from '@backendkit-labs/agent-core';
|
|
3
|
+
|
|
4
|
+
export interface OpenAICompatibleProviderOptions {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
model: string;
|
|
8
|
+
maxRetries?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isRetryable(err: unknown): boolean {
|
|
12
|
+
if (err && typeof err === 'object' && 'status' in err) {
|
|
13
|
+
const s = (err as { status: number }).status;
|
|
14
|
+
return s === 429 || s >= 500;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const sleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));
|
|
20
|
+
|
|
21
|
+
export class OpenAICompatibleProvider implements LLMProvider {
|
|
22
|
+
private readonly client: OpenAI;
|
|
23
|
+
private readonly model: string;
|
|
24
|
+
private readonly maxRetries: number;
|
|
25
|
+
|
|
26
|
+
constructor(opts: OpenAICompatibleProviderOptions) {
|
|
27
|
+
this.client = new OpenAI({ apiKey: opts.apiKey, baseURL: opts.baseUrl, maxRetries: 0 });
|
|
28
|
+
this.model = opts.model;
|
|
29
|
+
this.maxRetries = opts.maxRetries ?? 3;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async chat(messages: LLMMessage[], tools: ToolDefinition[], callbacks: LLMStreamCallbacks): Promise<void> {
|
|
33
|
+
const oaiTools = tools.map(t => ({
|
|
34
|
+
type: 'function' as const,
|
|
35
|
+
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
36
|
+
})) as OpenAI.Chat.ChatCompletionTool[];
|
|
37
|
+
|
|
38
|
+
let attempt = 0;
|
|
39
|
+
let emitted = false;
|
|
40
|
+
|
|
41
|
+
while (attempt < this.maxRetries) {
|
|
42
|
+
attempt++;
|
|
43
|
+
try {
|
|
44
|
+
await this.stream(messages, oaiTools, {
|
|
45
|
+
...callbacks,
|
|
46
|
+
onChunk: (d) => { emitted = true; callbacks.onChunk?.(d); },
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (emitted || !isRetryable(err) || attempt >= this.maxRetries) {
|
|
51
|
+
callbacks.onError(err instanceof Error ? err : new Error(String(err)));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await sleep(Math.min(1000 * 2 ** attempt, 16_000));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private async stream(
|
|
60
|
+
messages: LLMMessage[],
|
|
61
|
+
oaiTools: OpenAI.Chat.ChatCompletionTool[],
|
|
62
|
+
callbacks: LLMStreamCallbacks,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
const stream = await this.client.chat.completions.create({
|
|
65
|
+
model: this.model,
|
|
66
|
+
messages: messages as OpenAI.Chat.ChatCompletionMessageParam[],
|
|
67
|
+
tools: oaiTools.length ? oaiTools : undefined,
|
|
68
|
+
tool_choice: oaiTools.length ? 'auto' : undefined,
|
|
69
|
+
stream: true,
|
|
70
|
+
stream_options: { include_usage: true },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
let content = '';
|
|
74
|
+
const toolBuffers = new Map<number, { id: string; name: string; args: string }>();
|
|
75
|
+
|
|
76
|
+
for await (const chunk of stream) {
|
|
77
|
+
if (chunk.usage) callbacks.onMetrics?.(chunk.usage.prompt_tokens, chunk.usage.completion_tokens);
|
|
78
|
+
const delta = chunk.choices[0]?.delta;
|
|
79
|
+
if (!delta) continue;
|
|
80
|
+
if (delta.content) { content += delta.content; callbacks.onChunk?.(delta.content); }
|
|
81
|
+
if (delta.tool_calls) {
|
|
82
|
+
for (const tc of delta.tool_calls) {
|
|
83
|
+
if (!toolBuffers.has(tc.index)) toolBuffers.set(tc.index, { id: '', name: '', args: '' });
|
|
84
|
+
const buf = toolBuffers.get(tc.index)!;
|
|
85
|
+
if (tc.id) buf.id = tc.id;
|
|
86
|
+
if (tc.function?.name) buf.name += tc.function.name;
|
|
87
|
+
if (tc.function?.arguments) buf.args += tc.function.arguments;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const toolCalls = [...toolBuffers.entries()]
|
|
93
|
+
.sort(([a], [b]) => a - b)
|
|
94
|
+
.map(([, b]) => ({ id: b.id, type: 'function' as const, function: { name: b.name, arguments: b.args } }));
|
|
95
|
+
|
|
96
|
+
for (const tc of toolCalls) callbacks.onToolCall?.(tc.function.name, tc.function.arguments, tc.id);
|
|
97
|
+
|
|
98
|
+
callbacks.onDone({
|
|
99
|
+
role: 'assistant',
|
|
100
|
+
content: content || null,
|
|
101
|
+
tool_calls: toolCalls.length ? toolCalls : undefined,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineTool, z } from '@backendkit-labs/agent-core';
|
|
2
|
+
|
|
3
|
+
// TODO: Replace these with tools that call your actual domain SDK/API
|
|
4
|
+
|
|
5
|
+
export const exampleAction = defineTool({
|
|
6
|
+
name: 'example_action',
|
|
7
|
+
description: 'TODO: describe what this tool does',
|
|
8
|
+
input: z.object({
|
|
9
|
+
target: z.string().describe('Target to act on'),
|
|
10
|
+
// TODO: add more parameters
|
|
11
|
+
}),
|
|
12
|
+
execute: async ({ target }) => {
|
|
13
|
+
// TODO: implement the actual action
|
|
14
|
+
return `Action executed on: ${target}`;
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const exampleQuery = defineTool({
|
|
19
|
+
name: 'example_query',
|
|
20
|
+
description: 'TODO: describe what this query returns',
|
|
21
|
+
input: z.object({
|
|
22
|
+
filter: z.string().optional().describe('Optional filter'),
|
|
23
|
+
}),
|
|
24
|
+
execute: async ({ filter }) => {
|
|
25
|
+
// TODO: implement the actual query
|
|
26
|
+
return JSON.stringify({ results: [], filter });
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"outDir": "dist",
|
|
9
|
+
"rootDir": "src",
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"resolveJsonModule": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
3
|
+
import { createEngine } from './engine';
|
|
4
|
+
|
|
5
|
+
const transport = new CallbackTransport((event) => {
|
|
6
|
+
if (event.type === 'token') process.stdout.write(event.content);
|
|
7
|
+
if (event.type === 'tool_call') console.log(`\n⚙ ${event.name}`);
|
|
8
|
+
if (event.type === 'agent_switch') console.log(`\n→ ${event.to_name ?? event.to}`);
|
|
9
|
+
if (event.type === 'block_end') console.log();
|
|
10
|
+
if (event.type === 'error') console.error(`\nError: ${event.message}`);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const engine = createEngine(transport);
|
|
14
|
+
|
|
15
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
16
|
+
|
|
17
|
+
function prompt() {
|
|
18
|
+
rl.question('\n> ', async (input) => {
|
|
19
|
+
const trimmed = input.trim();
|
|
20
|
+
if (!trimmed || trimmed === '/exit') { rl.close(); return; }
|
|
21
|
+
await engine.run(trimmed);
|
|
22
|
+
prompt();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log('Agent ready. Type your request or /exit to quit.\n');
|
|
27
|
+
prompt();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { AgentMCPServer } from '@backendkit-labs/mcp-server';
|
|
3
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { createEngine } from './engine';
|
|
6
|
+
|
|
7
|
+
const server = new AgentMCPServer({
|
|
8
|
+
name: process.env.npm_package_name ?? 'my-agent',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
port: parseInt(process.env.MCP_PORT ?? '3001'),
|
|
11
|
+
|
|
12
|
+
tools: [
|
|
13
|
+
{
|
|
14
|
+
name: 'agent_request',
|
|
15
|
+
description: 'TODO: describe what requests this agent handles',
|
|
16
|
+
schema: {
|
|
17
|
+
request: z.string().describe('Natural-language request for the agent'),
|
|
18
|
+
},
|
|
19
|
+
buildPrompt: ({ request }: { request: string }) => request,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
engineFactory: (transport: CallbackTransport) => createEngine(transport),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
void (async () => { await server.start(); })();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { AgentMCPServer } from '@backendkit-labs/mcp-server';
|
|
3
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { createEngine } from './engine';
|
|
6
|
+
|
|
7
|
+
const server = new AgentMCPServer({
|
|
8
|
+
name: process.env.npm_package_name ?? 'my-agent',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
port: parseInt(process.env.MCP_PORT ?? '3001'),
|
|
11
|
+
|
|
12
|
+
tools: [
|
|
13
|
+
{
|
|
14
|
+
name: 'agent_request',
|
|
15
|
+
description: 'TODO: describe what requests this agent handles',
|
|
16
|
+
schema: {
|
|
17
|
+
request: z.string().describe('Natural-language request for the agent'),
|
|
18
|
+
},
|
|
19
|
+
buildPrompt: ({ request }: { request: string }) => request,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
engineFactory: (transport: CallbackTransport) => createEngine(transport),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
void (async () => { await server.start(); })();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { AgentMCPServer } from '@backendkit-labs/mcp-server';
|
|
3
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { createEngine } from './engine';
|
|
6
|
+
|
|
7
|
+
const server = new AgentMCPServer({
|
|
8
|
+
name: process.env.npm_package_name ?? 'my-agent',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
|
|
11
|
+
tools: [
|
|
12
|
+
{
|
|
13
|
+
name: 'agent_request',
|
|
14
|
+
description: 'TODO: describe what requests this agent handles',
|
|
15
|
+
schema: {
|
|
16
|
+
request: z.string().describe('Natural-language request for the agent'),
|
|
17
|
+
},
|
|
18
|
+
buildPrompt: ({ request }: { request: string }) => request,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
|
|
22
|
+
engineFactory: (transport: CallbackTransport) => createEngine(transport),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
void (async () => { await server.startStdio(); })();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
3
|
+
import { createEngine } from './engine';
|
|
4
|
+
|
|
5
|
+
const transport = new CallbackTransport((event) => {
|
|
6
|
+
if (event.type === 'token') process.stdout.write(event.content);
|
|
7
|
+
if (event.type === 'tool_call') console.log(`\n⚙ ${event.name}`);
|
|
8
|
+
if (event.type === 'agent_switch') console.log(`\n→ ${event.to_name ?? event.to}`);
|
|
9
|
+
if (event.type === 'block_end') console.log();
|
|
10
|
+
if (event.type === 'error') console.error(`\nError: ${event.message}`);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const engine = createEngine(transport);
|
|
14
|
+
|
|
15
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
16
|
+
|
|
17
|
+
function prompt() {
|
|
18
|
+
rl.question('\n> ', async (input) => {
|
|
19
|
+
const trimmed = input.trim();
|
|
20
|
+
if (!trimmed || trimmed === '/exit') { rl.close(); return; }
|
|
21
|
+
await engine.run(trimmed);
|
|
22
|
+
prompt();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log('Agent ready. Type your request or /exit to quit.\n');
|
|
27
|
+
prompt();
|