@alejandroroman/agent-kit 0.1.3 → 0.2.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/dist/_memory/dist/config.d.ts +14 -0
- package/dist/_memory/dist/config.js +16 -0
- package/dist/_memory/dist/db/client.d.ts +2 -0
- package/dist/_memory/dist/db/client.js +15 -0
- package/dist/_memory/dist/db/schema.d.ts +14 -0
- package/dist/_memory/dist/db/schema.js +51 -0
- package/dist/_memory/dist/embeddings/ollama.d.ts +12 -0
- package/dist/_memory/dist/embeddings/ollama.js +22 -0
- package/dist/_memory/dist/embeddings/provider.d.ts +4 -0
- package/dist/_memory/dist/embeddings/provider.js +1 -0
- package/dist/_memory/dist/index.d.ts +10 -0
- package/dist/_memory/dist/index.js +6 -0
- package/dist/_memory/dist/search.d.ts +30 -0
- package/dist/_memory/dist/search.js +121 -0
- package/dist/_memory/dist/server.d.ts +8 -0
- package/dist/_memory/dist/server.js +126 -0
- package/dist/_memory/dist/store.d.ts +51 -0
- package/dist/_memory/dist/store.js +115 -0
- package/dist/_memory/server.js +0 -0
- package/dist/agent/loop.js +210 -111
- package/dist/api/errors.d.ts +3 -0
- package/dist/api/errors.js +37 -0
- package/dist/api/events.d.ts +5 -0
- package/dist/api/events.js +28 -0
- package/dist/api/router.js +10 -0
- package/dist/api/traces.d.ts +3 -0
- package/dist/api/traces.js +35 -0
- package/dist/api/types.d.ts +2 -0
- package/dist/bootstrap.d.ts +6 -5
- package/dist/bootstrap.js +26 -7
- package/dist/cli/chat.js +18 -63
- package/dist/cli/claude-md-template.d.ts +5 -0
- package/dist/cli/claude-md-template.js +220 -0
- package/dist/cli/config-writer.js +3 -0
- package/dist/cli/create.js +1 -4
- package/dist/cli/env.d.ts +14 -0
- package/dist/cli/env.js +68 -0
- package/dist/cli/init.js +14 -7
- package/dist/cli/list.js +1 -2
- package/dist/cli/paths.d.ts +3 -0
- package/dist/cli/paths.js +4 -0
- package/dist/cli/repl.d.ts +23 -0
- package/dist/cli/repl.js +73 -0
- package/dist/cli/slack-setup.d.ts +6 -0
- package/dist/cli/slack-setup.js +234 -0
- package/dist/cli/start.js +96 -96
- package/dist/cli/ui.d.ts +2 -2
- package/dist/cli/ui.js +5 -5
- package/dist/cli/validate.js +1 -4
- package/dist/cli/whats-new.d.ts +1 -0
- package/dist/cli/whats-new.js +69 -0
- package/dist/cli.js +14 -0
- package/dist/config/resolve.d.ts +1 -0
- package/dist/config/resolve.js +1 -0
- package/dist/config/schema.d.ts +2 -0
- package/dist/config/schema.js +1 -0
- package/dist/config/writer.d.ts +18 -0
- package/dist/config/writer.js +85 -0
- package/dist/cron/scheduler.d.ts +4 -1
- package/dist/cron/scheduler.js +99 -52
- package/dist/gateways/slack/client.d.ts +1 -0
- package/dist/gateways/slack/client.js +9 -0
- package/dist/gateways/slack/handler.js +2 -1
- package/dist/gateways/slack/index.js +75 -29
- package/dist/gateways/slack/listener.d.ts +8 -1
- package/dist/gateways/slack/listener.js +36 -10
- package/dist/heartbeat/runner.js +99 -82
- package/dist/index.js +4 -209
- package/dist/llm/anthropic.d.ts +1 -0
- package/dist/llm/anthropic.js +11 -2
- package/dist/llm/fallback.js +34 -2
- package/dist/llm/openai.d.ts +2 -0
- package/dist/llm/openai.js +33 -2
- package/dist/llm/types.d.ts +16 -2
- package/dist/llm/types.js +9 -0
- package/dist/logger.js +8 -0
- package/dist/media/sanitize.d.ts +5 -0
- package/dist/media/sanitize.js +53 -0
- package/dist/multi/spawn.js +29 -10
- package/dist/session/compaction.js +3 -1
- package/dist/session/prune-images.d.ts +9 -0
- package/dist/session/prune-images.js +42 -0
- package/dist/skills/activate.d.ts +6 -0
- package/dist/skills/activate.js +72 -27
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/telemetry/db.d.ts +63 -0
- package/dist/telemetry/db.js +193 -0
- package/dist/telemetry/index.d.ts +17 -0
- package/dist/telemetry/index.js +82 -0
- package/dist/telemetry/sanitize.d.ts +6 -0
- package/dist/telemetry/sanitize.js +48 -0
- package/dist/telemetry/sqlite-processor.d.ts +11 -0
- package/dist/telemetry/sqlite-processor.js +108 -0
- package/dist/telemetry/types.d.ts +30 -0
- package/dist/telemetry/types.js +31 -0
- package/dist/tools/builtin/index.d.ts +2 -0
- package/dist/tools/builtin/index.js +2 -0
- package/dist/tools/builtin/self-config.d.ts +4 -0
- package/dist/tools/builtin/self-config.js +182 -0
- package/dist/tools/registry.js +8 -1
- package/package.json +26 -20
package/dist/cli/chat.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import * as readline from "readline";
|
|
2
|
-
import * as path from "path";
|
|
3
1
|
import { loadConfig } from "../config/index.js";
|
|
4
|
-
import { buildAgentRuntime
|
|
5
|
-
import { runAgentLoop } from "../agent/loop.js";
|
|
6
|
-
import { compactMessages } from "../session/compaction.js";
|
|
2
|
+
import { buildAgentRuntime } from "../bootstrap.js";
|
|
7
3
|
import { ensureOllama } from "./ollama.js";
|
|
8
4
|
import { resolveApiKey } from "./ui.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
import { showWhatsNew } from "./whats-new.js";
|
|
6
|
+
import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
|
|
7
|
+
import { startRepl } from "./repl.js";
|
|
12
8
|
export async function chat(agentName) {
|
|
13
9
|
if (!agentName) {
|
|
14
10
|
const config = loadConfig(CONFIG_PATH);
|
|
@@ -24,20 +20,20 @@ export async function chat(agentName) {
|
|
|
24
20
|
}
|
|
25
21
|
}
|
|
26
22
|
await resolveApiKey({ save: false });
|
|
23
|
+
showWhatsNew();
|
|
27
24
|
const config = loadConfig(CONFIG_PATH);
|
|
28
25
|
if (!config.agents[agentName]) {
|
|
29
26
|
const names = Object.keys(config.agents).join(", ");
|
|
30
27
|
console.error(`Agent "${agentName}" not found. Available: ${names}`);
|
|
31
28
|
process.exit(1);
|
|
32
29
|
}
|
|
33
|
-
// Warn if Ollama isn't running (memory tools won't work)
|
|
34
30
|
if (config.defaults.memory) {
|
|
35
31
|
const ollama = await ensureOllama();
|
|
36
32
|
if (!ollama.running) {
|
|
37
33
|
console.log(" (Ollama not running — memory tools unavailable)\n");
|
|
38
34
|
}
|
|
39
35
|
}
|
|
40
|
-
const runtime = buildAgentRuntime(agentName, config, {
|
|
36
|
+
const runtime = await buildAgentRuntime(agentName, config, {
|
|
41
37
|
dataDir: DATA_DIR,
|
|
42
38
|
skillsDir: SKILLS_DIR,
|
|
43
39
|
});
|
|
@@ -54,58 +50,17 @@ export async function chat(agentName) {
|
|
|
54
50
|
}
|
|
55
51
|
console.log(` Commands: /new (reset), /quit`);
|
|
56
52
|
console.log();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
startRepl({
|
|
54
|
+
agentName,
|
|
55
|
+
model: resolved.model,
|
|
56
|
+
fallbacks: resolved.fallbacks,
|
|
57
|
+
maxIterations: resolved.maxIterations,
|
|
58
|
+
compactionThreshold: resolved.compactionThreshold,
|
|
59
|
+
maxToolResultSize: resolved.maxToolResultSize,
|
|
60
|
+
toolRegistry,
|
|
61
|
+
session,
|
|
62
|
+
soul,
|
|
63
|
+
skillsIndex,
|
|
64
|
+
promptFragments,
|
|
61
65
|
});
|
|
62
|
-
const ask = () => {
|
|
63
|
-
rl.question("You: ", async (input) => {
|
|
64
|
-
const trimmed = input.trim();
|
|
65
|
-
if (trimmed === "/quit") {
|
|
66
|
-
rl.close();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (trimmed === "/new") {
|
|
70
|
-
messages = [];
|
|
71
|
-
console.log(" Session reset.\n");
|
|
72
|
-
ask();
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (!trimmed) {
|
|
76
|
-
ask();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const userMsg = { role: "user", content: trimmed };
|
|
80
|
-
messages.push(userMsg);
|
|
81
|
-
session.append(userMsg);
|
|
82
|
-
messages = await compactMessages(messages, resolved.model, resolved.compactionThreshold);
|
|
83
|
-
try {
|
|
84
|
-
const systemPrompt = buildSystemPrompt(soul, skillsIndex, promptFragments);
|
|
85
|
-
const result = await runAgentLoop(messages, {
|
|
86
|
-
model: resolved.model,
|
|
87
|
-
fallbacks: resolved.fallbacks,
|
|
88
|
-
systemPrompt,
|
|
89
|
-
toolRegistry,
|
|
90
|
-
maxIterations: resolved.maxIterations,
|
|
91
|
-
compactionThreshold: resolved.compactionThreshold,
|
|
92
|
-
maxToolResultSize: resolved.maxToolResultSize,
|
|
93
|
-
agentName,
|
|
94
|
-
source: "cli",
|
|
95
|
-
});
|
|
96
|
-
const newMessages = result.messages.slice(messages.length - 1);
|
|
97
|
-
for (const msg of newMessages) {
|
|
98
|
-
session.append(msg);
|
|
99
|
-
}
|
|
100
|
-
messages = result.messages;
|
|
101
|
-
console.log(`\nAgent: ${result.text}`);
|
|
102
|
-
console.log(` (${result.usage.inputTokens + result.usage.outputTokens} tokens)\n`);
|
|
103
|
-
}
|
|
104
|
-
catch (err) {
|
|
105
|
-
console.error(`\n Error: ${err instanceof Error ? err.message : err}\n`);
|
|
106
|
-
}
|
|
107
|
-
ask();
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
ask();
|
|
111
66
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDE.md template generated by `agent-kit init`.
|
|
3
|
+
* Gives any Claude Code session full context for working on this project.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CLAUDE_MD_TEMPLATE = "# Agent Kit Project\n\nPersonal AI agent framework \u2014 multi-model agent loop with Slack gateway, cron scheduling, skills, and tool sandboxing.\n\nPowered by [`@alejandroroman/agent-kit`](https://www.npmjs.com/package/@alejandroroman/agent-kit).\n\n## Run Commands\n\n- `pnpm start` \u2014 start all services (cron, Slack, heartbeat, REPL)\n- `pnpm chat` \u2014 lightweight single-agent chat\n- `pnpm create` \u2014 AI-powered agent creation wizard\n\n## Project Layout\n\n```\nagent-kit.json # Main config \u2014 agents, cron jobs, models, defaults\n.env # Secrets (API keys, Slack tokens)\ndata/ # Runtime data (per-agent sessions, SOUL.md files, DBs)\n agents/<name>/\n SOUL.md # Agent personality / system prompt\n sessions/ # JSONL conversation logs\nskills/ # Custom skill definitions\n <skill-name>/\n skill.json # Manifest (name, description, tools list)\n SKILL.md # Instructions injected when skill is activated\n tools/ # Tool implementations (TypeScript)\n```\n\n## Configuration (agent-kit.json)\n\n### Models\n\n```json\n\"models\": [\n { \"model\": \"anthropic:claude-sonnet-4-6\", \"alias\": \"sonnet\" },\n { \"model\": \"anthropic:claude-haiku-4-5-20251001\", \"alias\": \"haiku\" }\n]\n```\n\nAgents reference models by alias. The framework handles automatic fallback between models on rate limits/server errors.\n\n### Agent Definition\n\n```json\n\"agents\": {\n \"my-agent\": {\n \"displayName\": \"My Agent\",\n \"emoji\": \"\uD83E\uDD16\",\n \"model\": \"sonnet\",\n \"tools\": [\"read_file\", \"write_file\", \"web_search\"],\n \"skills\": [\"my-skill\"],\n \"sandbox\": { \"allowedPaths\": [\"data/agents/my-agent/\"] },\n \"slack\": { \"channelId\": \"C0XXX\", \"channelName\": \"#my-channel\" },\n \"heartbeat\": { \"enabled\": true, \"intervalMinutes\": 60, \"model\": \"haiku\" },\n \"can_spawn\": [{ \"agent\": \"helper\", \"tool\": \"delegate\", \"description\": \"...\" }]\n }\n}\n```\n\n**Key fields:**\n- `tools` \u2014 builtin tools the agent can use (see list below)\n- `skills` \u2014 skill directories to make available via `activate_skill`\n- `sandbox` \u2014 restrict file access (`allowedPaths`) or commands (`allowedCommands`)\n- `slack` \u2014 bind agent to a Slack channel for inbound/outbound messages\n- `heartbeat` \u2014 periodic autonomous check-ins (posts to Slack if something noteworthy)\n- `spawn_only: true` \u2014 agent can only be invoked by other agents, not directly\n\n### Cron Jobs\n\n```json\n\"cron\": [\n {\n \"id\": \"daily-task\",\n \"agent\": \"my-agent\",\n \"schedule\": \"0 8 * * *\",\n \"prompt\": \"Activate the my-skill skill. Do the daily task.\",\n \"enabled\": true\n }\n]\n```\n\nSchedule uses standard cron syntax. Agents must activate their skill first in the prompt.\n\n### Defaults\n\n```json\n\"defaults\": {\n \"model\": \"sonnet\",\n \"maxTokens\": 4096,\n \"maxIterations\": 20,\n \"memory\": {\n \"dbPath\": \"./data/memory.db\",\n \"ollamaEndpoint\": \"http://localhost:11434\",\n \"ollamaModel\": \"all-minilm:l6-v2\"\n }\n}\n```\n\n## Built-in Tools\n\n| Tool | Description |\n|------|-------------|\n| `read_file` | Read a file (respects sandbox allowedPaths) |\n| `write_file` | Write a file (respects sandbox allowedPaths) |\n| `run_command` | Execute a shell command (respects sandbox allowedCommands) |\n| `web_search` | Search the web (Brave or Grok provider) |\n| `store_memory` | Store a memory with content and metadata |\n| `search_memory` | Semantic search across stored memories |\n| `get_memory` | Retrieve a specific memory by ID |\n| `update_memory` | Update an existing memory |\n| `forget_memory` | Delete a memory |\n| `list_memories` | List all memories with optional filtering |\n| `update_agent_config` | Agent updates its own config (allowlisted fields) |\n| `manage_cron` | Agent manages its own cron jobs (CRUD) |\n\nMemory tools require Ollama running with `all-minilm:l6-v2` model.\n\n## Creating Skills\n\nA skill is a directory in `skills/` with:\n\n1. **`skill.json`** \u2014 manifest\n```json\n{\n \"name\": \"my-skill\",\n \"description\": \"What this skill does\",\n \"tools\": [\"my_tool\"],\n \"prompt\": \"SKILL.md\"\n}\n```\n\n2. **`SKILL.md`** \u2014 instructions injected into agent context when activated\n\n3. **`tools/<name>.ts`** \u2014 tool implementations\n```typescript\nimport type { Tool } from \"@alejandroroman/agent-kit/dist/tools/types.js\";\n\nconst tool: Tool = {\n name: \"my_tool\",\n description: \"What this tool does\",\n parameters: {\n type: \"object\",\n properties: {\n input: { type: \"string\", description: \"The input\" }\n },\n required: [\"input\"]\n },\n execute: async (args) => {\n return \"result\";\n },\n};\n\nexport default tool;\n```\n\n## Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `ANTHROPIC_API_KEY` | Yes | Anthropic API key for Claude models |\n| `OPENAI_API_KEY` | No | OpenAI API key (for fallback models) |\n| `SLACK_BOT_TOKEN` | No | Slack bot token (xoxb-...) for gateway |\n| `SLACK_APP_TOKEN` | No | Slack app token (xapp-...) for Socket Mode |\n| `BRAVE_SEARCH_API_KEY` | No | Brave Search API key for web_search |\n| `XAI_API_KEY` | No | xAI/Grok API key for web_search |\n\n## Slack Setup\n\n1. Create a Slack app at api.slack.com/apps \u2192 From Manifest (YAML):\n```yaml\ndisplay_information:\n name: Agent Kit\nsettings:\n socket_mode_enabled: true\nfeatures:\n bot_user:\n display_name: Agent Kit\n always_online: true\n event_subscriptions:\n bot_events:\n - message.channels\n - message.groups\noauth_config:\n scopes:\n bot:\n - chat:write\n - channels:history\n - channels:read\n - groups:history\n - groups:read\n```\n2. Generate an App-Level Token (Settings \u2192 Basic Information) with `connections:write` scope \u2192 `SLACK_APP_TOKEN`\n3. Install to workspace \u2192 copy Bot Token \u2192 `SLACK_BOT_TOKEN`\n4. Create channels, invite the bot (`/invite @Agent Kit`), copy channel IDs\n5. Add `slack` binding to each agent in `agent-kit.json`\n\n## Ollama (for Memory)\n\nMemory tools use Ollama for local embeddings:\n```bash\n# Install: https://ollama.com\nollama pull all-minilm:l6-v2\n# Ollama must be running when agent-kit starts\n```\n\nIf Ollama isn't running, everything else works \u2014 memory tools are just unavailable.\n\n## pnpm v10 Note\n\nIf using pnpm v10+, native dependencies need explicit approval:\n```json\n// In package.json:\n\"pnpm\": { \"onlyBuiltDependencies\": [\"better-sqlite3\"] }\n```\nThen `pnpm install` to rebuild native bindings.\n";
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDE.md template generated by `agent-kit init`.
|
|
3
|
+
* Gives any Claude Code session full context for working on this project.
|
|
4
|
+
*/
|
|
5
|
+
export const CLAUDE_MD_TEMPLATE = `# Agent Kit Project
|
|
6
|
+
|
|
7
|
+
Personal AI agent framework — multi-model agent loop with Slack gateway, cron scheduling, skills, and tool sandboxing.
|
|
8
|
+
|
|
9
|
+
Powered by [\`@alejandroroman/agent-kit\`](https://www.npmjs.com/package/@alejandroroman/agent-kit).
|
|
10
|
+
|
|
11
|
+
## Run Commands
|
|
12
|
+
|
|
13
|
+
- \`pnpm start\` — start all services (cron, Slack, heartbeat, REPL)
|
|
14
|
+
- \`pnpm chat\` — lightweight single-agent chat
|
|
15
|
+
- \`pnpm create\` — AI-powered agent creation wizard
|
|
16
|
+
|
|
17
|
+
## Project Layout
|
|
18
|
+
|
|
19
|
+
\`\`\`
|
|
20
|
+
agent-kit.json # Main config — agents, cron jobs, models, defaults
|
|
21
|
+
.env # Secrets (API keys, Slack tokens)
|
|
22
|
+
data/ # Runtime data (per-agent sessions, SOUL.md files, DBs)
|
|
23
|
+
agents/<name>/
|
|
24
|
+
SOUL.md # Agent personality / system prompt
|
|
25
|
+
sessions/ # JSONL conversation logs
|
|
26
|
+
skills/ # Custom skill definitions
|
|
27
|
+
<skill-name>/
|
|
28
|
+
skill.json # Manifest (name, description, tools list)
|
|
29
|
+
SKILL.md # Instructions injected when skill is activated
|
|
30
|
+
tools/ # Tool implementations (TypeScript)
|
|
31
|
+
\`\`\`
|
|
32
|
+
|
|
33
|
+
## Configuration (agent-kit.json)
|
|
34
|
+
|
|
35
|
+
### Models
|
|
36
|
+
|
|
37
|
+
\`\`\`json
|
|
38
|
+
"models": [
|
|
39
|
+
{ "model": "anthropic:claude-sonnet-4-6", "alias": "sonnet" },
|
|
40
|
+
{ "model": "anthropic:claude-haiku-4-5-20251001", "alias": "haiku" }
|
|
41
|
+
]
|
|
42
|
+
\`\`\`
|
|
43
|
+
|
|
44
|
+
Agents reference models by alias. The framework handles automatic fallback between models on rate limits/server errors.
|
|
45
|
+
|
|
46
|
+
### Agent Definition
|
|
47
|
+
|
|
48
|
+
\`\`\`json
|
|
49
|
+
"agents": {
|
|
50
|
+
"my-agent": {
|
|
51
|
+
"displayName": "My Agent",
|
|
52
|
+
"emoji": "🤖",
|
|
53
|
+
"model": "sonnet",
|
|
54
|
+
"tools": ["read_file", "write_file", "web_search"],
|
|
55
|
+
"skills": ["my-skill"],
|
|
56
|
+
"sandbox": { "allowedPaths": ["data/agents/my-agent/"] },
|
|
57
|
+
"slack": { "channelId": "C0XXX", "channelName": "#my-channel" },
|
|
58
|
+
"heartbeat": { "enabled": true, "intervalMinutes": 60, "model": "haiku" },
|
|
59
|
+
"can_spawn": [{ "agent": "helper", "tool": "delegate", "description": "..." }]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
\`\`\`
|
|
63
|
+
|
|
64
|
+
**Key fields:**
|
|
65
|
+
- \`tools\` — builtin tools the agent can use (see list below)
|
|
66
|
+
- \`skills\` — skill directories to make available via \`activate_skill\`
|
|
67
|
+
- \`sandbox\` — restrict file access (\`allowedPaths\`) or commands (\`allowedCommands\`)
|
|
68
|
+
- \`slack\` — bind agent to a Slack channel for inbound/outbound messages
|
|
69
|
+
- \`heartbeat\` — periodic autonomous check-ins (posts to Slack if something noteworthy)
|
|
70
|
+
- \`spawn_only: true\` — agent can only be invoked by other agents, not directly
|
|
71
|
+
|
|
72
|
+
### Cron Jobs
|
|
73
|
+
|
|
74
|
+
\`\`\`json
|
|
75
|
+
"cron": [
|
|
76
|
+
{
|
|
77
|
+
"id": "daily-task",
|
|
78
|
+
"agent": "my-agent",
|
|
79
|
+
"schedule": "0 8 * * *",
|
|
80
|
+
"prompt": "Activate the my-skill skill. Do the daily task.",
|
|
81
|
+
"enabled": true
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
\`\`\`
|
|
85
|
+
|
|
86
|
+
Schedule uses standard cron syntax. Agents must activate their skill first in the prompt.
|
|
87
|
+
|
|
88
|
+
### Defaults
|
|
89
|
+
|
|
90
|
+
\`\`\`json
|
|
91
|
+
"defaults": {
|
|
92
|
+
"model": "sonnet",
|
|
93
|
+
"maxTokens": 4096,
|
|
94
|
+
"maxIterations": 20,
|
|
95
|
+
"memory": {
|
|
96
|
+
"dbPath": "./data/memory.db",
|
|
97
|
+
"ollamaEndpoint": "http://localhost:11434",
|
|
98
|
+
"ollamaModel": "all-minilm:l6-v2"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
\`\`\`
|
|
102
|
+
|
|
103
|
+
## Built-in Tools
|
|
104
|
+
|
|
105
|
+
| Tool | Description |
|
|
106
|
+
|------|-------------|
|
|
107
|
+
| \`read_file\` | Read a file (respects sandbox allowedPaths) |
|
|
108
|
+
| \`write_file\` | Write a file (respects sandbox allowedPaths) |
|
|
109
|
+
| \`run_command\` | Execute a shell command (respects sandbox allowedCommands) |
|
|
110
|
+
| \`web_search\` | Search the web (Brave or Grok provider) |
|
|
111
|
+
| \`store_memory\` | Store a memory with content and metadata |
|
|
112
|
+
| \`search_memory\` | Semantic search across stored memories |
|
|
113
|
+
| \`get_memory\` | Retrieve a specific memory by ID |
|
|
114
|
+
| \`update_memory\` | Update an existing memory |
|
|
115
|
+
| \`forget_memory\` | Delete a memory |
|
|
116
|
+
| \`list_memories\` | List all memories with optional filtering |
|
|
117
|
+
| \`update_agent_config\` | Agent updates its own config (allowlisted fields) |
|
|
118
|
+
| \`manage_cron\` | Agent manages its own cron jobs (CRUD) |
|
|
119
|
+
|
|
120
|
+
Memory tools require Ollama running with \`all-minilm:l6-v2\` model.
|
|
121
|
+
|
|
122
|
+
## Creating Skills
|
|
123
|
+
|
|
124
|
+
A skill is a directory in \`skills/\` with:
|
|
125
|
+
|
|
126
|
+
1. **\`skill.json\`** — manifest
|
|
127
|
+
\`\`\`json
|
|
128
|
+
{
|
|
129
|
+
"name": "my-skill",
|
|
130
|
+
"description": "What this skill does",
|
|
131
|
+
"tools": ["my_tool"],
|
|
132
|
+
"prompt": "SKILL.md"
|
|
133
|
+
}
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
2. **\`SKILL.md\`** — instructions injected into agent context when activated
|
|
137
|
+
|
|
138
|
+
3. **\`tools/<name>.ts\`** — tool implementations
|
|
139
|
+
\`\`\`typescript
|
|
140
|
+
import type { Tool } from "@alejandroroman/agent-kit/dist/tools/types.js";
|
|
141
|
+
|
|
142
|
+
const tool: Tool = {
|
|
143
|
+
name: "my_tool",
|
|
144
|
+
description: "What this tool does",
|
|
145
|
+
parameters: {
|
|
146
|
+
type: "object",
|
|
147
|
+
properties: {
|
|
148
|
+
input: { type: "string", description: "The input" }
|
|
149
|
+
},
|
|
150
|
+
required: ["input"]
|
|
151
|
+
},
|
|
152
|
+
execute: async (args) => {
|
|
153
|
+
return "result";
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export default tool;
|
|
158
|
+
\`\`\`
|
|
159
|
+
|
|
160
|
+
## Environment Variables
|
|
161
|
+
|
|
162
|
+
| Variable | Required | Description |
|
|
163
|
+
|----------|----------|-------------|
|
|
164
|
+
| \`ANTHROPIC_API_KEY\` | Yes | Anthropic API key for Claude models |
|
|
165
|
+
| \`OPENAI_API_KEY\` | No | OpenAI API key (for fallback models) |
|
|
166
|
+
| \`SLACK_BOT_TOKEN\` | No | Slack bot token (xoxb-...) for gateway |
|
|
167
|
+
| \`SLACK_APP_TOKEN\` | No | Slack app token (xapp-...) for Socket Mode |
|
|
168
|
+
| \`BRAVE_SEARCH_API_KEY\` | No | Brave Search API key for web_search |
|
|
169
|
+
| \`XAI_API_KEY\` | No | xAI/Grok API key for web_search |
|
|
170
|
+
|
|
171
|
+
## Slack Setup
|
|
172
|
+
|
|
173
|
+
1. Create a Slack app at api.slack.com/apps → From Manifest (YAML):
|
|
174
|
+
\`\`\`yaml
|
|
175
|
+
display_information:
|
|
176
|
+
name: Agent Kit
|
|
177
|
+
settings:
|
|
178
|
+
socket_mode_enabled: true
|
|
179
|
+
features:
|
|
180
|
+
bot_user:
|
|
181
|
+
display_name: Agent Kit
|
|
182
|
+
always_online: true
|
|
183
|
+
event_subscriptions:
|
|
184
|
+
bot_events:
|
|
185
|
+
- message.channels
|
|
186
|
+
- message.groups
|
|
187
|
+
oauth_config:
|
|
188
|
+
scopes:
|
|
189
|
+
bot:
|
|
190
|
+
- chat:write
|
|
191
|
+
- channels:history
|
|
192
|
+
- channels:read
|
|
193
|
+
- groups:history
|
|
194
|
+
- groups:read
|
|
195
|
+
\`\`\`
|
|
196
|
+
2. Generate an App-Level Token (Settings → Basic Information) with \`connections:write\` scope → \`SLACK_APP_TOKEN\`
|
|
197
|
+
3. Install to workspace → copy Bot Token → \`SLACK_BOT_TOKEN\`
|
|
198
|
+
4. Create channels, invite the bot (\`/invite @Agent Kit\`), copy channel IDs
|
|
199
|
+
5. Add \`slack\` binding to each agent in \`agent-kit.json\`
|
|
200
|
+
|
|
201
|
+
## Ollama (for Memory)
|
|
202
|
+
|
|
203
|
+
Memory tools use Ollama for local embeddings:
|
|
204
|
+
\`\`\`bash
|
|
205
|
+
# Install: https://ollama.com
|
|
206
|
+
ollama pull all-minilm:l6-v2
|
|
207
|
+
# Ollama must be running when agent-kit starts
|
|
208
|
+
\`\`\`
|
|
209
|
+
|
|
210
|
+
If Ollama isn't running, everything else works — memory tools are just unavailable.
|
|
211
|
+
|
|
212
|
+
## pnpm v10 Note
|
|
213
|
+
|
|
214
|
+
If using pnpm v10+, native dependencies need explicit approval:
|
|
215
|
+
\`\`\`json
|
|
216
|
+
// In package.json:
|
|
217
|
+
"pnpm": { "onlyBuiltDependencies": ["better-sqlite3"] }
|
|
218
|
+
\`\`\`
|
|
219
|
+
Then \`pnpm install\` to rebuild native bindings.
|
|
220
|
+
`;
|
|
@@ -103,6 +103,9 @@ export function scaffoldProjectPackageJson(projectDir, projectName) {
|
|
|
103
103
|
dependencies: {
|
|
104
104
|
"@alejandroroman/agent-kit": "^0.1.0",
|
|
105
105
|
},
|
|
106
|
+
pnpm: {
|
|
107
|
+
onlyBuiltDependencies: ["better-sqlite3"],
|
|
108
|
+
},
|
|
106
109
|
};
|
|
107
110
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
108
111
|
// Create .gitignore if missing
|
package/dist/cli/create.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
2
|
import * as p from "@clack/prompts";
|
|
4
3
|
import { banner, done, resolveApiKey } from "./ui.js";
|
|
5
4
|
import { loadConfig } from "../config/index.js";
|
|
6
5
|
import { runSetupAgent } from "./setup-agent/index.js";
|
|
7
|
-
|
|
8
|
-
const DATA_DIR = path.join(process.cwd(), "data");
|
|
9
|
-
const SKILLS_DIR = path.join(process.cwd(), "skills");
|
|
6
|
+
import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
|
|
10
7
|
export async function create() {
|
|
11
8
|
banner();
|
|
12
9
|
// Require existing config
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a .env file into a key-value map.
|
|
3
|
+
* Strips quotes, ignores comments and blank lines.
|
|
4
|
+
*/
|
|
5
|
+
export declare function loadEnvFile(envPath: string): Record<string, string>;
|
|
6
|
+
/**
|
|
7
|
+
* Upsert a key in a .env file. Replaces if exists, appends if not.
|
|
8
|
+
* Values are always quoted to prevent shell issues.
|
|
9
|
+
*/
|
|
10
|
+
export declare function upsertEnvKey(envPath: string, key: string, value: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Load specific keys from .env into process.env (if not already set).
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadEnvIntoProcess(envPath: string, keys: string[]): void;
|
package/dist/cli/env.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a .env file into a key-value map.
|
|
4
|
+
* Strips quotes, ignores comments and blank lines.
|
|
5
|
+
*/
|
|
6
|
+
export function loadEnvFile(envPath) {
|
|
7
|
+
if (!fs.existsSync(envPath))
|
|
8
|
+
return {};
|
|
9
|
+
const result = {};
|
|
10
|
+
const content = fs.readFileSync(envPath, "utf-8");
|
|
11
|
+
for (const line of content.split("\n")) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
14
|
+
continue;
|
|
15
|
+
const eqIndex = trimmed.indexOf("=");
|
|
16
|
+
if (eqIndex === -1)
|
|
17
|
+
continue;
|
|
18
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
19
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
20
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
21
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
22
|
+
value = value.slice(1, -1);
|
|
23
|
+
}
|
|
24
|
+
result[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Upsert a key in a .env file. Replaces if exists, appends if not.
|
|
30
|
+
* Values are always quoted to prevent shell issues.
|
|
31
|
+
*/
|
|
32
|
+
export function upsertEnvKey(envPath, key, value) {
|
|
33
|
+
const quotedLine = `${key}="${value}"`;
|
|
34
|
+
if (!fs.existsSync(envPath)) {
|
|
35
|
+
fs.writeFileSync(envPath, quotedLine + "\n", { mode: 0o600 });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const content = fs.readFileSync(envPath, "utf-8");
|
|
39
|
+
const lines = content.split("\n");
|
|
40
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
41
|
+
const pattern = new RegExp(`^${escaped}\\s*=`);
|
|
42
|
+
let replaced = false;
|
|
43
|
+
const updated = lines.map((line) => {
|
|
44
|
+
if (pattern.test(line.trim())) {
|
|
45
|
+
replaced = true;
|
|
46
|
+
return quotedLine;
|
|
47
|
+
}
|
|
48
|
+
return line;
|
|
49
|
+
});
|
|
50
|
+
if (!replaced) {
|
|
51
|
+
const needsNewline = content.length > 0 && !content.endsWith("\n");
|
|
52
|
+
fs.writeFileSync(envPath, content + (needsNewline ? "\n" : "") + quotedLine + "\n", { mode: 0o600 });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
fs.writeFileSync(envPath, updated.join("\n"), { mode: 0o600 });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Load specific keys from .env into process.env (if not already set).
|
|
60
|
+
*/
|
|
61
|
+
export function loadEnvIntoProcess(envPath, keys) {
|
|
62
|
+
const env = loadEnvFile(envPath);
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
if (!process.env[key] && env[key]) {
|
|
65
|
+
process.env[key] = env[key];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/cli/init.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
2
|
import { execSync } from "child_process";
|
|
4
3
|
import * as p from "@clack/prompts";
|
|
5
4
|
import { banner, isCancel, done, resolveApiKey } from "./ui.js";
|
|
6
5
|
import { checkOllama } from "./ollama.js";
|
|
7
6
|
import { createFreshConfig, scaffoldProjectPackageJson } from "./config-writer.js";
|
|
8
7
|
import { runSetupAgent } from "./setup-agent/index.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
import { setupSlack } from "./slack-setup.js";
|
|
9
|
+
import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
|
|
10
|
+
import { CLAUDE_MD_TEMPLATE } from "./claude-md-template.js";
|
|
11
|
+
import * as path from "path";
|
|
12
12
|
function detectPackageManager() {
|
|
13
13
|
try {
|
|
14
14
|
execSync("pnpm --version", { stdio: "ignore" });
|
|
@@ -43,6 +43,11 @@ export async function init() {
|
|
|
43
43
|
createFreshConfig({ configPath: CONFIG_PATH });
|
|
44
44
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
45
45
|
fs.mkdirSync(SKILLS_DIR, { recursive: true });
|
|
46
|
+
// Generate CLAUDE.md for Claude Code context
|
|
47
|
+
const claudeMdPath = path.join(process.cwd(), "CLAUDE.md");
|
|
48
|
+
if (!fs.existsSync(claudeMdPath)) {
|
|
49
|
+
fs.writeFileSync(claudeMdPath, CLAUDE_MD_TEMPLATE);
|
|
50
|
+
}
|
|
46
51
|
}
|
|
47
52
|
catch (err) {
|
|
48
53
|
s.stop(`Failed to create project: ${err instanceof Error ? err.message : err}`);
|
|
@@ -53,13 +58,13 @@ export async function init() {
|
|
|
53
58
|
const pm = detectPackageManager();
|
|
54
59
|
s.start(`Installing dependencies (${pm} install)...`);
|
|
55
60
|
try {
|
|
56
|
-
execSync(`${pm} install`, { cwd: process.cwd(), stdio: "
|
|
61
|
+
execSync(`${pm} install`, { cwd: process.cwd(), stdio: "pipe" });
|
|
62
|
+
s.stop("Dependencies installed");
|
|
57
63
|
}
|
|
58
64
|
catch (err) {
|
|
59
65
|
s.stop(`Failed to install dependencies: ${err instanceof Error ? err.message : err}`);
|
|
60
|
-
p.log.warn(
|
|
66
|
+
p.log.warn(`Run \`${pm} install\` manually before starting.`);
|
|
61
67
|
}
|
|
62
|
-
s.stop("Dependencies installed");
|
|
63
68
|
// Ask if they want to create agents now
|
|
64
69
|
const createNow = await p.confirm({
|
|
65
70
|
message: "Create your first agent now? (AI-powered setup)",
|
|
@@ -82,4 +87,6 @@ export async function init() {
|
|
|
82
87
|
});
|
|
83
88
|
console.log();
|
|
84
89
|
done(summary);
|
|
90
|
+
// Offer Slack setup after agent creation
|
|
91
|
+
await setupSlack({ configPath: CONFIG_PATH, fromInit: true });
|
|
85
92
|
}
|
package/dist/cli/list.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import * as path from "path";
|
|
2
1
|
import { loadConfig } from "../config/index.js";
|
|
3
|
-
|
|
2
|
+
import { CONFIG_PATH } from "./paths.js";
|
|
4
3
|
export function list() {
|
|
5
4
|
const config = loadConfig(CONFIG_PATH);
|
|
6
5
|
const agents = Object.entries(config.agents);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ToolRegistry } from "../tools/registry.js";
|
|
2
|
+
import type { SessionManager } from "../session/manager.js";
|
|
3
|
+
import type { UsageStore } from "../usage/index.js";
|
|
4
|
+
interface ReplOptions {
|
|
5
|
+
agentName: string;
|
|
6
|
+
model: string;
|
|
7
|
+
fallbacks: string[];
|
|
8
|
+
maxIterations: number;
|
|
9
|
+
compactionThreshold: number;
|
|
10
|
+
maxToolResultSize: number;
|
|
11
|
+
toolRegistry: ToolRegistry;
|
|
12
|
+
session: SessionManager;
|
|
13
|
+
soul: string | undefined;
|
|
14
|
+
skillsIndex: string;
|
|
15
|
+
promptFragments: string[];
|
|
16
|
+
usageStore?: UsageStore;
|
|
17
|
+
/** Extra commands beyond /quit and /new */
|
|
18
|
+
commands?: Record<string, () => void>;
|
|
19
|
+
/** Called on /quit before closing */
|
|
20
|
+
onQuit?: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare function startRepl(opts: ReplOptions): void;
|
|
23
|
+
export {};
|
package/dist/cli/repl.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as readline from "readline";
|
|
2
|
+
import { runAgentLoop } from "../agent/loop.js";
|
|
3
|
+
import { compactMessages } from "../session/compaction.js";
|
|
4
|
+
import { buildSystemPrompt } from "../bootstrap.js";
|
|
5
|
+
export function startRepl(opts) {
|
|
6
|
+
let messages = opts.session.getMessages();
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
const ask = () => {
|
|
12
|
+
rl.question("You: ", async (input) => {
|
|
13
|
+
const trimmed = input.trim();
|
|
14
|
+
if (trimmed === "/quit") {
|
|
15
|
+
try {
|
|
16
|
+
await opts.onQuit?.();
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.error(`Warning: cleanup error: ${err instanceof Error ? err.message : err}`);
|
|
20
|
+
}
|
|
21
|
+
rl.close();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (trimmed === "/new") {
|
|
25
|
+
messages = [];
|
|
26
|
+
console.log(" Session reset.\n");
|
|
27
|
+
ask();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Custom commands
|
|
31
|
+
if (opts.commands && trimmed in opts.commands) {
|
|
32
|
+
opts.commands[trimmed]();
|
|
33
|
+
ask();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!trimmed) {
|
|
37
|
+
ask();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const userMsg = { role: "user", content: trimmed };
|
|
41
|
+
messages.push(userMsg);
|
|
42
|
+
opts.session.append(userMsg);
|
|
43
|
+
messages = await compactMessages(messages, opts.model, opts.compactionThreshold);
|
|
44
|
+
try {
|
|
45
|
+
const systemPrompt = buildSystemPrompt(opts.soul, opts.skillsIndex, opts.promptFragments);
|
|
46
|
+
const result = await runAgentLoop(messages, {
|
|
47
|
+
model: opts.model,
|
|
48
|
+
fallbacks: opts.fallbacks,
|
|
49
|
+
systemPrompt,
|
|
50
|
+
toolRegistry: opts.toolRegistry,
|
|
51
|
+
maxIterations: opts.maxIterations,
|
|
52
|
+
compactionThreshold: opts.compactionThreshold,
|
|
53
|
+
maxToolResultSize: opts.maxToolResultSize,
|
|
54
|
+
agentName: opts.agentName,
|
|
55
|
+
usageStore: opts.usageStore,
|
|
56
|
+
source: "cli",
|
|
57
|
+
});
|
|
58
|
+
const newMessages = result.messages.slice(messages.length);
|
|
59
|
+
for (const msg of newMessages) {
|
|
60
|
+
opts.session.append(msg);
|
|
61
|
+
}
|
|
62
|
+
messages = result.messages;
|
|
63
|
+
console.log(`\nAgent: ${result.text}`);
|
|
64
|
+
console.log(` (${result.usage.inputTokens + result.usage.outputTokens} tokens)\n`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.error(`\n Error: ${err instanceof Error ? err.message : err}\n`);
|
|
68
|
+
}
|
|
69
|
+
ask();
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
ask();
|
|
73
|
+
}
|