@alejandroroman/agent-kit 0.1.4 → 0.2.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/dist/agent/loop.js +213 -111
- package/dist/agent/types.d.ts +2 -0
- 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 +3 -1
- package/dist/bootstrap.js +26 -7
- package/dist/cli/chat.js +3 -1
- 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/env.d.ts +14 -0
- package/dist/cli/env.js +68 -0
- package/dist/cli/init.js +10 -0
- package/dist/cli/setup-agent/index.js +61 -18
- package/dist/cli/slack-setup.d.ts +6 -0
- package/dist/cli/slack-setup.js +234 -0
- package/dist/cli/start.js +65 -16
- package/dist/cli/ui.d.ts +2 -0
- package/dist/cli/ui.js +4 -1
- 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/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.d.ts +1 -0
- package/dist/logger.js +11 -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/package.json +10 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const ATTR: {
|
|
2
|
+
readonly AGENT: "agent.name";
|
|
3
|
+
readonly SOURCE: "agent.source";
|
|
4
|
+
readonly RUN_ID: "agent.run_id";
|
|
5
|
+
readonly MODEL: "llm.model";
|
|
6
|
+
readonly PROVIDER: "llm.provider";
|
|
7
|
+
readonly INPUT_TOKENS: "llm.tokens.input";
|
|
8
|
+
readonly OUTPUT_TOKENS: "llm.tokens.output";
|
|
9
|
+
readonly CACHE_READ_TOKENS: "llm.tokens.cache_read";
|
|
10
|
+
readonly CACHE_CREATION_TOKENS: "llm.tokens.cache_creation";
|
|
11
|
+
readonly LATENCY_MS: "llm.latency_ms";
|
|
12
|
+
readonly STOP_REASON: "llm.stop_reason";
|
|
13
|
+
readonly TOOL_NAME: "tool.name";
|
|
14
|
+
readonly TOOL_STATUS: "tool.status";
|
|
15
|
+
readonly TOOL_DURATION_MS: "tool.duration_ms";
|
|
16
|
+
readonly SKILL_NAME: "skill.name";
|
|
17
|
+
readonly SKILL_TOOLS_LOADED: "skill.tools_loaded";
|
|
18
|
+
readonly ITERATION: "agent.iteration";
|
|
19
|
+
readonly JOB_ID: "cron.job_id";
|
|
20
|
+
readonly SCHEDULE: "cron.schedule";
|
|
21
|
+
readonly CHANNEL: "slack.channel";
|
|
22
|
+
readonly THREAD_TS: "slack.thread_ts";
|
|
23
|
+
readonly TOKENS_BEFORE: "compaction.tokens_before";
|
|
24
|
+
readonly TOKENS_AFTER: "compaction.tokens_after";
|
|
25
|
+
readonly FALLBACK_ORIGINAL: "llm.fallback.original_model";
|
|
26
|
+
readonly FALLBACK_MODEL: "llm.fallback.model";
|
|
27
|
+
readonly FALLBACK_ERROR: "llm.fallback.error";
|
|
28
|
+
readonly SUPPRESSED: "agent.suppressed";
|
|
29
|
+
readonly SPAWN_CHILD: "agent.spawn.child";
|
|
30
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Span attribute keys — centralized to avoid typos
|
|
2
|
+
export const ATTR = {
|
|
3
|
+
AGENT: "agent.name",
|
|
4
|
+
SOURCE: "agent.source",
|
|
5
|
+
RUN_ID: "agent.run_id",
|
|
6
|
+
MODEL: "llm.model",
|
|
7
|
+
PROVIDER: "llm.provider",
|
|
8
|
+
INPUT_TOKENS: "llm.tokens.input",
|
|
9
|
+
OUTPUT_TOKENS: "llm.tokens.output",
|
|
10
|
+
CACHE_READ_TOKENS: "llm.tokens.cache_read",
|
|
11
|
+
CACHE_CREATION_TOKENS: "llm.tokens.cache_creation",
|
|
12
|
+
LATENCY_MS: "llm.latency_ms",
|
|
13
|
+
STOP_REASON: "llm.stop_reason",
|
|
14
|
+
TOOL_NAME: "tool.name",
|
|
15
|
+
TOOL_STATUS: "tool.status",
|
|
16
|
+
TOOL_DURATION_MS: "tool.duration_ms",
|
|
17
|
+
SKILL_NAME: "skill.name",
|
|
18
|
+
SKILL_TOOLS_LOADED: "skill.tools_loaded",
|
|
19
|
+
ITERATION: "agent.iteration",
|
|
20
|
+
JOB_ID: "cron.job_id",
|
|
21
|
+
SCHEDULE: "cron.schedule",
|
|
22
|
+
CHANNEL: "slack.channel",
|
|
23
|
+
THREAD_TS: "slack.thread_ts",
|
|
24
|
+
TOKENS_BEFORE: "compaction.tokens_before",
|
|
25
|
+
TOKENS_AFTER: "compaction.tokens_after",
|
|
26
|
+
FALLBACK_ORIGINAL: "llm.fallback.original_model",
|
|
27
|
+
FALLBACK_MODEL: "llm.fallback.model",
|
|
28
|
+
FALLBACK_ERROR: "llm.fallback.error",
|
|
29
|
+
SUPPRESSED: "agent.suppressed",
|
|
30
|
+
SPAWN_CHILD: "agent.spawn.child",
|
|
31
|
+
};
|
|
@@ -2,7 +2,9 @@ import { ToolRegistry } from "../registry.js";
|
|
|
2
2
|
import { readFileTool } from "./read-file.js";
|
|
3
3
|
import { writeFileTool } from "./write-file.js";
|
|
4
4
|
import { runCommandTool } from "./run-command.js";
|
|
5
|
+
import { createUpdateAgentConfigTool, createManageCronTool } from "./self-config.js";
|
|
5
6
|
export { readFileTool, writeFileTool, runCommandTool };
|
|
7
|
+
export { createUpdateAgentConfigTool, createManageCronTool };
|
|
6
8
|
interface BuiltinRegistryOptions {
|
|
7
9
|
allowedCommands?: string[];
|
|
8
10
|
allowedPaths?: string[];
|
|
@@ -4,7 +4,9 @@ import { createWriteFileTool, writeFileTool } from "./write-file.js";
|
|
|
4
4
|
import { createRunCommandTool, runCommandTool } from "./run-command.js";
|
|
5
5
|
import { createMemoryTools } from "./memory.js";
|
|
6
6
|
import { createWebSearchTool } from "./web-search.js";
|
|
7
|
+
import { createUpdateAgentConfigTool, createManageCronTool } from "./self-config.js";
|
|
7
8
|
export { readFileTool, writeFileTool, runCommandTool };
|
|
9
|
+
export { createUpdateAgentConfigTool, createManageCronTool };
|
|
8
10
|
export function createBuiltinRegistry(options = {}) {
|
|
9
11
|
const registry = new ToolRegistry();
|
|
10
12
|
registry.register(createReadFileTool(options.allowedPaths));
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Tool } from "../types.js";
|
|
2
|
+
import type { ConfigWriter } from "../../config/writer.js";
|
|
3
|
+
export declare function createUpdateAgentConfigTool(agentName: string, writer: ConfigWriter): Tool;
|
|
4
|
+
export declare function createManageCronTool(agentName: string, writer: ConfigWriter): Tool;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import cron from "node-cron";
|
|
2
|
+
import { createLogger } from "../../logger.js";
|
|
3
|
+
const log = createLogger("tools:self-config");
|
|
4
|
+
const ALLOWED_CONFIG_FIELDS = [
|
|
5
|
+
"displayName", "emoji", "avatar", "model",
|
|
6
|
+
"maxIterations", "maxTokens", "heartbeat",
|
|
7
|
+
];
|
|
8
|
+
function pickAllowed(input) {
|
|
9
|
+
const result = {};
|
|
10
|
+
for (const key of ALLOWED_CONFIG_FIELDS) {
|
|
11
|
+
if (key in input)
|
|
12
|
+
result[key] = input[key];
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
export function createUpdateAgentConfigTool(agentName, writer) {
|
|
17
|
+
return {
|
|
18
|
+
name: "update_agent_config",
|
|
19
|
+
description: `Update your own agent configuration. Allowed fields: ${ALLOWED_CONFIG_FIELDS.join(", ")}. Fields like tools, skills, sandbox, and slack cannot be changed.`,
|
|
20
|
+
parameters: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
displayName: { type: "string", description: "Your display name" },
|
|
24
|
+
emoji: { type: "string", description: "Your emoji icon" },
|
|
25
|
+
avatar: { type: "string", description: "URL to your avatar image" },
|
|
26
|
+
model: { type: "string", description: "Model alias to use (must be a configured alias)" },
|
|
27
|
+
maxIterations: { type: "number", description: "Max agent loop iterations" },
|
|
28
|
+
maxTokens: { type: "number", description: "Max tokens per response" },
|
|
29
|
+
heartbeat: {
|
|
30
|
+
type: "object",
|
|
31
|
+
description: "Heartbeat settings (partial update)",
|
|
32
|
+
properties: {
|
|
33
|
+
enabled: { type: "boolean" },
|
|
34
|
+
intervalMinutes: { type: "number" },
|
|
35
|
+
model: { type: "string" },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
execute: async (args) => {
|
|
41
|
+
try {
|
|
42
|
+
const updates = pickAllowed(args);
|
|
43
|
+
if (Object.keys(updates).length === 0) {
|
|
44
|
+
return "Error: No allowed fields provided. Allowed: " + ALLOWED_CONFIG_FIELDS.join(", ");
|
|
45
|
+
}
|
|
46
|
+
// Pre-validate model alias if provided
|
|
47
|
+
if (updates.model) {
|
|
48
|
+
const config = writer.read();
|
|
49
|
+
if (config.models) {
|
|
50
|
+
const aliases = config.models.map((m) => m.alias);
|
|
51
|
+
if (!aliases.includes(updates.model)) {
|
|
52
|
+
return `Error: Unknown model alias "${updates.model}". Available: ${aliases.join(", ")}`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
await writer.mutate((config) => {
|
|
57
|
+
const agent = config.agents[agentName];
|
|
58
|
+
if (!agent)
|
|
59
|
+
throw new Error(`Agent "${agentName}" not found in config`);
|
|
60
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
61
|
+
if (key === "heartbeat" && typeof value === "object" && value !== null) {
|
|
62
|
+
agent.heartbeat = { ...agent.heartbeat, ...value };
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
agent[key] = value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const fields = Object.keys(updates).join(", ");
|
|
70
|
+
log.info({ agent: agentName, fields }, "agent config updated");
|
|
71
|
+
return `Updated ${agentName} config: ${fields}`;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
return `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const MIN_CRON_INTERVAL_MINUTES = 5;
|
|
80
|
+
function isTooFrequent(schedule) {
|
|
81
|
+
const parts = schedule.trim().split(/\s+/);
|
|
82
|
+
if (parts.length < 5)
|
|
83
|
+
return false;
|
|
84
|
+
const minuteField = parts[0];
|
|
85
|
+
if (minuteField === "*")
|
|
86
|
+
return true;
|
|
87
|
+
const stepMatch = minuteField.match(/^\*\/(\d+)$/);
|
|
88
|
+
if (stepMatch && parseInt(stepMatch[1], 10) < MIN_CRON_INTERVAL_MINUTES)
|
|
89
|
+
return true;
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
export function createManageCronTool(agentName, writer) {
|
|
93
|
+
return {
|
|
94
|
+
name: "manage_cron",
|
|
95
|
+
description: "Manage your own cron jobs. Actions: list, create, update, delete. You can only manage jobs that belong to you.",
|
|
96
|
+
parameters: {
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {
|
|
99
|
+
action: { type: "string", enum: ["list", "create", "update", "delete"], description: "The operation to perform" },
|
|
100
|
+
id: { type: "string", description: "Cron job ID (required for create/update/delete)" },
|
|
101
|
+
schedule: { type: "string", description: "Cron schedule expression (create/update)" },
|
|
102
|
+
prompt: { type: "string", description: "The prompt to run (create/update)" },
|
|
103
|
+
enabled: { type: "boolean", description: "Whether the job is enabled (create/update)" },
|
|
104
|
+
},
|
|
105
|
+
required: ["action"],
|
|
106
|
+
},
|
|
107
|
+
execute: async (args) => {
|
|
108
|
+
try {
|
|
109
|
+
const action = args.action;
|
|
110
|
+
if (action === "list") {
|
|
111
|
+
const config = writer.read();
|
|
112
|
+
const jobs = (config.cron ?? []).filter((j) => j.agent === agentName);
|
|
113
|
+
return JSON.stringify(jobs, null, 2);
|
|
114
|
+
}
|
|
115
|
+
const id = args.id;
|
|
116
|
+
if (!id)
|
|
117
|
+
return "Error: id is required for create/update/delete";
|
|
118
|
+
if (action === "create") {
|
|
119
|
+
const schedule = args.schedule;
|
|
120
|
+
const prompt = args.prompt;
|
|
121
|
+
const enabled = args.enabled ?? true;
|
|
122
|
+
if (!schedule || !prompt)
|
|
123
|
+
return "Error: schedule and prompt are required for create";
|
|
124
|
+
if (!cron.validate(schedule))
|
|
125
|
+
return `Error: Invalid cron schedule "${schedule}"`;
|
|
126
|
+
if (isTooFrequent(schedule))
|
|
127
|
+
return `Error: Schedule runs too frequently. Minimum interval is ${MIN_CRON_INTERVAL_MINUTES} minutes.`;
|
|
128
|
+
await writer.mutate((config) => {
|
|
129
|
+
config.cron = config.cron ?? [];
|
|
130
|
+
if (config.cron.some((j) => j.id === id)) {
|
|
131
|
+
throw new Error(`Cron job "${id}" already exists`);
|
|
132
|
+
}
|
|
133
|
+
config.cron.push({ id, agent: agentName, schedule, prompt, enabled });
|
|
134
|
+
});
|
|
135
|
+
log.info({ agent: agentName, jobId: id }, "cron job created");
|
|
136
|
+
return `Created cron job "${id}"`;
|
|
137
|
+
}
|
|
138
|
+
if (action === "update") {
|
|
139
|
+
if (args.schedule) {
|
|
140
|
+
const schedule = args.schedule;
|
|
141
|
+
if (!cron.validate(schedule))
|
|
142
|
+
return `Error: Invalid cron schedule "${schedule}"`;
|
|
143
|
+
if (isTooFrequent(schedule))
|
|
144
|
+
return `Error: Schedule runs too frequently. Minimum interval is ${MIN_CRON_INTERVAL_MINUTES} minutes.`;
|
|
145
|
+
}
|
|
146
|
+
await writer.mutate((config) => {
|
|
147
|
+
const job = (config.cron ?? []).find((j) => j.id === id);
|
|
148
|
+
if (!job)
|
|
149
|
+
throw new Error(`Cron job "${id}" not found`);
|
|
150
|
+
if (job.agent !== agentName)
|
|
151
|
+
throw new Error(`Cron job "${id}" belongs to "${job.agent}", not "${agentName}"`);
|
|
152
|
+
if (args.schedule)
|
|
153
|
+
job.schedule = args.schedule;
|
|
154
|
+
if (args.prompt)
|
|
155
|
+
job.prompt = args.prompt;
|
|
156
|
+
if (args.enabled !== undefined)
|
|
157
|
+
job.enabled = args.enabled;
|
|
158
|
+
});
|
|
159
|
+
log.info({ agent: agentName, jobId: id }, "cron job updated");
|
|
160
|
+
return `Updated cron job "${id}"`;
|
|
161
|
+
}
|
|
162
|
+
if (action === "delete") {
|
|
163
|
+
await writer.mutate((config) => {
|
|
164
|
+
const idx = (config.cron ?? []).findIndex((j) => j.id === id);
|
|
165
|
+
if (idx === -1)
|
|
166
|
+
throw new Error(`Cron job "${id}" not found`);
|
|
167
|
+
if (config.cron[idx].agent !== agentName) {
|
|
168
|
+
throw new Error(`Cron job "${id}" belongs to "${config.cron[idx].agent}", not "${agentName}"`);
|
|
169
|
+
}
|
|
170
|
+
config.cron.splice(idx, 1);
|
|
171
|
+
});
|
|
172
|
+
log.info({ agent: agentName, jobId: id }, "cron job deleted");
|
|
173
|
+
return `Deleted cron job "${id}"`;
|
|
174
|
+
}
|
|
175
|
+
return `Error: Unknown action "${action}"`;
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
return `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alejandroroman/agent-kit",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -27,8 +27,14 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
30
|
-
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
31
30
|
"@clack/prompts": "^1.0.1",
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
32
|
+
"@opentelemetry/api": "^1.9.0",
|
|
33
|
+
"@opentelemetry/context-async-hooks": "^2.6.0",
|
|
34
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
|
|
35
|
+
"@opentelemetry/sdk-node": "^0.213.0",
|
|
36
|
+
"@opentelemetry/sdk-trace-base": "^2.6.0",
|
|
37
|
+
"@sentry/node": "^10.42.0",
|
|
32
38
|
"@slack/bolt": "^4.6.0",
|
|
33
39
|
"better-sqlite3": "^11.9.1",
|
|
34
40
|
"nanoid": "^5.1.5",
|
|
@@ -36,6 +42,7 @@
|
|
|
36
42
|
"openai": "^6.24.0",
|
|
37
43
|
"pino": "^10.3.1",
|
|
38
44
|
"pino-pretty": "^13.1.3",
|
|
45
|
+
"sharp": "0.33.5",
|
|
39
46
|
"sqlite-vec": "^0.1.6",
|
|
40
47
|
"twitter-api-v2": "^1.29.0",
|
|
41
48
|
"zod": "^4.3.6"
|
|
@@ -45,6 +52,7 @@
|
|
|
45
52
|
"@types/better-sqlite3": "^7.6.13",
|
|
46
53
|
"@types/node": "^25.3.0",
|
|
47
54
|
"@types/node-cron": "^3.0.11",
|
|
55
|
+
"@types/sharp": "^0.32.0",
|
|
48
56
|
"tsx": "^4.21.0",
|
|
49
57
|
"typescript": "^5.9.3",
|
|
50
58
|
"vitest": "^4.0.18"
|