@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
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import { isCancel } from "./ui.js";
|
|
5
|
+
import { upsertEnvKey, loadEnvFile } from "./env.js";
|
|
6
|
+
import { readRawConfig, writeConfig } from "./config-writer.js";
|
|
7
|
+
import { loadConfig } from "../config/index.js";
|
|
8
|
+
const SLACK_MANIFEST = `display_information:
|
|
9
|
+
name: Agent Kit
|
|
10
|
+
settings:
|
|
11
|
+
socket_mode_enabled: true
|
|
12
|
+
features:
|
|
13
|
+
bot_user:
|
|
14
|
+
display_name: Agent Kit
|
|
15
|
+
always_online: true
|
|
16
|
+
event_subscriptions:
|
|
17
|
+
bot_events:
|
|
18
|
+
- message.channels
|
|
19
|
+
- message.groups
|
|
20
|
+
oauth_config:
|
|
21
|
+
scopes:
|
|
22
|
+
bot:
|
|
23
|
+
- chat:write
|
|
24
|
+
- channels:history
|
|
25
|
+
- channels:read
|
|
26
|
+
- groups:history
|
|
27
|
+
- groups:read`;
|
|
28
|
+
function openBrowser(url) {
|
|
29
|
+
try {
|
|
30
|
+
const cmd = process.platform === "darwin" ? "open"
|
|
31
|
+
: process.platform === "win32" ? "start"
|
|
32
|
+
: "xdg-open";
|
|
33
|
+
execFileSync(cmd, [url], { stdio: "ignore" });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
p.log.warn(`Could not open browser. Visit: ${url}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function setupSlack(opts) {
|
|
40
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
41
|
+
// 1. Opt-in (only during init)
|
|
42
|
+
if (opts.fromInit) {
|
|
43
|
+
const wantSlack = await p.confirm({
|
|
44
|
+
message: "Would you like to connect Slack?",
|
|
45
|
+
});
|
|
46
|
+
if (isCancel(wantSlack) || !wantSlack)
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Check for existing tokens
|
|
50
|
+
const existingEnv = loadEnvFile(envPath);
|
|
51
|
+
const hasAppToken = !!existingEnv.SLACK_APP_TOKEN;
|
|
52
|
+
const hasBotToken = !!existingEnv.SLACK_BOT_TOKEN;
|
|
53
|
+
let skipTokens = false;
|
|
54
|
+
if (hasAppToken && hasBotToken) {
|
|
55
|
+
const keep = await p.confirm({
|
|
56
|
+
message: "Slack tokens already exist in .env. Keep them?",
|
|
57
|
+
});
|
|
58
|
+
if (isCancel(keep))
|
|
59
|
+
return;
|
|
60
|
+
if (keep) {
|
|
61
|
+
skipTokens = true;
|
|
62
|
+
process.env.SLACK_APP_TOKEN ??= existingEnv.SLACK_APP_TOKEN;
|
|
63
|
+
process.env.SLACK_BOT_TOKEN ??= existingEnv.SLACK_BOT_TOKEN;
|
|
64
|
+
p.log.info("Using existing tokens from .env");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 2. Instructions — Create App
|
|
68
|
+
if (!skipTokens) {
|
|
69
|
+
p.log.step("Step 1: Create Slack App");
|
|
70
|
+
console.log();
|
|
71
|
+
console.log(" Go to api.slack.com/apps → Create New App → From a manifest");
|
|
72
|
+
console.log();
|
|
73
|
+
const openPage = await p.confirm({
|
|
74
|
+
message: "Open api.slack.com in your browser?",
|
|
75
|
+
});
|
|
76
|
+
if (!isCancel(openPage) && openPage) {
|
|
77
|
+
openBrowser("https://api.slack.com/apps");
|
|
78
|
+
}
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(" Select your workspace, then paste this manifest:");
|
|
81
|
+
console.log();
|
|
82
|
+
for (const line of SLACK_MANIFEST.split("\n")) {
|
|
83
|
+
console.log(` ${line}`);
|
|
84
|
+
}
|
|
85
|
+
console.log();
|
|
86
|
+
console.log(" Click Create.");
|
|
87
|
+
console.log();
|
|
88
|
+
// 3. Instructions — App Token
|
|
89
|
+
p.log.step("Step 2: Generate App-Level Token");
|
|
90
|
+
console.log();
|
|
91
|
+
console.log(" Settings → Basic Information → App-Level Tokens");
|
|
92
|
+
console.log(" Click 'Generate Token and Scopes'");
|
|
93
|
+
console.log(" Name: socket-mode | Scope: connections:write");
|
|
94
|
+
console.log(" Copy the xapp-... token");
|
|
95
|
+
console.log();
|
|
96
|
+
// 4. Collect App Token
|
|
97
|
+
const appToken = await p.password({
|
|
98
|
+
message: "Paste App Token (xapp-...):",
|
|
99
|
+
validate: (val) => {
|
|
100
|
+
if (!val?.trim())
|
|
101
|
+
return "App token is required";
|
|
102
|
+
if (!val.startsWith("xapp-"))
|
|
103
|
+
return "Token should start with xapp-";
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
if (isCancel(appToken))
|
|
107
|
+
return;
|
|
108
|
+
upsertEnvKey(envPath, "SLACK_APP_TOKEN", appToken.trim());
|
|
109
|
+
process.env.SLACK_APP_TOKEN = appToken.trim();
|
|
110
|
+
p.log.success("App token saved to .env");
|
|
111
|
+
// 5. Instructions — Bot Token
|
|
112
|
+
p.log.step("Step 3: Install to Workspace");
|
|
113
|
+
console.log();
|
|
114
|
+
console.log(" Settings → Install App → Install to Workspace");
|
|
115
|
+
console.log(" Authorize, then copy the Bot User OAuth Token (xoxb-...)");
|
|
116
|
+
console.log();
|
|
117
|
+
// 6. Collect Bot Token
|
|
118
|
+
const botToken = await p.password({
|
|
119
|
+
message: "Paste Bot Token (xoxb-...):",
|
|
120
|
+
validate: (val) => {
|
|
121
|
+
if (!val?.trim())
|
|
122
|
+
return "Bot token is required";
|
|
123
|
+
if (!val.startsWith("xoxb-"))
|
|
124
|
+
return "Token should start with xoxb-";
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
if (isCancel(botToken))
|
|
128
|
+
return;
|
|
129
|
+
upsertEnvKey(envPath, "SLACK_BOT_TOKEN", botToken.trim());
|
|
130
|
+
process.env.SLACK_BOT_TOKEN = botToken.trim();
|
|
131
|
+
p.log.success("Bot token saved to .env");
|
|
132
|
+
} // end if (!skipTokens)
|
|
133
|
+
// 7. Channel Binding
|
|
134
|
+
let config;
|
|
135
|
+
try {
|
|
136
|
+
config = loadConfig(opts.configPath);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
p.log.error(`Could not load config: ${err instanceof Error ? err.message : err}`);
|
|
140
|
+
p.log.info("Slack tokens were saved to .env. Run `agent-kit slack-setup` again after fixing your config.");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const agentNames = Object.keys(config.agents);
|
|
144
|
+
if (agentNames.length === 0) {
|
|
145
|
+
p.log.info("No agents configured yet. Run `agent-kit create` first, then `agent-kit slack-setup` to bind channels.");
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
p.log.step("Step 4: Connect agents to Slack channels");
|
|
149
|
+
console.log();
|
|
150
|
+
console.log(" Create channels in Slack, then invite the bot: /invite @Agent Kit");
|
|
151
|
+
console.log(" Get channel IDs: right-click channel → View channel details → copy ID (starts with C)");
|
|
152
|
+
console.log();
|
|
153
|
+
const raw = readRawConfig(opts.configPath);
|
|
154
|
+
if (!raw) {
|
|
155
|
+
p.log.error("Could not read agent-kit.json");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const agents = raw.agents;
|
|
159
|
+
if (!agents) {
|
|
160
|
+
p.log.error("No agents section in config");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const usedChannels = new Map(); // channelId → agentName
|
|
164
|
+
// Collect already-bound channels
|
|
165
|
+
for (const [name, def] of Object.entries(agents)) {
|
|
166
|
+
const slack = def.slack;
|
|
167
|
+
if (slack?.channelId) {
|
|
168
|
+
usedChannels.set(slack.channelId, name);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (const name of agentNames) {
|
|
172
|
+
const agentDef = config.agents[name];
|
|
173
|
+
const emoji = agentDef.emoji ?? " ";
|
|
174
|
+
const display = agentDef.displayName ?? name;
|
|
175
|
+
// Skip if already bound
|
|
176
|
+
if (agentDef.slack) {
|
|
177
|
+
p.log.info(`${emoji} ${display} — already bound to ${agentDef.slack.channelName}`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const bind = await p.confirm({
|
|
181
|
+
message: `Connect ${emoji} ${display} to a Slack channel?`,
|
|
182
|
+
});
|
|
183
|
+
if (isCancel(bind))
|
|
184
|
+
return;
|
|
185
|
+
if (!bind)
|
|
186
|
+
continue;
|
|
187
|
+
const channelId = await p.text({
|
|
188
|
+
message: "Channel ID (e.g. C0123456789):",
|
|
189
|
+
validate: (val) => {
|
|
190
|
+
if (!val?.trim())
|
|
191
|
+
return "Channel ID is required";
|
|
192
|
+
if (!/^[CG][A-Z0-9]{8,}$/.test(val.trim())) {
|
|
193
|
+
return "Channel ID should start with C or G followed by uppercase letters/numbers";
|
|
194
|
+
}
|
|
195
|
+
const existing = usedChannels.get(val.trim());
|
|
196
|
+
if (existing) {
|
|
197
|
+
return `Channel already bound to agent "${existing}"`;
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
if (isCancel(channelId))
|
|
202
|
+
return;
|
|
203
|
+
const channelName = await p.text({
|
|
204
|
+
message: "Channel name (e.g. #finances):",
|
|
205
|
+
validate: (val) => {
|
|
206
|
+
if (!val?.trim())
|
|
207
|
+
return "Channel name is required";
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
if (isCancel(channelName))
|
|
211
|
+
return;
|
|
212
|
+
const id = channelId.trim();
|
|
213
|
+
const cName = channelName.trim();
|
|
214
|
+
// Update raw config
|
|
215
|
+
agents[name] = {
|
|
216
|
+
...agents[name],
|
|
217
|
+
slack: { channelId: id, channelName: cName },
|
|
218
|
+
};
|
|
219
|
+
usedChannels.set(id, name);
|
|
220
|
+
p.log.success(`${emoji} ${display} → ${cName}`);
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
writeConfig(opts.configPath, raw);
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
p.log.error(`Could not save config: ${err instanceof Error ? err.message : err}`);
|
|
227
|
+
p.log.info("Slack tokens were saved, but channel bindings were not persisted.");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// 8. Done
|
|
232
|
+
console.log();
|
|
233
|
+
p.log.success("Slack configured! Start with `pnpm start` to connect.");
|
|
234
|
+
}
|
package/dist/cli/start.js
CHANGED
|
@@ -1,24 +1,44 @@
|
|
|
1
|
-
import * as readline from "readline";
|
|
2
|
-
import * as path from "path";
|
|
3
1
|
import { loadConfig } from "../config/index.js";
|
|
4
|
-
import { runAgentLoop } from "../agent/loop.js";
|
|
5
|
-
import { compactMessages } from "../session/compaction.js";
|
|
6
2
|
import { CronScheduler } from "../cron/scheduler.js";
|
|
7
3
|
import { HeartbeatRunner } from "../heartbeat/index.js";
|
|
8
4
|
import { createSlackGateway } from "../gateways/slack/index.js";
|
|
9
5
|
import { createApiServer } from "../api/router.js";
|
|
10
6
|
import { createLogger } from "../logger.js";
|
|
11
|
-
import { buildAgentRuntime,
|
|
7
|
+
import { buildAgentRuntime, initUsageStore, createAgentExecutor, } from "../bootstrap.js";
|
|
12
8
|
import { ensureOllama } from "./ollama.js";
|
|
13
9
|
import { resolveApiKey } from "./ui.js";
|
|
10
|
+
import { showWhatsNew } from "./whats-new.js";
|
|
11
|
+
import { ConfigWriter } from "../config/writer.js";
|
|
12
|
+
import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
|
|
13
|
+
import { startRepl } from "./repl.js";
|
|
14
|
+
import * as path from "path";
|
|
15
|
+
import { loadEnvIntoProcess } from "./env.js";
|
|
16
|
+
import { initTelemetry, getTelemetryDb } from "../telemetry/index.js";
|
|
14
17
|
const log = createLogger("start");
|
|
15
|
-
const CONFIG_PATH = path.join(process.cwd(), "agent-kit.json");
|
|
16
|
-
const DATA_DIR = path.join(process.cwd(), "data");
|
|
17
|
-
const SKILLS_DIR = path.join(process.cwd(), "skills");
|
|
18
18
|
const DEFAULT_AGENT = "default";
|
|
19
19
|
export async function start() {
|
|
20
20
|
await resolveApiKey({ save: false });
|
|
21
|
+
showWhatsNew();
|
|
22
|
+
// Telemetry (OpenTelemetry + Sentry)
|
|
23
|
+
const shutdownTelemetry = initTelemetry({
|
|
24
|
+
dbPath: path.join(DATA_DIR, "telemetry.db"),
|
|
25
|
+
});
|
|
26
|
+
// Prune telemetry data older than 30 days
|
|
27
|
+
const telDb = getTelemetryDb();
|
|
28
|
+
if (telDb) {
|
|
29
|
+
try {
|
|
30
|
+
telDb.cleanup(30);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
log.warn({ err }, "telemetry cleanup failed");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Load Slack tokens from .env (if not already in process.env)
|
|
37
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
38
|
+
loadEnvIntoProcess(envPath, ["SLACK_BOT_TOKEN", "SLACK_APP_TOKEN"]);
|
|
21
39
|
const config = loadConfig(CONFIG_PATH);
|
|
40
|
+
const configWriter = new ConfigWriter(CONFIG_PATH);
|
|
41
|
+
configWriter.watch();
|
|
22
42
|
// Ollama check (warn only)
|
|
23
43
|
if (config.defaults.memory) {
|
|
24
44
|
const ollama = await ensureOllama();
|
|
@@ -32,6 +52,7 @@ export async function start() {
|
|
|
32
52
|
configPath: CONFIG_PATH,
|
|
33
53
|
usageStore,
|
|
34
54
|
dataDir: DATA_DIR,
|
|
55
|
+
telemetryDb: getTelemetryDb(),
|
|
35
56
|
}, apiPort);
|
|
36
57
|
// Build primary agent runtime (for REPL)
|
|
37
58
|
const agentNames = Object.keys(config.agents);
|
|
@@ -41,17 +62,18 @@ export async function start() {
|
|
|
41
62
|
console.error(`Agent "${agentName}" not found. Available: ${names}`);
|
|
42
63
|
process.exit(1);
|
|
43
64
|
}
|
|
44
|
-
const runtime = buildAgentRuntime(agentName, config, {
|
|
65
|
+
const runtime = await buildAgentRuntime(agentName, config, {
|
|
45
66
|
dataDir: DATA_DIR,
|
|
46
67
|
skillsDir: SKILLS_DIR,
|
|
47
68
|
usageStore,
|
|
69
|
+
configPath: CONFIG_PATH,
|
|
48
70
|
});
|
|
49
71
|
const warnings = runtime.toolRegistry.validateAgents(config.agents);
|
|
50
72
|
for (const w of warnings)
|
|
51
73
|
log.warn(w);
|
|
52
74
|
const { resolved, toolRegistry, agentRegistry, promptFragments, skillsIndex, soul, session } = runtime;
|
|
53
75
|
// Cron scheduler
|
|
54
|
-
const scheduler = new CronScheduler(config, toolRegistry, agentRegistry, DATA_DIR, SKILLS_DIR, usageStore);
|
|
76
|
+
const scheduler = new CronScheduler(config, toolRegistry, agentRegistry, DATA_DIR, SKILLS_DIR, usageStore, configWriter);
|
|
55
77
|
const enabledJobs = scheduler.getJobs().filter((j) => j.enabled);
|
|
56
78
|
// Agent executor for Slack
|
|
57
79
|
const executeAgent = createAgentExecutor(config, {
|
|
@@ -60,10 +82,14 @@ export async function start() {
|
|
|
60
82
|
agentRegistry,
|
|
61
83
|
usageStore,
|
|
62
84
|
source: "slack",
|
|
85
|
+
configPath: CONFIG_PATH,
|
|
63
86
|
});
|
|
64
87
|
// Slack gateway
|
|
65
88
|
let gateway;
|
|
66
89
|
const hasSlackBindings = Object.values(config.agents).some((a) => a.slack);
|
|
90
|
+
if (hasSlackBindings && (!process.env.SLACK_BOT_TOKEN || !process.env.SLACK_APP_TOKEN)) {
|
|
91
|
+
log.warn("Slack bindings configured but SLACK_BOT_TOKEN/SLACK_APP_TOKEN not found — run `agent-kit slack-setup`");
|
|
92
|
+
}
|
|
67
93
|
if (hasSlackBindings && process.env.SLACK_BOT_TOKEN && process.env.SLACK_APP_TOKEN) {
|
|
68
94
|
try {
|
|
69
95
|
gateway = createSlackGateway(config, { onAgentRequest: executeAgent });
|
|
@@ -84,22 +110,35 @@ export async function start() {
|
|
|
84
110
|
log.warn({ err }, "failed to start API server");
|
|
85
111
|
}
|
|
86
112
|
// Cron
|
|
113
|
+
const cronCallbacks = {
|
|
114
|
+
onResult: (jobId, agentName, result) => {
|
|
115
|
+
log.info({ jobId, tokens: result.usage.inputTokens + result.usage.outputTokens }, "cron job completed");
|
|
116
|
+
process.stdout.write("You: ");
|
|
117
|
+
const channelOverride = config.cron.find((j) => j.id === jobId)?.slack?.channelId;
|
|
118
|
+
gateway?.onJobResult(agentName, jobId, result, channelOverride).catch((err) => log.warn({ err, jobId }, "failed to post cron result to Slack"));
|
|
119
|
+
},
|
|
120
|
+
onError: (jobId, agentName, error) => {
|
|
121
|
+
log.error({ err: error, jobId }, "cron job failed");
|
|
122
|
+
process.stdout.write("You: ");
|
|
123
|
+
const channelOverride = config.cron.find((j) => j.id === jobId)?.slack?.channelId;
|
|
124
|
+
gateway?.onJobError(agentName, jobId, error, channelOverride).catch((err) => log.warn({ err, jobId }, "failed to post cron error to Slack"));
|
|
125
|
+
},
|
|
126
|
+
};
|
|
87
127
|
if (enabledJobs.length > 0) {
|
|
88
|
-
scheduler.start(
|
|
89
|
-
onResult: (jobId, agentName, result) => {
|
|
90
|
-
log.info({ jobId, tokens: result.usage.inputTokens + result.usage.outputTokens }, "cron job completed");
|
|
91
|
-
process.stdout.write("You: ");
|
|
92
|
-
const channelOverride = config.cron.find((j) => j.id === jobId)?.slack?.channelId;
|
|
93
|
-
gateway?.onJobResult(agentName, jobId, result, channelOverride).catch((err) => log.warn({ err, jobId }, "failed to post cron result to Slack"));
|
|
94
|
-
},
|
|
95
|
-
onError: (jobId, agentName, error) => {
|
|
96
|
-
log.error({ err: error, jobId }, "cron job failed");
|
|
97
|
-
process.stdout.write("You: ");
|
|
98
|
-
const channelOverride = config.cron.find((j) => j.id === jobId)?.slack?.channelId;
|
|
99
|
-
gateway?.onJobError(agentName, jobId, error, channelOverride).catch((err) => log.warn({ err, jobId }, "failed to post cron error to Slack"));
|
|
100
|
-
},
|
|
101
|
-
});
|
|
128
|
+
scheduler.start(cronCallbacks);
|
|
102
129
|
}
|
|
130
|
+
// Hot-reload: restart cron when config changes
|
|
131
|
+
configWriter.on("config:changed", () => {
|
|
132
|
+
try {
|
|
133
|
+
const newConfig = loadConfig(CONFIG_PATH);
|
|
134
|
+
scheduler.reload(newConfig);
|
|
135
|
+
scheduler.start(cronCallbacks);
|
|
136
|
+
log.info("hot-reload: cron scheduler restarted");
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
log.error({ err }, "hot-reload: failed to reload config");
|
|
140
|
+
}
|
|
141
|
+
});
|
|
103
142
|
// Heartbeat
|
|
104
143
|
const heartbeat = new HeartbeatRunner(config, agentRegistry, DATA_DIR, SKILLS_DIR, usageStore);
|
|
105
144
|
const heartbeatAgents = heartbeat.getHeartbeatAgents();
|
|
@@ -152,87 +191,48 @@ export async function start() {
|
|
|
152
191
|
console.log(` Commands: /new (reset), /agents, /help, /quit`);
|
|
153
192
|
console.log();
|
|
154
193
|
// REPL
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
};
|
|
171
|
-
const ask = () => {
|
|
172
|
-
rl.question("You: ", async (input) => {
|
|
173
|
-
const trimmed = input.trim();
|
|
174
|
-
if (trimmed === "/quit") {
|
|
175
|
-
await shutdown();
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (trimmed === "/new") {
|
|
179
|
-
messages = [];
|
|
180
|
-
console.log(" Session reset.\n");
|
|
181
|
-
ask();
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
if (trimmed === "/agents") {
|
|
194
|
+
startRepl({
|
|
195
|
+
agentName,
|
|
196
|
+
model: resolved.model,
|
|
197
|
+
fallbacks: resolved.fallbacks,
|
|
198
|
+
maxIterations: resolved.maxIterations,
|
|
199
|
+
compactionThreshold: resolved.compactionThreshold,
|
|
200
|
+
maxToolResultSize: resolved.maxToolResultSize,
|
|
201
|
+
toolRegistry,
|
|
202
|
+
session,
|
|
203
|
+
soul,
|
|
204
|
+
skillsIndex,
|
|
205
|
+
promptFragments,
|
|
206
|
+
usageStore,
|
|
207
|
+
commands: {
|
|
208
|
+
"/agents": () => {
|
|
185
209
|
for (const [name, def] of agentList) {
|
|
186
210
|
const emoji = def.emoji ?? " ";
|
|
187
211
|
const display = def.displayName ?? name;
|
|
188
212
|
console.log(` ${emoji} ${display} (${name})`);
|
|
189
213
|
}
|
|
190
214
|
console.log();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
if (trimmed === "/help") {
|
|
215
|
+
},
|
|
216
|
+
"/help": () => {
|
|
195
217
|
console.log(" /new — Reset conversation");
|
|
196
218
|
console.log(" /agents — List agents");
|
|
197
219
|
console.log(" /quit — Shut down\n");
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
messages.push(userMsg);
|
|
207
|
-
session.append(userMsg);
|
|
208
|
-
messages = await compactMessages(messages, resolved.model, resolved.compactionThreshold);
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
onQuit: async () => {
|
|
223
|
+
configWriter.stopWatching();
|
|
224
|
+
scheduler.stop();
|
|
225
|
+
heartbeat.stop();
|
|
226
|
+
await gateway?.stop();
|
|
227
|
+
await apiServer.stop();
|
|
209
228
|
try {
|
|
210
|
-
|
|
211
|
-
const result = await runAgentLoop(messages, {
|
|
212
|
-
model: resolved.model,
|
|
213
|
-
fallbacks: resolved.fallbacks,
|
|
214
|
-
systemPrompt,
|
|
215
|
-
toolRegistry,
|
|
216
|
-
maxIterations: resolved.maxIterations,
|
|
217
|
-
compactionThreshold: resolved.compactionThreshold,
|
|
218
|
-
maxToolResultSize: resolved.maxToolResultSize,
|
|
219
|
-
agentName,
|
|
220
|
-
usageStore,
|
|
221
|
-
source: "cli",
|
|
222
|
-
});
|
|
223
|
-
const newMessages = result.messages.slice(messages.length - 1);
|
|
224
|
-
for (const msg of newMessages) {
|
|
225
|
-
session.append(msg);
|
|
226
|
-
}
|
|
227
|
-
messages = result.messages;
|
|
228
|
-
console.log(`\nAgent: ${result.text}`);
|
|
229
|
-
console.log(` (${result.usage.inputTokens + result.usage.outputTokens} tokens)\n`);
|
|
229
|
+
usageStore?.close();
|
|
230
230
|
}
|
|
231
|
-
catch
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
catch { }
|
|
232
|
+
await Promise.race([
|
|
233
|
+
shutdownTelemetry(),
|
|
234
|
+
new Promise((r) => setTimeout(r, 5000)),
|
|
235
|
+
]);
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
238
|
}
|
package/dist/cli/ui.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
declare const VERSION: any;
|
|
2
|
+
export { VERSION };
|
|
1
3
|
export declare function banner(): void;
|
|
2
|
-
export declare function status(label: string, value: string): void;
|
|
3
4
|
export declare function done(message: string): void;
|
|
4
|
-
export declare function onCancel(): void;
|
|
5
5
|
/**
|
|
6
6
|
* Check if a @clack/prompts value was cancelled. Exits the process if so.
|
|
7
7
|
* The `return` after `isCancel()` in callers is technically unreachable
|
package/dist/cli/ui.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
1
2
|
import * as fs from "fs";
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import * as p from "@clack/prompts";
|
|
4
|
-
const
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const { version: VERSION } = require("../../package.json");
|
|
7
|
+
export { VERSION };
|
|
5
8
|
export function banner() {
|
|
6
9
|
console.log();
|
|
7
10
|
p.intro(`agent-kit v${VERSION}`);
|
|
8
11
|
}
|
|
9
|
-
export function status(label, value) {
|
|
10
|
-
p.log.info(`${label}: ${value}`);
|
|
11
|
-
}
|
|
12
12
|
export function done(message) {
|
|
13
13
|
p.outro(message);
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
function onCancel() {
|
|
16
16
|
p.cancel("Cancelled.");
|
|
17
17
|
process.exit(0);
|
|
18
18
|
}
|
package/dist/cli/validate.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import * as path from "path";
|
|
2
1
|
import { loadConfig } from "../config/index.js";
|
|
3
2
|
import { validateAgent } from "../scripts/validate-agent.js";
|
|
3
|
+
import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
|
|
4
4
|
const PASS = "\u2713";
|
|
5
5
|
const FAIL = "\u2717";
|
|
6
|
-
const CONFIG_PATH = path.join(process.cwd(), "agent-kit.json");
|
|
7
|
-
const SKILLS_DIR = path.join(process.cwd(), "skills");
|
|
8
|
-
const DATA_DIR = path.join(process.cwd(), "data");
|
|
9
6
|
function printResult(result) {
|
|
10
7
|
for (const check of result.checks) {
|
|
11
8
|
const icon = check.passed ? PASS : FAIL;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showWhatsNew(): void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import { VERSION } from "./ui.js";
|
|
5
|
+
const RELEASE_NOTES = {
|
|
6
|
+
"0.1.6": [
|
|
7
|
+
"\"What's new\" notification after CLI updates",
|
|
8
|
+
"Auto-activate skills per agent",
|
|
9
|
+
"Hot-reload on external config file changes",
|
|
10
|
+
],
|
|
11
|
+
"0.1.5": [
|
|
12
|
+
"Agent self-configuration tools",
|
|
13
|
+
"Bookkeeper: bump maxIterations, filter uncategorized by year",
|
|
14
|
+
"Generate CLAUDE.md and fix pnpm native deps on init",
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
const VERSION_FILE = path.join(os.homedir(), ".agent-kit", ".last-version");
|
|
18
|
+
function getLastSeenVersion() {
|
|
19
|
+
try {
|
|
20
|
+
return fs.readFileSync(VERSION_FILE, "utf-8").trim() || null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function setLastSeenVersion(version) {
|
|
27
|
+
try {
|
|
28
|
+
fs.mkdirSync(path.dirname(VERSION_FILE), { recursive: true });
|
|
29
|
+
fs.writeFileSync(VERSION_FILE, version);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// never crash startup
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function showWhatsNew() {
|
|
36
|
+
try {
|
|
37
|
+
const last = getLastSeenVersion();
|
|
38
|
+
// First run — just record the version, don't spam
|
|
39
|
+
if (last === null) {
|
|
40
|
+
setLastSeenVersion(VERSION);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (last === VERSION)
|
|
44
|
+
return;
|
|
45
|
+
// Collect notes for versions newer than last-seen
|
|
46
|
+
const entries = [];
|
|
47
|
+
for (const [ver, notes] of Object.entries(RELEASE_NOTES)) {
|
|
48
|
+
if (ver > last)
|
|
49
|
+
entries.push([ver, notes]);
|
|
50
|
+
}
|
|
51
|
+
if (entries.length > 0) {
|
|
52
|
+
// Sort descending
|
|
53
|
+
entries.sort((a, b) => (b[0] > a[0] ? 1 : -1));
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(" \u2728 What's new in agent-kit");
|
|
56
|
+
for (const [ver, notes] of entries) {
|
|
57
|
+
console.log(`\n v${ver}`);
|
|
58
|
+
for (const note of notes) {
|
|
59
|
+
console.log(` • ${note}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
setLastSeenVersion(VERSION);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// never crash startup
|
|
68
|
+
}
|
|
69
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -29,6 +29,19 @@ async function main() {
|
|
|
29
29
|
await chat(args[0]);
|
|
30
30
|
break;
|
|
31
31
|
}
|
|
32
|
+
case "slack-setup": {
|
|
33
|
+
const fs = await import("fs");
|
|
34
|
+
const { setupSlack } = await import("./cli/slack-setup.js");
|
|
35
|
+
const { banner } = await import("./cli/ui.js");
|
|
36
|
+
const { CONFIG_PATH } = await import("./cli/paths.js");
|
|
37
|
+
banner();
|
|
38
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
39
|
+
console.error("No agent-kit.json found. Run `agent-kit init` first.");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
await setupSlack({ configPath: CONFIG_PATH, fromInit: false });
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
32
45
|
case "validate": {
|
|
33
46
|
const { validate } = await import("./cli/validate.js");
|
|
34
47
|
validate(args[0]);
|
|
@@ -44,6 +57,7 @@ async function main() {
|
|
|
44
57
|
agent-kit create AI-powered agent creation
|
|
45
58
|
agent-kit list Show configured agents
|
|
46
59
|
agent-kit chat <agent> Lightweight single-agent chat
|
|
60
|
+
agent-kit slack-setup Interactive Slack setup
|
|
47
61
|
agent-kit validate [agent] Validate config
|
|
48
62
|
|
|
49
63
|
Options:
|
package/dist/config/resolve.d.ts
CHANGED
package/dist/config/resolve.js
CHANGED
|
@@ -34,6 +34,7 @@ export function resolveAgent(name, config, toolRegistry, skillsDir) {
|
|
|
34
34
|
tools: toolRegistry.resolve(agentDef.tools),
|
|
35
35
|
skills,
|
|
36
36
|
canSpawn: agentDef.can_spawn,
|
|
37
|
+
autoActivateSkills: agentDef.autoActivateSkills,
|
|
37
38
|
maxIterations: agentDef.maxIterations ?? config.defaults.maxIterations,
|
|
38
39
|
maxTokens: agentDef.maxTokens ?? config.defaults.maxTokens,
|
|
39
40
|
compactionThreshold: config.defaults.compactionThreshold,
|