titan-agent 2026.4.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.
Files changed (43) hide show
  1. package/.env.example +21 -0
  2. package/LICENSE +29 -0
  3. package/README.md +305 -0
  4. package/assets/titan-logo.png +0 -0
  5. package/dist/agent/agent.js +1458 -0
  6. package/dist/agent/agent.js.map +1 -0
  7. package/dist/agent/generator.js +1078 -0
  8. package/dist/agent/generator.js.map +1 -0
  9. package/dist/cli/index.js +5064 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/gateway/server.js +4462 -0
  12. package/dist/gateway/server.js.map +1 -0
  13. package/dist/skills/builtin/apply_patch.js +368 -0
  14. package/dist/skills/builtin/apply_patch.js.map +1 -0
  15. package/dist/skills/builtin/auto_generate.js +1129 -0
  16. package/dist/skills/builtin/auto_generate.js.map +1 -0
  17. package/dist/skills/builtin/browser.js +357 -0
  18. package/dist/skills/builtin/browser.js.map +1 -0
  19. package/dist/skills/builtin/cron.js +410 -0
  20. package/dist/skills/builtin/cron.js.map +1 -0
  21. package/dist/skills/builtin/filesystem.js +386 -0
  22. package/dist/skills/builtin/filesystem.js.map +1 -0
  23. package/dist/skills/builtin/memory_skill.js +430 -0
  24. package/dist/skills/builtin/memory_skill.js.map +1 -0
  25. package/dist/skills/builtin/process.js +421 -0
  26. package/dist/skills/builtin/process.js.map +1 -0
  27. package/dist/skills/builtin/sessions.js +1729 -0
  28. package/dist/skills/builtin/sessions.js.map +1 -0
  29. package/dist/skills/builtin/shell.js +327 -0
  30. package/dist/skills/builtin/shell.js.map +1 -0
  31. package/dist/skills/builtin/vision.js +491 -0
  32. package/dist/skills/builtin/vision.js.map +1 -0
  33. package/dist/skills/builtin/voice.js +468 -0
  34. package/dist/skills/builtin/voice.js.map +1 -0
  35. package/dist/skills/builtin/web_fetch.js +331 -0
  36. package/dist/skills/builtin/web_fetch.js.map +1 -0
  37. package/dist/skills/builtin/web_search.js +317 -0
  38. package/dist/skills/builtin/web_search.js.map +1 -0
  39. package/dist/skills/builtin/webhook.js +323 -0
  40. package/dist/skills/builtin/webhook.js.map +1 -0
  41. package/dist/skills/registry.js +3369 -0
  42. package/dist/skills/registry.js.map +1 -0
  43. package/package.json +118 -0
@@ -0,0 +1,1729 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/utils/constants.ts
13
+ import { homedir } from "os";
14
+ import { join } from "path";
15
+ var TITAN_VERSION, TITAN_NAME, TITAN_ASCII_LOGO, TITAN_HOME, TITAN_CONFIG_PATH, TITAN_DB_PATH, TITAN_WORKSPACE, TITAN_SKILLS_DIR, TITAN_LOGS_DIR, TITAN_MEMORY_DIR, AGENTS_MD, SOUL_MD, TOOLS_MD, DEFAULT_GATEWAY_HOST, DEFAULT_GATEWAY_PORT, DEFAULT_WEB_PORT, DEFAULT_MODEL, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE, MAX_CONTEXT_MESSAGES, SESSION_TIMEOUT_MS, DEFAULT_SANDBOX_MODE, ALLOWED_TOOLS_DEFAULT;
16
+ var init_constants = __esm({
17
+ "src/utils/constants.ts"() {
18
+ "use strict";
19
+ TITAN_VERSION = "2026.4.0";
20
+ TITAN_NAME = "TITAN";
21
+ TITAN_ASCII_LOGO = `
22
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
23
+ \u2551 \u2551
24
+ \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2551
25
+ \u2551 \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2551
26
+ \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2551
27
+ \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2551
28
+ \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2551
29
+ \u2551 \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u2551
30
+ \u2551 \u2551
31
+ \u2551 The Intelligent Task Automation Network \u2551
32
+ \u2551 v${TITAN_VERSION} \u2551
33
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`;
34
+ TITAN_HOME = join(homedir(), ".titan");
35
+ TITAN_CONFIG_PATH = join(TITAN_HOME, "titan.json");
36
+ TITAN_DB_PATH = join(TITAN_HOME, "titan.db");
37
+ TITAN_WORKSPACE = join(TITAN_HOME, "workspace");
38
+ TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, "skills");
39
+ TITAN_LOGS_DIR = join(TITAN_HOME, "logs");
40
+ TITAN_MEMORY_DIR = join(TITAN_HOME, "memory");
41
+ AGENTS_MD = join(TITAN_WORKSPACE, "AGENTS.md");
42
+ SOUL_MD = join(TITAN_WORKSPACE, "SOUL.md");
43
+ TOOLS_MD = join(TITAN_WORKSPACE, "TOOLS.md");
44
+ DEFAULT_GATEWAY_HOST = "127.0.0.1";
45
+ DEFAULT_GATEWAY_PORT = 18789;
46
+ DEFAULT_WEB_PORT = 18790;
47
+ DEFAULT_MODEL = "anthropic/claude-sonnet-4-20250514";
48
+ DEFAULT_MAX_TOKENS = 8192;
49
+ DEFAULT_TEMPERATURE = 0.7;
50
+ MAX_CONTEXT_MESSAGES = 50;
51
+ SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
52
+ DEFAULT_SANDBOX_MODE = "host";
53
+ ALLOWED_TOOLS_DEFAULT = [
54
+ "shell",
55
+ "read_file",
56
+ "write_file",
57
+ "edit_file",
58
+ "list_dir",
59
+ "web_search",
60
+ "browser",
61
+ "cron",
62
+ "webhook",
63
+ "email",
64
+ "memory"
65
+ ];
66
+ }
67
+ });
68
+
69
+ // src/utils/logger.ts
70
+ import chalk from "chalk";
71
+ function formatTimestamp() {
72
+ return chalk.gray((/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19));
73
+ }
74
+ function log(level, component, message, ...args) {
75
+ if (level < currentLevel) return;
76
+ const prefix = `${formatTimestamp()} ${LEVEL_LABELS[level]} ${chalk.blue(`[${component}]`)}`;
77
+ console.log(`${prefix} ${message}`, ...args);
78
+ }
79
+ var LEVEL_LABELS, currentLevel, logger, logger_default;
80
+ var init_logger = __esm({
81
+ "src/utils/logger.ts"() {
82
+ "use strict";
83
+ LEVEL_LABELS = {
84
+ [0 /* DEBUG */]: chalk.gray("DEBUG"),
85
+ [1 /* INFO */]: chalk.cyan("INFO "),
86
+ [2 /* WARN */]: chalk.yellow("WARN "),
87
+ [3 /* ERROR */]: chalk.red("ERROR"),
88
+ [4 /* SILENT */]: ""
89
+ };
90
+ currentLevel = 1 /* INFO */;
91
+ logger = {
92
+ debug: (component, msg, ...args) => log(0 /* DEBUG */, component, msg, ...args),
93
+ info: (component, msg, ...args) => log(1 /* INFO */, component, msg, ...args),
94
+ warn: (component, msg, ...args) => log(2 /* WARN */, component, msg, ...args),
95
+ error: (component, msg, ...args) => log(3 /* ERROR */, component, msg, ...args)
96
+ };
97
+ logger_default = logger;
98
+ }
99
+ });
100
+
101
+ // src/utils/helpers.ts
102
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
103
+ function ensureDir(dirPath) {
104
+ if (!existsSync(dirPath)) {
105
+ mkdirSync(dirPath, { recursive: true });
106
+ }
107
+ }
108
+ function readJsonFile(filePath) {
109
+ try {
110
+ if (!existsSync(filePath)) return null;
111
+ const content = readFileSync(filePath, "utf-8");
112
+ return JSON.parse(content);
113
+ } catch {
114
+ return null;
115
+ }
116
+ }
117
+ var init_helpers = __esm({
118
+ "src/utils/helpers.ts"() {
119
+ "use strict";
120
+ }
121
+ });
122
+
123
+ // src/config/schema.ts
124
+ import { z } from "zod";
125
+ var ProviderConfigSchema, ChannelConfigSchema, SecurityConfigSchema, GatewayConfigSchema, AgentConfigSchema, TitanConfigSchema;
126
+ var init_schema = __esm({
127
+ "src/config/schema.ts"() {
128
+ "use strict";
129
+ init_constants();
130
+ ProviderConfigSchema = z.object({
131
+ apiKey: z.string().optional(),
132
+ baseUrl: z.string().optional(),
133
+ model: z.string().optional(),
134
+ maxTokens: z.number().optional(),
135
+ temperature: z.number().min(0).max(2).optional()
136
+ });
137
+ ChannelConfigSchema = z.object({
138
+ enabled: z.boolean().default(false),
139
+ token: z.string().optional(),
140
+ apiKey: z.string().optional(),
141
+ allowFrom: z.array(z.string()).default([]),
142
+ dmPolicy: z.enum(["pairing", "open", "closed"]).default("pairing")
143
+ });
144
+ SecurityConfigSchema = z.object({
145
+ sandboxMode: z.enum(["host", "docker", "none"]).default(DEFAULT_SANDBOX_MODE),
146
+ allowedTools: z.array(z.string()).default(ALLOWED_TOOLS_DEFAULT),
147
+ deniedTools: z.array(z.string()).default([]),
148
+ maxConcurrentTasks: z.number().default(5),
149
+ commandTimeout: z.number().default(3e4),
150
+ fileSystemAllowlist: z.array(z.string()).default([]),
151
+ networkAllowlist: z.array(z.string()).default(["*"]),
152
+ shield: z.object({
153
+ enabled: z.boolean().default(true),
154
+ mode: z.enum(["standard", "strict"]).default("strict")
155
+ }).default({})
156
+ });
157
+ GatewayConfigSchema = z.object({
158
+ host: z.string().default(DEFAULT_GATEWAY_HOST),
159
+ port: z.number().default(DEFAULT_GATEWAY_PORT),
160
+ webPort: z.number().default(DEFAULT_WEB_PORT),
161
+ auth: z.object({
162
+ mode: z.enum(["none", "token", "password"]).default("token"),
163
+ token: z.string().optional(),
164
+ password: z.string().optional()
165
+ }).default({})
166
+ });
167
+ AgentConfigSchema = z.object({
168
+ model: z.string().default(DEFAULT_MODEL),
169
+ maxTokens: z.number().default(DEFAULT_MAX_TOKENS),
170
+ temperature: z.number().default(DEFAULT_TEMPERATURE),
171
+ systemPrompt: z.string().optional(),
172
+ workspace: z.string().optional(),
173
+ thinkingMode: z.enum(["off", "low", "medium", "high"]).default("medium")
174
+ });
175
+ TitanConfigSchema = z.object({
176
+ agent: AgentConfigSchema.default({}),
177
+ providers: z.object({
178
+ anthropic: ProviderConfigSchema.default({}),
179
+ openai: ProviderConfigSchema.default({}),
180
+ google: ProviderConfigSchema.default({}),
181
+ ollama: ProviderConfigSchema.default({})
182
+ }).default({}),
183
+ channels: z.object({
184
+ discord: ChannelConfigSchema.default({}),
185
+ telegram: ChannelConfigSchema.default({}),
186
+ slack: ChannelConfigSchema.default({}),
187
+ whatsapp: ChannelConfigSchema.default({}),
188
+ webchat: ChannelConfigSchema.default({}),
189
+ googlechat: ChannelConfigSchema.default({}),
190
+ matrix: ChannelConfigSchema.default({}),
191
+ signal: ChannelConfigSchema.default({}),
192
+ msteams: ChannelConfigSchema.default({}),
193
+ bluebubbles: ChannelConfigSchema.default({})
194
+ }).default({}),
195
+ gateway: GatewayConfigSchema.default({}),
196
+ security: SecurityConfigSchema.default({}),
197
+ memory: z.object({
198
+ enabled: z.boolean().default(true),
199
+ maxHistoryMessages: z.number().default(50),
200
+ vectorSearchEnabled: z.boolean().default(false)
201
+ }).default({}),
202
+ skills: z.object({
203
+ enabled: z.boolean().default(true),
204
+ autoDiscover: z.boolean().default(true),
205
+ marketplace: z.boolean().default(false)
206
+ }).default({}),
207
+ logging: z.object({
208
+ level: z.enum(["debug", "info", "warn", "error", "silent"]).default("info"),
209
+ file: z.boolean().default(true)
210
+ }).default({}),
211
+ autonomy: z.object({
212
+ /** autonomous = full auto, supervised = asks for dangerous ops, locked = asks for everything */
213
+ mode: z.enum(["autonomous", "supervised", "locked"]).default("supervised"),
214
+ /** Auto-approve moderate-risk tools in main session (cli/webchat) */
215
+ autoApproveMainSession: z.boolean().default(true),
216
+ /** Timeout for HITL approval requests (ms). Auto-deny after timeout. */
217
+ approvalTimeoutMs: z.number().default(6e4),
218
+ /** Notify user of auto-approved actions */
219
+ notifyOnAutoApprove: z.boolean().default(true)
220
+ }).default({})
221
+ });
222
+ }
223
+ });
224
+
225
+ // src/config/config.ts
226
+ import { existsSync as existsSync2 } from "fs";
227
+ function getDefaultConfig() {
228
+ return TitanConfigSchema.parse({});
229
+ }
230
+ function loadConfig() {
231
+ if (cachedConfig) return cachedConfig;
232
+ ensureDir(TITAN_HOME);
233
+ let rawConfig = {};
234
+ if (existsSync2(TITAN_CONFIG_PATH)) {
235
+ const loaded = readJsonFile(TITAN_CONFIG_PATH);
236
+ if (loaded) {
237
+ rawConfig = loaded;
238
+ logger_default.debug(COMPONENT, `Loaded config from ${TITAN_CONFIG_PATH}`);
239
+ } else {
240
+ logger_default.warn(COMPONENT, `Failed to parse config at ${TITAN_CONFIG_PATH}, using defaults`);
241
+ }
242
+ } else {
243
+ logger_default.info(COMPONENT, "No config file found, using defaults");
244
+ }
245
+ applyEnvOverrides(rawConfig);
246
+ const result = TitanConfigSchema.safeParse(rawConfig);
247
+ if (!result.success) {
248
+ logger_default.warn(COMPONENT, "Config validation issues, using defaults for invalid fields");
249
+ logger_default.debug(COMPONENT, result.error.message);
250
+ cachedConfig = getDefaultConfig();
251
+ } else {
252
+ cachedConfig = result.data;
253
+ }
254
+ return cachedConfig;
255
+ }
256
+ function applyEnvOverrides(config) {
257
+ const envMap = {
258
+ TITAN_MODEL: (val) => setNested(config, "agent.model", val),
259
+ TITAN_GATEWAY_PORT: (val) => setNested(config, "gateway.port", parseInt(val, 10)),
260
+ TITAN_GATEWAY_HOST: (val) => setNested(config, "gateway.host", val),
261
+ TITAN_LOG_LEVEL: (val) => setNested(config, "logging.level", val),
262
+ ANTHROPIC_API_KEY: (val) => setNested(config, "providers.anthropic.apiKey", val),
263
+ OPENAI_API_KEY: (val) => setNested(config, "providers.openai.apiKey", val),
264
+ GOOGLE_API_KEY: (val) => setNested(config, "providers.google.apiKey", val),
265
+ OLLAMA_BASE_URL: (val) => setNested(config, "providers.ollama.baseUrl", val),
266
+ DISCORD_TOKEN: (val) => setNested(config, "channels.discord.token", val),
267
+ TELEGRAM_TOKEN: (val) => setNested(config, "channels.telegram.token", val),
268
+ SLACK_TOKEN: (val) => setNested(config, "channels.slack.token", val)
269
+ };
270
+ for (const [envKey, setter] of Object.entries(envMap)) {
271
+ const val = process.env[envKey];
272
+ if (val) {
273
+ setter(val);
274
+ logger_default.debug(COMPONENT, `Applied env override: ${envKey}`);
275
+ }
276
+ }
277
+ }
278
+ function setNested(obj, path, value) {
279
+ const parts = path.split(".");
280
+ let current = obj;
281
+ for (let i = 0; i < parts.length - 1; i++) {
282
+ if (!current[parts[i]] || typeof current[parts[i]] !== "object") {
283
+ current[parts[i]] = {};
284
+ }
285
+ current = current[parts[i]];
286
+ }
287
+ current[parts[parts.length - 1]] = value;
288
+ }
289
+ var COMPONENT, cachedConfig;
290
+ var init_config = __esm({
291
+ "src/config/config.ts"() {
292
+ "use strict";
293
+ init_constants();
294
+ init_helpers();
295
+ init_schema();
296
+ init_logger();
297
+ COMPONENT = "Config";
298
+ cachedConfig = null;
299
+ }
300
+ });
301
+
302
+ // src/agent/toolRunner.ts
303
+ function registerTool(handler) {
304
+ toolRegistry.set(handler.name, handler);
305
+ logger_default.debug(COMPONENT2, `Registered tool: ${handler.name}`);
306
+ }
307
+ function getToolDefinitions() {
308
+ const config = loadConfig();
309
+ const allowed = new Set(config.security.allowedTools);
310
+ const denied = new Set(config.security.deniedTools);
311
+ return Array.from(toolRegistry.values()).filter((tool) => {
312
+ if (denied.has(tool.name)) return false;
313
+ if (allowed.size > 0 && !allowed.has(tool.name)) return false;
314
+ return true;
315
+ }).map((tool) => ({
316
+ type: "function",
317
+ function: {
318
+ name: tool.name,
319
+ description: tool.description,
320
+ parameters: tool.parameters
321
+ }
322
+ }));
323
+ }
324
+ async function executeTool(toolCall) {
325
+ const config = loadConfig();
326
+ const startTime = Date.now();
327
+ const handler = toolRegistry.get(toolCall.function.name);
328
+ if (!handler) {
329
+ return {
330
+ toolCallId: toolCall.id,
331
+ name: toolCall.function.name,
332
+ content: `Error: Unknown tool "${toolCall.function.name}"`,
333
+ success: false,
334
+ durationMs: Date.now() - startTime
335
+ };
336
+ }
337
+ if (config.security.deniedTools.includes(handler.name)) {
338
+ return {
339
+ toolCallId: toolCall.id,
340
+ name: handler.name,
341
+ content: `Error: Tool "${handler.name}" is denied by security policy`,
342
+ success: false,
343
+ durationMs: Date.now() - startTime
344
+ };
345
+ }
346
+ try {
347
+ let args = {};
348
+ try {
349
+ args = JSON.parse(toolCall.function.arguments);
350
+ } catch {
351
+ args = {};
352
+ }
353
+ logger_default.info(COMPONENT2, `Executing tool: ${handler.name}`);
354
+ const timeout = config.security.commandTimeout || 3e4;
355
+ const result = await Promise.race([
356
+ handler.execute(args),
357
+ new Promise(
358
+ (_, reject) => setTimeout(() => reject(new Error(`Tool "${handler.name}" timed out after ${timeout}ms`)), timeout)
359
+ )
360
+ ]);
361
+ const durationMs = Date.now() - startTime;
362
+ logger_default.info(COMPONENT2, `Tool ${handler.name} completed in ${durationMs}ms`);
363
+ return {
364
+ toolCallId: toolCall.id,
365
+ name: handler.name,
366
+ content: result,
367
+ success: true,
368
+ durationMs
369
+ };
370
+ } catch (error) {
371
+ const durationMs = Date.now() - startTime;
372
+ const errorMsg = error.message;
373
+ logger_default.error(COMPONENT2, `Tool ${handler.name} failed: ${errorMsg}`);
374
+ return {
375
+ toolCallId: toolCall.id,
376
+ name: handler.name,
377
+ content: `Error: ${errorMsg}`,
378
+ success: false,
379
+ durationMs
380
+ };
381
+ }
382
+ }
383
+ async function executeTools(toolCalls) {
384
+ const config = loadConfig();
385
+ const maxConcurrent = config.security.maxConcurrentTasks || 5;
386
+ const results = [];
387
+ for (let i = 0; i < toolCalls.length; i += maxConcurrent) {
388
+ const batch = toolCalls.slice(i, i + maxConcurrent);
389
+ const batchResults = await Promise.all(batch.map(executeTool));
390
+ results.push(...batchResults);
391
+ }
392
+ return results;
393
+ }
394
+ var COMPONENT2, toolRegistry;
395
+ var init_toolRunner = __esm({
396
+ "src/agent/toolRunner.ts"() {
397
+ "use strict";
398
+ init_logger();
399
+ init_config();
400
+ COMPONENT2 = "ToolRunner";
401
+ toolRegistry = /* @__PURE__ */ new Map();
402
+ }
403
+ });
404
+
405
+ // src/skills/registry.ts
406
+ function registerSkill(meta, handler) {
407
+ registeredSkills.set(meta.name, meta);
408
+ registerTool(handler);
409
+ logger_default.debug(COMPONENT3, `Registered skill: ${meta.name} (${meta.source})`);
410
+ }
411
+ var COMPONENT3, registeredSkills;
412
+ var init_registry = __esm({
413
+ "src/skills/registry.ts"() {
414
+ "use strict";
415
+ init_constants();
416
+ init_toolRunner();
417
+ init_helpers();
418
+ init_logger();
419
+ COMPONENT3 = "Skills";
420
+ registeredSkills = /* @__PURE__ */ new Map();
421
+ }
422
+ });
423
+
424
+ // src/security/encryption.ts
425
+ var encryption_exports = {};
426
+ __export(encryption_exports, {
427
+ decrypt: () => decrypt,
428
+ encrypt: () => encrypt,
429
+ generateKey: () => generateKey
430
+ });
431
+ import { randomBytes, createCipheriv, createDecipheriv } from "crypto";
432
+ function generateKey() {
433
+ return randomBytes(32);
434
+ }
435
+ function encrypt(text, key) {
436
+ try {
437
+ const keyBuffer = typeof key === "string" ? Buffer.from(key, "hex") : key;
438
+ if (keyBuffer.length !== 32) {
439
+ throw new Error("Encryption key must be exactly 32 bytes (256 bits).");
440
+ }
441
+ const iv = randomBytes(16);
442
+ const cipher = createCipheriv(ALGORITHM, keyBuffer, iv);
443
+ let encrypted = cipher.update(text, "utf8", "hex");
444
+ encrypted += cipher.final("hex");
445
+ const authTag = cipher.getAuthTag().toString("hex");
446
+ return {
447
+ iv: iv.toString("hex"),
448
+ authTag,
449
+ data: encrypted
450
+ };
451
+ } catch (e) {
452
+ logger_default.error(COMPONENT4, `Encryption failed: ${e.message}`);
453
+ throw new Error("Failed to encrypt session data.");
454
+ }
455
+ }
456
+ function decrypt(payload, key) {
457
+ try {
458
+ const keyBuffer = typeof key === "string" ? Buffer.from(key, "hex") : key;
459
+ if (keyBuffer.length !== 32) {
460
+ throw new Error("Decryption key must be exactly 32 bytes (256 bits).");
461
+ }
462
+ const decipher = createDecipheriv(ALGORITHM, keyBuffer, Buffer.from(payload.iv, "hex"));
463
+ decipher.setAuthTag(Buffer.from(payload.authTag, "hex"));
464
+ let decrypted = decipher.update(payload.data, "hex", "utf8");
465
+ decrypted += decipher.final("utf8");
466
+ return decrypted;
467
+ } catch (e) {
468
+ logger_default.error(COMPONENT4, `Decryption failed: ${e.message}`);
469
+ throw new Error("Failed to decrypt session data. Invalid key or corrupted payload.");
470
+ }
471
+ }
472
+ var COMPONENT4, ALGORITHM;
473
+ var init_encryption = __esm({
474
+ "src/security/encryption.ts"() {
475
+ "use strict";
476
+ init_logger();
477
+ COMPONENT4 = "Encryption";
478
+ ALGORITHM = "aes-256-gcm";
479
+ }
480
+ });
481
+
482
+ // src/memory/memory.ts
483
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
484
+ import { join as join2 } from "path";
485
+ function getDefaultStore() {
486
+ return {
487
+ conversations: [],
488
+ memories: [],
489
+ sessions: [],
490
+ usageStats: [],
491
+ cronJobs: [],
492
+ skillsInstalled: []
493
+ };
494
+ }
495
+ function loadStore() {
496
+ if (store) return store;
497
+ ensureDir(TITAN_HOME);
498
+ if (existsSync3(DB_FILE)) {
499
+ try {
500
+ const raw = readFileSync2(DB_FILE, "utf-8");
501
+ store = JSON.parse(raw);
502
+ store.conversations = store.conversations || [];
503
+ store.memories = store.memories || [];
504
+ store.sessions = store.sessions || [];
505
+ store.usageStats = store.usageStats || [];
506
+ store.cronJobs = store.cronJobs || [];
507
+ store.skillsInstalled = store.skillsInstalled || [];
508
+ } catch {
509
+ logger_default.warn(COMPONENT5, "Could not load data store, creating fresh one");
510
+ store = getDefaultStore();
511
+ }
512
+ } else {
513
+ store = getDefaultStore();
514
+ }
515
+ return store;
516
+ }
517
+ function saveStore() {
518
+ if (!store) return;
519
+ ensureDir(TITAN_HOME);
520
+ try {
521
+ writeFileSync2(DB_FILE, JSON.stringify(store, null, 2), "utf-8");
522
+ } catch (e) {
523
+ logger_default.error(COMPONENT5, `Failed to save data: ${e.message}`);
524
+ }
525
+ }
526
+ function debouncedSave() {
527
+ if (saveTimeout) clearTimeout(saveTimeout);
528
+ saveTimeout = setTimeout(saveStore, 1e3);
529
+ }
530
+ function getDb() {
531
+ return loadStore();
532
+ }
533
+ function saveMessage(message, e2eKey) {
534
+ const s = loadStore();
535
+ let content = message.content;
536
+ let isEncrypted = false;
537
+ if (e2eKey) {
538
+ try {
539
+ const payload = encrypt(message.content, Buffer.from(e2eKey, "base64"));
540
+ content = JSON.stringify(payload);
541
+ isEncrypted = true;
542
+ } catch (e) {
543
+ logger_default.error(COMPONENT5, `Failed to encrypt message for storage`);
544
+ content = "[ENCRYPTION FAILED] " + content;
545
+ }
546
+ }
547
+ s.conversations.push({
548
+ ...message,
549
+ content,
550
+ isEncrypted,
551
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
552
+ });
553
+ if (s.conversations.length > 5e3) {
554
+ s.conversations = s.conversations.slice(-5e3);
555
+ }
556
+ debouncedSave();
557
+ }
558
+ function getHistory(sessionId, limit = 50, e2eKey) {
559
+ const s = loadStore();
560
+ const rawHistory = s.conversations.filter((m) => m.sessionId === sessionId).slice(-limit);
561
+ if (!e2eKey) {
562
+ return rawHistory;
563
+ }
564
+ return rawHistory.map((m) => {
565
+ if (m.isEncrypted) {
566
+ try {
567
+ const payload = JSON.parse(m.content);
568
+ return {
569
+ ...m,
570
+ content: decrypt(payload, Buffer.from(e2eKey, "base64"))
571
+ };
572
+ } catch (e) {
573
+ logger_default.error(COMPONENT5, `Failed to decrypt message ${m.id}`);
574
+ return { ...m, content: "[DECRYPTION FAILED]" };
575
+ }
576
+ }
577
+ return m;
578
+ });
579
+ }
580
+ function searchMemories(category, query) {
581
+ const s = loadStore();
582
+ let results = s.memories;
583
+ if (category) {
584
+ results = results.filter((m) => m.category === category);
585
+ }
586
+ if (query) {
587
+ const q = query.toLowerCase();
588
+ results = results.filter(
589
+ (m) => m.key.toLowerCase().includes(q) || m.value.toLowerCase().includes(q)
590
+ );
591
+ }
592
+ return results.slice(-50).map((m) => ({ key: m.key, value: m.value, category: m.category }));
593
+ }
594
+ function recordUsage(sessionId, provider, model, promptTokens, completionTokens) {
595
+ const s = loadStore();
596
+ s.usageStats.push({
597
+ id: s.usageStats.length + 1,
598
+ session_id: sessionId,
599
+ provider,
600
+ model,
601
+ prompt_tokens: promptTokens,
602
+ completion_tokens: completionTokens,
603
+ total_tokens: promptTokens + completionTokens,
604
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
605
+ });
606
+ if (s.usageStats.length > 1e4) {
607
+ s.usageStats = s.usageStats.slice(-1e4);
608
+ }
609
+ debouncedSave();
610
+ }
611
+ var COMPONENT5, DB_FILE, store, saveTimeout;
612
+ var init_memory = __esm({
613
+ "src/memory/memory.ts"() {
614
+ "use strict";
615
+ init_constants();
616
+ init_helpers();
617
+ init_logger();
618
+ init_encryption();
619
+ COMPONENT5 = "Memory";
620
+ DB_FILE = join2(TITAN_HOME, "titan-data.json");
621
+ store = null;
622
+ saveTimeout = null;
623
+ }
624
+ });
625
+
626
+ // src/agent/session.ts
627
+ import { v4 as uuid } from "uuid";
628
+ function getOrCreateSession(channel, userId, agentId = "default", isEncrypted = false) {
629
+ const sessionKey = `${channel}:${userId}:${agentId}`;
630
+ const cached = activeSessions.get(sessionKey);
631
+ if (cached && cached.status === "active") {
632
+ return cached;
633
+ }
634
+ const store2 = getDb();
635
+ const existing = store2.sessions.find(
636
+ (s) => s.channel === channel && s.user_id === userId && s.agent_id === agentId && s.status === "active"
637
+ );
638
+ if (existing) {
639
+ const lastActive = new Date(existing.last_active || existing.created_at).getTime();
640
+ if (Date.now() - lastActive > SESSION_TIMEOUT_MS) {
641
+ existing.status = "idle";
642
+ logger_default.debug(COMPONENT6, `Session ${existing.id} timed out, creating new one`);
643
+ } else {
644
+ const session2 = {
645
+ id: existing.id,
646
+ channel: existing.channel,
647
+ userId: existing.user_id,
648
+ agentId: existing.agent_id,
649
+ status: existing.status,
650
+ messageCount: existing.message_count,
651
+ createdAt: existing.created_at,
652
+ lastActive: existing.last_active,
653
+ // Note: If a session was encrypted but dropped from memory, we cannot recover the key
654
+ // A robust implementation would involve key exchange, but for now we warn:
655
+ e2eKey: void 0
656
+ };
657
+ if (isEncrypted) {
658
+ logger_default.warn(COMPONENT6, `Recovered session ${existing.id}, but E2E key was lost from memory.`);
659
+ }
660
+ activeSessions.set(sessionKey, session2);
661
+ return session2;
662
+ }
663
+ }
664
+ const session = {
665
+ id: uuid(),
666
+ channel,
667
+ userId,
668
+ agentId,
669
+ status: "active",
670
+ messageCount: 0,
671
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
672
+ lastActive: (/* @__PURE__ */ new Date()).toISOString()
673
+ };
674
+ if (isEncrypted) {
675
+ Promise.resolve().then(() => (init_encryption(), encryption_exports)).then(({ generateKey: generateKey2 }) => {
676
+ session.e2eKey = generateKey2().toString("base64");
677
+ logger_default.info(COMPONENT6, `Generated E2E key for session ${session.id}`);
678
+ }).catch((err) => {
679
+ logger_default.error(COMPONENT6, `Failed to load encryption module: ${err}`);
680
+ });
681
+ }
682
+ store2.sessions.push({
683
+ id: session.id,
684
+ channel,
685
+ user_id: userId,
686
+ agent_id: agentId,
687
+ status: "active",
688
+ message_count: 0,
689
+ created_at: session.createdAt,
690
+ last_active: session.lastActive
691
+ });
692
+ activeSessions.set(sessionKey, session);
693
+ logger_default.info(COMPONENT6, `Created new session: ${session.id} (${channel}/${userId})`);
694
+ return session;
695
+ }
696
+ function addMessage(session, role, content, extra) {
697
+ const messageId = uuid();
698
+ saveMessage({
699
+ id: messageId,
700
+ sessionId: session.id,
701
+ role,
702
+ content,
703
+ toolCalls: extra?.toolCalls,
704
+ toolCallId: extra?.toolCallId,
705
+ model: extra?.model,
706
+ tokenCount: extra?.tokenCount || 0
707
+ }, session.e2eKey);
708
+ session.messageCount++;
709
+ session.lastActive = (/* @__PURE__ */ new Date()).toISOString();
710
+ const store2 = getDb();
711
+ const sessionRec = store2.sessions.find((s) => s.id === session.id);
712
+ if (sessionRec) {
713
+ sessionRec.message_count = session.messageCount;
714
+ sessionRec.last_active = session.lastActive;
715
+ }
716
+ }
717
+ function getContextMessages(session, maxMessages = MAX_CONTEXT_MESSAGES) {
718
+ const history = getHistory(session.id, maxMessages, session.e2eKey);
719
+ return history.map((msg) => ({
720
+ role: msg.role,
721
+ content: msg.content,
722
+ toolCallId: msg.toolCallId || void 0,
723
+ toolCalls: msg.toolCalls ? JSON.parse(msg.toolCalls) : void 0
724
+ }));
725
+ }
726
+ function listSessions() {
727
+ const store2 = getDb();
728
+ return store2.sessions.filter((s) => s.status === "active").sort((a, b) => b.last_active.localeCompare(a.last_active)).map((s) => ({
729
+ id: s.id,
730
+ channel: s.channel,
731
+ userId: s.user_id,
732
+ agentId: s.agent_id,
733
+ status: s.status,
734
+ messageCount: s.message_count,
735
+ createdAt: s.created_at,
736
+ lastActive: s.last_active
737
+ }));
738
+ }
739
+ function closeSession(sessionId) {
740
+ const store2 = getDb();
741
+ const sessionRec = store2.sessions.find((s) => s.id === sessionId);
742
+ if (sessionRec) {
743
+ sessionRec.status = "closed";
744
+ }
745
+ for (const [key, session] of activeSessions) {
746
+ if (session.id === sessionId) {
747
+ activeSessions.delete(key);
748
+ break;
749
+ }
750
+ }
751
+ logger_default.info(COMPONENT6, `Closed session: ${sessionId}`);
752
+ }
753
+ var COMPONENT6, activeSessions;
754
+ var init_session = __esm({
755
+ "src/agent/session.ts"() {
756
+ "use strict";
757
+ init_memory();
758
+ init_constants();
759
+ init_logger();
760
+ COMPONENT6 = "Session";
761
+ activeSessions = /* @__PURE__ */ new Map();
762
+ }
763
+ });
764
+
765
+ // src/providers/base.ts
766
+ var LLMProvider;
767
+ var init_base = __esm({
768
+ "src/providers/base.ts"() {
769
+ "use strict";
770
+ LLMProvider = class {
771
+ /** Get the provider identifier from a model string like "anthropic/claude-3" */
772
+ static parseModelId(modelId) {
773
+ const parts = modelId.split("/");
774
+ if (parts.length >= 2) {
775
+ return { provider: parts[0], model: parts.slice(1).join("/") };
776
+ }
777
+ return { provider: "anthropic", model: modelId };
778
+ }
779
+ };
780
+ }
781
+ });
782
+
783
+ // src/providers/anthropic.ts
784
+ import { v4 as uuid2 } from "uuid";
785
+ var COMPONENT7, AnthropicProvider;
786
+ var init_anthropic = __esm({
787
+ "src/providers/anthropic.ts"() {
788
+ "use strict";
789
+ init_base();
790
+ init_config();
791
+ init_logger();
792
+ COMPONENT7 = "Anthropic";
793
+ AnthropicProvider = class extends LLMProvider {
794
+ name = "anthropic";
795
+ displayName = "Anthropic (Claude)";
796
+ get apiKey() {
797
+ const config = loadConfig();
798
+ return config.providers.anthropic.apiKey || process.env.ANTHROPIC_API_KEY || "";
799
+ }
800
+ get baseUrl() {
801
+ const config = loadConfig();
802
+ return config.providers.anthropic.baseUrl || "https://api.anthropic.com";
803
+ }
804
+ async chat(options) {
805
+ const model = options.model || "claude-sonnet-4-20250514";
806
+ const apiKey = this.apiKey;
807
+ if (!apiKey) throw new Error("Anthropic API key not configured");
808
+ logger_default.debug(COMPONENT7, `Chat request: model=${model}, messages=${options.messages.length}`);
809
+ const systemMessage = options.messages.find((m) => m.role === "system");
810
+ const nonSystemMessages = options.messages.filter((m) => m.role !== "system");
811
+ const body = {
812
+ model: model.replace("anthropic/", ""),
813
+ max_tokens: options.maxTokens || 8192,
814
+ messages: nonSystemMessages.map((m) => ({
815
+ role: m.role === "tool" ? "user" : m.role,
816
+ content: m.role === "tool" ? [{ type: "tool_result", tool_use_id: m.toolCallId, content: m.content }] : m.content
817
+ }))
818
+ };
819
+ if (systemMessage) {
820
+ body.system = systemMessage.content;
821
+ }
822
+ if (options.tools && options.tools.length > 0) {
823
+ body.tools = options.tools.map((t) => ({
824
+ name: t.function.name,
825
+ description: t.function.description,
826
+ input_schema: t.function.parameters
827
+ }));
828
+ }
829
+ if (options.temperature !== void 0) {
830
+ body.temperature = options.temperature;
831
+ }
832
+ const response = await fetch(`${this.baseUrl}/v1/messages`, {
833
+ method: "POST",
834
+ headers: {
835
+ "Content-Type": "application/json",
836
+ "x-api-key": apiKey,
837
+ "anthropic-version": "2023-06-01"
838
+ },
839
+ body: JSON.stringify(body)
840
+ });
841
+ if (!response.ok) {
842
+ const errorText = await response.text();
843
+ throw new Error(`Anthropic API error (${response.status}): ${errorText}`);
844
+ }
845
+ const data = await response.json();
846
+ const content = data.content;
847
+ let textContent = "";
848
+ const toolCalls = [];
849
+ for (const block of content) {
850
+ if (block.type === "text") {
851
+ textContent += block.text;
852
+ } else if (block.type === "tool_use") {
853
+ toolCalls.push({
854
+ id: block.id,
855
+ type: "function",
856
+ function: {
857
+ name: block.name,
858
+ arguments: JSON.stringify(block.input)
859
+ }
860
+ });
861
+ }
862
+ }
863
+ const usage = data.usage;
864
+ return {
865
+ id: data.id || uuid2(),
866
+ content: textContent,
867
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
868
+ usage: usage ? {
869
+ promptTokens: usage.input_tokens,
870
+ completionTokens: usage.output_tokens,
871
+ totalTokens: usage.input_tokens + usage.output_tokens
872
+ } : void 0,
873
+ finishReason: toolCalls.length > 0 ? "tool_calls" : "stop",
874
+ model
875
+ };
876
+ }
877
+ async *chatStream(options) {
878
+ try {
879
+ const response = await this.chat(options);
880
+ if (response.content) {
881
+ yield { type: "text", content: response.content };
882
+ }
883
+ if (response.toolCalls) {
884
+ for (const tc of response.toolCalls) {
885
+ yield { type: "tool_call", toolCall: tc };
886
+ }
887
+ }
888
+ yield { type: "done" };
889
+ } catch (error) {
890
+ yield { type: "error", error: error.message };
891
+ }
892
+ }
893
+ async listModels() {
894
+ return [
895
+ "claude-opus-4-0",
896
+ "claude-sonnet-4-20250514",
897
+ "claude-haiku-4-20250414",
898
+ "claude-3-5-sonnet-20241022",
899
+ "claude-3-5-haiku-20241022"
900
+ ];
901
+ }
902
+ async healthCheck() {
903
+ try {
904
+ if (!this.apiKey) return false;
905
+ const response = await fetch(`${this.baseUrl}/v1/messages`, {
906
+ method: "POST",
907
+ headers: {
908
+ "Content-Type": "application/json",
909
+ "x-api-key": this.apiKey,
910
+ "anthropic-version": "2023-06-01"
911
+ },
912
+ body: JSON.stringify({
913
+ model: "claude-haiku-4-20250414",
914
+ max_tokens: 1,
915
+ messages: [{ role: "user", content: "ping" }]
916
+ })
917
+ });
918
+ return response.ok || response.status === 400;
919
+ } catch {
920
+ return false;
921
+ }
922
+ }
923
+ };
924
+ }
925
+ });
926
+
927
+ // src/providers/openai.ts
928
+ import { v4 as uuid3 } from "uuid";
929
+ var COMPONENT8, OpenAIProvider;
930
+ var init_openai = __esm({
931
+ "src/providers/openai.ts"() {
932
+ "use strict";
933
+ init_base();
934
+ init_config();
935
+ init_logger();
936
+ COMPONENT8 = "OpenAI";
937
+ OpenAIProvider = class extends LLMProvider {
938
+ name = "openai";
939
+ displayName = "OpenAI (GPT)";
940
+ get apiKey() {
941
+ const config = loadConfig();
942
+ return config.providers.openai.apiKey || process.env.OPENAI_API_KEY || "";
943
+ }
944
+ get baseUrl() {
945
+ const config = loadConfig();
946
+ return config.providers.openai.baseUrl || "https://api.openai.com";
947
+ }
948
+ async chat(options) {
949
+ const model = options.model || "gpt-4o";
950
+ const apiKey = this.apiKey;
951
+ if (!apiKey) throw new Error("OpenAI API key not configured");
952
+ logger_default.debug(COMPONENT8, `Chat request: model=${model}, messages=${options.messages.length}`);
953
+ const body = {
954
+ model: model.replace("openai/", ""),
955
+ messages: options.messages.map((m) => {
956
+ if (m.role === "tool") {
957
+ return { role: "tool", content: m.content, tool_call_id: m.toolCallId };
958
+ }
959
+ if (m.role === "assistant" && m.toolCalls) {
960
+ return {
961
+ role: "assistant",
962
+ content: m.content || null,
963
+ tool_calls: m.toolCalls.map((tc) => ({
964
+ id: tc.id,
965
+ type: "function",
966
+ function: { name: tc.function.name, arguments: tc.function.arguments }
967
+ }))
968
+ };
969
+ }
970
+ return { role: m.role, content: m.content };
971
+ }),
972
+ max_tokens: options.maxTokens || 8192
973
+ };
974
+ if (options.tools && options.tools.length > 0) {
975
+ body.tools = options.tools;
976
+ }
977
+ if (options.temperature !== void 0) {
978
+ body.temperature = options.temperature;
979
+ }
980
+ const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
981
+ method: "POST",
982
+ headers: {
983
+ "Content-Type": "application/json",
984
+ Authorization: `Bearer ${apiKey}`
985
+ },
986
+ body: JSON.stringify(body)
987
+ });
988
+ if (!response.ok) {
989
+ const errorText = await response.text();
990
+ throw new Error(`OpenAI API error (${response.status}): ${errorText}`);
991
+ }
992
+ const data = await response.json();
993
+ const choices = data.choices;
994
+ const choice = choices[0];
995
+ const message = choice.message;
996
+ const toolCalls = [];
997
+ if (message.tool_calls) {
998
+ for (const tc of message.tool_calls) {
999
+ const fn = tc.function;
1000
+ toolCalls.push({
1001
+ id: tc.id,
1002
+ type: "function",
1003
+ function: { name: fn.name, arguments: fn.arguments }
1004
+ });
1005
+ }
1006
+ }
1007
+ const usage = data.usage;
1008
+ return {
1009
+ id: data.id || uuid3(),
1010
+ content: message.content || "",
1011
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
1012
+ usage: usage ? {
1013
+ promptTokens: usage.prompt_tokens,
1014
+ completionTokens: usage.completion_tokens,
1015
+ totalTokens: usage.total_tokens
1016
+ } : void 0,
1017
+ finishReason: toolCalls.length > 0 ? "tool_calls" : choice.finish_reason || "stop",
1018
+ model
1019
+ };
1020
+ }
1021
+ async *chatStream(options) {
1022
+ try {
1023
+ const response = await this.chat(options);
1024
+ if (response.content) {
1025
+ yield { type: "text", content: response.content };
1026
+ }
1027
+ if (response.toolCalls) {
1028
+ for (const tc of response.toolCalls) {
1029
+ yield { type: "tool_call", toolCall: tc };
1030
+ }
1031
+ }
1032
+ yield { type: "done" };
1033
+ } catch (error) {
1034
+ yield { type: "error", error: error.message };
1035
+ }
1036
+ }
1037
+ async listModels() {
1038
+ return ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "o1", "o1-mini", "o3-mini"];
1039
+ }
1040
+ async healthCheck() {
1041
+ try {
1042
+ if (!this.apiKey) return false;
1043
+ const response = await fetch(`${this.baseUrl}/v1/models`, {
1044
+ headers: { Authorization: `Bearer ${this.apiKey}` }
1045
+ });
1046
+ return response.ok;
1047
+ } catch {
1048
+ return false;
1049
+ }
1050
+ }
1051
+ };
1052
+ }
1053
+ });
1054
+
1055
+ // src/providers/google.ts
1056
+ import { v4 as uuid4 } from "uuid";
1057
+ var COMPONENT9, GoogleProvider;
1058
+ var init_google = __esm({
1059
+ "src/providers/google.ts"() {
1060
+ "use strict";
1061
+ init_base();
1062
+ init_config();
1063
+ init_logger();
1064
+ COMPONENT9 = "Google";
1065
+ GoogleProvider = class extends LLMProvider {
1066
+ name = "google";
1067
+ displayName = "Google (Gemini)";
1068
+ get apiKey() {
1069
+ const config = loadConfig();
1070
+ return config.providers.google.apiKey || process.env.GOOGLE_API_KEY || "";
1071
+ }
1072
+ async chat(options) {
1073
+ const model = (options.model || "gemini-2.0-flash").replace("google/", "");
1074
+ const apiKey = this.apiKey;
1075
+ if (!apiKey) throw new Error("Google API key not configured");
1076
+ logger_default.debug(COMPONENT9, `Chat request: model=${model}, messages=${options.messages.length}`);
1077
+ const systemInstruction = options.messages.find((m) => m.role === "system")?.content;
1078
+ const contents = options.messages.filter((m) => m.role !== "system").map((m) => ({
1079
+ role: m.role === "assistant" ? "model" : "user",
1080
+ parts: [{ text: m.content }]
1081
+ }));
1082
+ const body = {
1083
+ contents,
1084
+ generationConfig: {
1085
+ maxOutputTokens: options.maxTokens || 8192,
1086
+ temperature: options.temperature ?? 0.7
1087
+ }
1088
+ };
1089
+ if (systemInstruction) {
1090
+ body.systemInstruction = { parts: [{ text: systemInstruction }] };
1091
+ }
1092
+ if (options.tools && options.tools.length > 0) {
1093
+ body.tools = [{
1094
+ functionDeclarations: options.tools.map((t) => ({
1095
+ name: t.function.name,
1096
+ description: t.function.description,
1097
+ parameters: t.function.parameters
1098
+ }))
1099
+ }];
1100
+ }
1101
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
1102
+ const response = await fetch(url, {
1103
+ method: "POST",
1104
+ headers: { "Content-Type": "application/json" },
1105
+ body: JSON.stringify(body)
1106
+ });
1107
+ if (!response.ok) {
1108
+ const errorText = await response.text();
1109
+ throw new Error(`Google API error (${response.status}): ${errorText}`);
1110
+ }
1111
+ const data = await response.json();
1112
+ const candidates = data.candidates;
1113
+ let textContent = "";
1114
+ const toolCalls = [];
1115
+ if (candidates && candidates.length > 0) {
1116
+ const parts = candidates[0].content?.parts || [];
1117
+ for (const part of parts) {
1118
+ if (part.text) {
1119
+ textContent += part.text;
1120
+ }
1121
+ if (part.functionCall) {
1122
+ const fc = part.functionCall;
1123
+ toolCalls.push({
1124
+ id: uuid4(),
1125
+ type: "function",
1126
+ function: {
1127
+ name: fc.name,
1128
+ arguments: JSON.stringify(fc.args)
1129
+ }
1130
+ });
1131
+ }
1132
+ }
1133
+ }
1134
+ const usageMeta = data.usageMetadata;
1135
+ return {
1136
+ id: uuid4(),
1137
+ content: textContent,
1138
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
1139
+ usage: usageMeta ? {
1140
+ promptTokens: usageMeta.promptTokenCount || 0,
1141
+ completionTokens: usageMeta.candidatesTokenCount || 0,
1142
+ totalTokens: usageMeta.totalTokenCount || 0
1143
+ } : void 0,
1144
+ finishReason: toolCalls.length > 0 ? "tool_calls" : "stop",
1145
+ model: `google/${model}`
1146
+ };
1147
+ }
1148
+ async *chatStream(options) {
1149
+ try {
1150
+ const response = await this.chat(options);
1151
+ if (response.content) yield { type: "text", content: response.content };
1152
+ if (response.toolCalls) {
1153
+ for (const tc of response.toolCalls) yield { type: "tool_call", toolCall: tc };
1154
+ }
1155
+ yield { type: "done" };
1156
+ } catch (error) {
1157
+ yield { type: "error", error: error.message };
1158
+ }
1159
+ }
1160
+ async listModels() {
1161
+ return ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash", "gemini-1.5-pro"];
1162
+ }
1163
+ async healthCheck() {
1164
+ try {
1165
+ if (!this.apiKey) return false;
1166
+ const url = `https://generativelanguage.googleapis.com/v1beta/models?key=${this.apiKey}`;
1167
+ const response = await fetch(url);
1168
+ return response.ok;
1169
+ } catch {
1170
+ return false;
1171
+ }
1172
+ }
1173
+ };
1174
+ }
1175
+ });
1176
+
1177
+ // src/providers/ollama.ts
1178
+ import { v4 as uuid5 } from "uuid";
1179
+ var COMPONENT10, OllamaProvider;
1180
+ var init_ollama = __esm({
1181
+ "src/providers/ollama.ts"() {
1182
+ "use strict";
1183
+ init_base();
1184
+ init_config();
1185
+ init_logger();
1186
+ COMPONENT10 = "Ollama";
1187
+ OllamaProvider = class extends LLMProvider {
1188
+ name = "ollama";
1189
+ displayName = "Ollama (Local)";
1190
+ get baseUrl() {
1191
+ const config = loadConfig();
1192
+ return config.providers.ollama.baseUrl || process.env.OLLAMA_BASE_URL || "http://localhost:11434";
1193
+ }
1194
+ async chat(options) {
1195
+ const model = (options.model || "llama3.1").replace("ollama/", "");
1196
+ logger_default.debug(COMPONENT10, `Chat request: model=${model}, messages=${options.messages.length}`);
1197
+ const body = {
1198
+ model,
1199
+ messages: options.messages.map((m) => ({
1200
+ role: m.role,
1201
+ content: m.content
1202
+ })),
1203
+ stream: false,
1204
+ options: {
1205
+ num_predict: options.maxTokens || 8192,
1206
+ temperature: options.temperature ?? 0.7
1207
+ }
1208
+ };
1209
+ if (options.tools && options.tools.length > 0) {
1210
+ body.tools = options.tools.map((t) => ({
1211
+ type: "function",
1212
+ function: {
1213
+ name: t.function.name,
1214
+ description: t.function.description,
1215
+ parameters: t.function.parameters
1216
+ }
1217
+ }));
1218
+ }
1219
+ const response = await fetch(`${this.baseUrl}/api/chat`, {
1220
+ method: "POST",
1221
+ headers: { "Content-Type": "application/json" },
1222
+ body: JSON.stringify(body)
1223
+ });
1224
+ if (!response.ok) {
1225
+ const errorText = await response.text();
1226
+ throw new Error(`Ollama error (${response.status}): ${errorText}`);
1227
+ }
1228
+ const data = await response.json();
1229
+ const message = data.message;
1230
+ const toolCalls = [];
1231
+ if (message.tool_calls) {
1232
+ for (const tc of message.tool_calls) {
1233
+ const fn = tc.function;
1234
+ toolCalls.push({
1235
+ id: uuid5(),
1236
+ type: "function",
1237
+ function: {
1238
+ name: fn.name,
1239
+ arguments: JSON.stringify(fn.arguments)
1240
+ }
1241
+ });
1242
+ }
1243
+ }
1244
+ return {
1245
+ id: uuid5(),
1246
+ content: message.content || "",
1247
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
1248
+ usage: {
1249
+ promptTokens: data.prompt_eval_count || 0,
1250
+ completionTokens: data.eval_count || 0,
1251
+ totalTokens: (data.prompt_eval_count || 0) + (data.eval_count || 0)
1252
+ },
1253
+ finishReason: toolCalls.length > 0 ? "tool_calls" : "stop",
1254
+ model: `ollama/${model}`
1255
+ };
1256
+ }
1257
+ async *chatStream(options) {
1258
+ try {
1259
+ const response = await this.chat(options);
1260
+ if (response.content) yield { type: "text", content: response.content };
1261
+ if (response.toolCalls) {
1262
+ for (const tc of response.toolCalls) yield { type: "tool_call", toolCall: tc };
1263
+ }
1264
+ yield { type: "done" };
1265
+ } catch (error) {
1266
+ yield { type: "error", error: error.message };
1267
+ }
1268
+ }
1269
+ async listModels() {
1270
+ try {
1271
+ const response = await fetch(`${this.baseUrl}/api/tags`);
1272
+ if (!response.ok) return [];
1273
+ const data = await response.json();
1274
+ return (data.models || []).map((m) => m.name);
1275
+ } catch {
1276
+ return [];
1277
+ }
1278
+ }
1279
+ async healthCheck() {
1280
+ try {
1281
+ const response = await fetch(`${this.baseUrl}/api/tags`);
1282
+ return response.ok;
1283
+ } catch {
1284
+ return false;
1285
+ }
1286
+ }
1287
+ };
1288
+ }
1289
+ });
1290
+
1291
+ // src/providers/router.ts
1292
+ function initProviders() {
1293
+ if (initialized) return;
1294
+ providers.set("anthropic", new AnthropicProvider());
1295
+ providers.set("openai", new OpenAIProvider());
1296
+ providers.set("google", new GoogleProvider());
1297
+ providers.set("ollama", new OllamaProvider());
1298
+ initialized = true;
1299
+ }
1300
+ function resolveModel(modelId) {
1301
+ initProviders();
1302
+ const { provider: providerName, model } = LLMProvider.parseModelId(modelId);
1303
+ const provider = providers.get(providerName);
1304
+ if (!provider) {
1305
+ throw new Error(`Unknown provider: ${providerName}. Available: ${Array.from(providers.keys()).join(", ")}`);
1306
+ }
1307
+ return { provider, model };
1308
+ }
1309
+ async function chat(options) {
1310
+ const modelId = options.model || "anthropic/claude-sonnet-4-20250514";
1311
+ const { provider, model } = resolveModel(modelId);
1312
+ logger_default.info(COMPONENT11, `Routing to ${provider.displayName} (model: ${model})`);
1313
+ try {
1314
+ return await provider.chat({ ...options, model });
1315
+ } catch (error) {
1316
+ logger_default.error(COMPONENT11, `Provider ${provider.name} failed: ${error.message}`);
1317
+ const failoverOrder = ["anthropic", "openai", "google", "ollama"];
1318
+ for (const fallbackName of failoverOrder) {
1319
+ if (fallbackName === provider.name) continue;
1320
+ const fallback = providers.get(fallbackName);
1321
+ if (!fallback) continue;
1322
+ try {
1323
+ const healthy = await fallback.healthCheck();
1324
+ if (!healthy) continue;
1325
+ const models = await fallback.listModels();
1326
+ if (models.length === 0) continue;
1327
+ logger_default.warn(COMPONENT11, `Failing over to ${fallback.displayName} (model: ${models[0]})`);
1328
+ return await fallback.chat({ ...options, model: models[0] });
1329
+ } catch {
1330
+ continue;
1331
+ }
1332
+ }
1333
+ throw error;
1334
+ }
1335
+ }
1336
+ var COMPONENT11, providers, initialized;
1337
+ var init_router = __esm({
1338
+ "src/providers/router.ts"() {
1339
+ "use strict";
1340
+ init_base();
1341
+ init_anthropic();
1342
+ init_openai();
1343
+ init_google();
1344
+ init_ollama();
1345
+ init_logger();
1346
+ COMPONENT11 = "Router";
1347
+ providers = /* @__PURE__ */ new Map();
1348
+ initialized = false;
1349
+ }
1350
+ });
1351
+
1352
+ // src/memory/learning.ts
1353
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1354
+ import { join as join3 } from "path";
1355
+ function loadKnowledgeBase() {
1356
+ if (kb) return kb;
1357
+ ensureDir(TITAN_HOME);
1358
+ if (existsSync4(KNOWLEDGE_FILE)) {
1359
+ try {
1360
+ kb = JSON.parse(readFileSync3(KNOWLEDGE_FILE, "utf-8"));
1361
+ kb.entries = kb.entries || [];
1362
+ kb.toolSuccessRates = kb.toolSuccessRates || {};
1363
+ kb.errorPatterns = kb.errorPatterns || {};
1364
+ kb.userCorrections = kb.userCorrections || [];
1365
+ kb.conversationInsights = kb.conversationInsights || [];
1366
+ } catch {
1367
+ kb = createEmptyKB();
1368
+ }
1369
+ } else {
1370
+ kb = createEmptyKB();
1371
+ }
1372
+ return kb;
1373
+ }
1374
+ function createEmptyKB() {
1375
+ return {
1376
+ entries: [],
1377
+ toolSuccessRates: {},
1378
+ errorPatterns: {},
1379
+ userCorrections: [],
1380
+ conversationInsights: []
1381
+ };
1382
+ }
1383
+ function debouncedSave2() {
1384
+ if (saveTimeout2) clearTimeout(saveTimeout2);
1385
+ saveTimeout2 = setTimeout(() => {
1386
+ if (!kb) return;
1387
+ ensureDir(TITAN_HOME);
1388
+ writeFileSync3(KNOWLEDGE_FILE, JSON.stringify(kb, null, 2), "utf-8");
1389
+ }, 2e3);
1390
+ }
1391
+ function recordToolResult(toolName, success, context, error) {
1392
+ const k = loadKnowledgeBase();
1393
+ if (!k.toolSuccessRates[toolName]) {
1394
+ k.toolSuccessRates[toolName] = { success: 0, fail: 0, total: 0 };
1395
+ }
1396
+ k.toolSuccessRates[toolName].total++;
1397
+ if (success) {
1398
+ k.toolSuccessRates[toolName].success++;
1399
+ } else {
1400
+ k.toolSuccessRates[toolName].fail++;
1401
+ if (error) {
1402
+ const pattern = error.slice(0, 200);
1403
+ if (!k.errorPatterns[pattern]) {
1404
+ k.errorPatterns[pattern] = { count: 0, lastSeen: "" };
1405
+ }
1406
+ k.errorPatterns[pattern].count++;
1407
+ k.errorPatterns[pattern].lastSeen = (/* @__PURE__ */ new Date()).toISOString();
1408
+ }
1409
+ }
1410
+ debouncedSave2();
1411
+ }
1412
+ function getToolRecommendations() {
1413
+ const k = loadKnowledgeBase();
1414
+ const recommendations = {};
1415
+ for (const [tool, stats] of Object.entries(k.toolSuccessRates)) {
1416
+ if (stats.total > 0) {
1417
+ recommendations[tool] = stats.success / stats.total;
1418
+ }
1419
+ }
1420
+ return recommendations;
1421
+ }
1422
+ function getLearningContext() {
1423
+ const k = loadKnowledgeBase();
1424
+ const parts = [];
1425
+ const topEntries = k.entries.filter((e) => e.score > 0.6).sort((a, b) => b.score - a.score).slice(0, 10);
1426
+ if (topEntries.length > 0) {
1427
+ parts.push("Key learned facts:");
1428
+ for (const e of topEntries) {
1429
+ parts.push(`- [${e.category}] ${e.content}`);
1430
+ }
1431
+ }
1432
+ const toolRecs = getToolRecommendations();
1433
+ const bestTools = Object.entries(toolRecs).filter(([_, rate]) => rate > 0.8).sort((a, b) => b[1] - a[1]).slice(0, 5);
1434
+ if (bestTools.length > 0) {
1435
+ parts.push("\nMost reliable tools:");
1436
+ for (const [tool, rate] of bestTools) {
1437
+ parts.push(`- ${tool}: ${Math.round(rate * 100)}% success rate`);
1438
+ }
1439
+ }
1440
+ const frequentErrors = Object.entries(k.errorPatterns).filter(([_, info]) => info.count > 2).sort((a, b) => b[1].count - a[1].count).slice(0, 3);
1441
+ if (frequentErrors.length > 0) {
1442
+ parts.push("\nCommon errors to avoid:");
1443
+ for (const [pattern, info] of frequentErrors) {
1444
+ parts.push(`- ${pattern.slice(0, 100)} (seen ${info.count}x)${info.resolution ? ` \u2192 Fix: ${info.resolution}` : ""}`);
1445
+ }
1446
+ }
1447
+ return parts.join("\n");
1448
+ }
1449
+ var KNOWLEDGE_FILE, kb, saveTimeout2;
1450
+ var init_learning = __esm({
1451
+ "src/memory/learning.ts"() {
1452
+ "use strict";
1453
+ init_constants();
1454
+ init_helpers();
1455
+ init_logger();
1456
+ KNOWLEDGE_FILE = join3(TITAN_HOME, "knowledge.json");
1457
+ kb = null;
1458
+ saveTimeout2 = null;
1459
+ }
1460
+ });
1461
+
1462
+ // src/agent/agent.ts
1463
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1464
+ function readPromptFile(path) {
1465
+ try {
1466
+ if (existsSync5(path)) return readFileSync4(path, "utf-8");
1467
+ } catch {
1468
+ }
1469
+ return "";
1470
+ }
1471
+ function buildSystemPrompt(config) {
1472
+ const customPrompt = config.agent.systemPrompt || "";
1473
+ const memories = searchMemories("preference");
1474
+ const memoryContext = memories.length > 0 ? `
1475
+
1476
+ User preferences I remember:
1477
+ ${memories.map((m) => `- ${m.key}: ${m.value}`).join("\n")}` : "";
1478
+ const agentsMd = readPromptFile(AGENTS_MD);
1479
+ const soulMd = readPromptFile(SOUL_MD);
1480
+ const toolsMd = readPromptFile(TOOLS_MD);
1481
+ const workspaceContext = [
1482
+ agentsMd ? `
1483
+ ## Agent Instructions (AGENTS.md)
1484
+ ${agentsMd}` : "",
1485
+ soulMd ? `
1486
+ ## Personality (SOUL.md)
1487
+ ${soulMd}` : "",
1488
+ toolsMd ? `
1489
+ ## Tool Notes (TOOLS.md)
1490
+ ${toolsMd}` : ""
1491
+ ].filter(Boolean).join("\n");
1492
+ const learningContext = getLearningContext();
1493
+ return `You are ${TITAN_NAME}, The Intelligent Task Automation Network \u2014 a powerful personal AI assistant.
1494
+
1495
+ ## Core Capabilities
1496
+ - Execute shell commands and scripts on the user's system
1497
+ - Read, write, edit, and manage files
1498
+ - Browse the web and extract information (browser control via CDP)
1499
+ - Schedule automated tasks with cron
1500
+ - Set up webhook endpoints
1501
+ - Search the web for current information
1502
+ - Control browser sessions (navigate, snapshot, evaluate)
1503
+ - Manage agent sessions (list, history, send, close)
1504
+ - Remember facts and user preferences persistently
1505
+
1506
+ ## Behavior Guidelines
1507
+ - Be proactive: if a task implies follow-up actions, suggest or perform them
1508
+ - Be concise but thorough in responses
1509
+ - When executing commands, always explain what you're doing and why
1510
+ - If a task could be destructive (deleting files, etc.), confirm with the user first
1511
+ - Use tools when they would be helpful \u2014 don't just describe what could be done
1512
+ - Remember important information about the user for future conversations
1513
+ - If you encounter an error, try alternative approaches before reporting failure
1514
+
1515
+ ## Security
1516
+ - Never expose API keys, passwords, or other secrets
1517
+ - Don't execute commands that could compromise system security without explicit approval
1518
+ - Respect file system boundaries set in the configuration
1519
+
1520
+ ## Continuous Learning
1521
+ You get smarter with every interaction. Below is your accumulated knowledge:
1522
+ ${learningContext}
1523
+ ${customPrompt ? `
1524
+ ## Custom Instructions
1525
+ ${customPrompt}` : ""}${workspaceContext}${memoryContext}`;
1526
+ }
1527
+ async function processMessage(message, channel = "cli", userId = "default") {
1528
+ const startTime = Date.now();
1529
+ const config = loadConfig();
1530
+ const session = getOrCreateSession(channel, userId);
1531
+ logger_default.info(COMPONENT12, `Processing message in session ${session.id} (${channel}/${userId})`);
1532
+ addMessage(session, "user", message);
1533
+ const systemPrompt = buildSystemPrompt(config);
1534
+ const historyMessages = getContextMessages(session);
1535
+ const tools = getToolDefinitions();
1536
+ const messages = [
1537
+ { role: "system", content: systemPrompt },
1538
+ ...historyMessages
1539
+ ];
1540
+ let totalPromptTokens = 0;
1541
+ let totalCompletionTokens = 0;
1542
+ const toolsUsed = [];
1543
+ let finalContent = "";
1544
+ let modelUsed = config.agent.model;
1545
+ for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
1546
+ logger_default.debug(COMPONENT12, `Round ${round + 1}: ${messages.length} messages, ${tools.length} tools`);
1547
+ const response = await chat({
1548
+ model: config.agent.model,
1549
+ messages,
1550
+ tools: tools.length > 0 ? tools : void 0,
1551
+ maxTokens: config.agent.maxTokens,
1552
+ temperature: config.agent.temperature
1553
+ });
1554
+ modelUsed = response.model;
1555
+ totalPromptTokens += response.usage?.promptTokens || 0;
1556
+ totalCompletionTokens += response.usage?.completionTokens || 0;
1557
+ if (!response.toolCalls || response.toolCalls.length === 0) {
1558
+ finalContent = response.content;
1559
+ break;
1560
+ }
1561
+ logger_default.info(COMPONENT12, `LLM requested ${response.toolCalls.length} tool call(s)`);
1562
+ messages.push({
1563
+ role: "assistant",
1564
+ content: response.content || "",
1565
+ toolCalls: response.toolCalls
1566
+ });
1567
+ const toolResults = await executeTools(response.toolCalls);
1568
+ for (const result of toolResults) {
1569
+ toolsUsed.push(result.name);
1570
+ messages.push({
1571
+ role: "tool",
1572
+ content: result.content,
1573
+ toolCallId: result.toolCallId
1574
+ });
1575
+ const success = !result.content.toLowerCase().includes("error:");
1576
+ recordToolResult(result.name, success, void 0, success ? void 0 : result.content.slice(0, 200));
1577
+ }
1578
+ if (round === MAX_TOOL_ROUNDS - 1) {
1579
+ finalContent = response.content || "I completed the tool operations. Let me know if you need anything else.";
1580
+ }
1581
+ }
1582
+ addMessage(session, "assistant", finalContent, {
1583
+ model: modelUsed,
1584
+ tokenCount: totalCompletionTokens
1585
+ });
1586
+ const { provider: providerName } = { provider: modelUsed.split("/")[0] || "unknown" };
1587
+ recordUsage(session.id, providerName, modelUsed, totalPromptTokens, totalCompletionTokens);
1588
+ const durationMs = Date.now() - startTime;
1589
+ logger_default.info(COMPONENT12, `Response generated in ${durationMs}ms (${totalPromptTokens + totalCompletionTokens} tokens)`);
1590
+ return {
1591
+ content: finalContent,
1592
+ sessionId: session.id,
1593
+ toolsUsed: [...new Set(toolsUsed)],
1594
+ tokenUsage: {
1595
+ prompt: totalPromptTokens,
1596
+ completion: totalCompletionTokens,
1597
+ total: totalPromptTokens + totalCompletionTokens
1598
+ },
1599
+ model: modelUsed,
1600
+ durationMs
1601
+ };
1602
+ }
1603
+ var COMPONENT12, MAX_TOOL_ROUNDS;
1604
+ var init_agent = __esm({
1605
+ "src/agent/agent.ts"() {
1606
+ "use strict";
1607
+ init_router();
1608
+ init_config();
1609
+ init_session();
1610
+ init_toolRunner();
1611
+ init_memory();
1612
+ init_learning();
1613
+ init_logger();
1614
+ init_constants();
1615
+ COMPONENT12 = "Agent";
1616
+ MAX_TOOL_ROUNDS = 10;
1617
+ }
1618
+ });
1619
+
1620
+ // src/skills/builtin/sessions.ts
1621
+ function registerSessionsSkill() {
1622
+ registerSkill(
1623
+ { name: "sessions_list", description: "List active sessions", version: "1.0.0", source: "bundled", enabled: true },
1624
+ {
1625
+ name: "sessions_list",
1626
+ description: "List all active agent sessions with their IDs, channels, users, and message counts.",
1627
+ parameters: {
1628
+ type: "object",
1629
+ properties: {}
1630
+ },
1631
+ execute: async () => {
1632
+ const sessions = listSessions();
1633
+ if (sessions.length === 0) return "No active sessions.";
1634
+ return sessions.map(
1635
+ (s) => `\u2022 ${s.id.slice(0, 8)} | ${s.channel} | user: ${s.userId} | msgs: ${s.messageCount} | last active: ${s.lastActive}`
1636
+ ).join("\n");
1637
+ }
1638
+ }
1639
+ );
1640
+ registerSkill(
1641
+ { name: "sessions_history", description: "Get session message history", version: "1.0.0", source: "bundled", enabled: true },
1642
+ {
1643
+ name: "sessions_history",
1644
+ description: "Retrieve the recent message history for a specific session.",
1645
+ parameters: {
1646
+ type: "object",
1647
+ properties: {
1648
+ sessionChannel: { type: "string", description: "Channel of the session" },
1649
+ sessionUserId: { type: "string", description: "User ID of the session" },
1650
+ limit: { type: "number", description: "Max messages to return (default: 20)" }
1651
+ },
1652
+ required: ["sessionChannel", "sessionUserId"]
1653
+ },
1654
+ execute: async (args) => {
1655
+ const channel = args.sessionChannel;
1656
+ const userId = args.sessionUserId;
1657
+ const limit = args.limit || 20;
1658
+ const session = getOrCreateSession(channel, userId);
1659
+ const messages = getContextMessages(session, limit);
1660
+ if (messages.length === 0) return `No messages in session ${channel}/${userId}.`;
1661
+ return messages.map(
1662
+ (m) => `[${m.role}] ${m.content.slice(0, 500)}`
1663
+ ).join("\n---\n");
1664
+ }
1665
+ }
1666
+ );
1667
+ registerSkill(
1668
+ { name: "sessions_send", description: "Send a message to another session", version: "1.0.0", source: "bundled", enabled: true },
1669
+ {
1670
+ name: "sessions_send",
1671
+ description: "Send a message to a specific user's session on a channel, triggering agent processing.",
1672
+ parameters: {
1673
+ type: "object",
1674
+ properties: {
1675
+ targetChannel: { type: "string", description: 'Target channel (e.g., "discord", "telegram")' },
1676
+ targetUserId: { type: "string", description: "Target user ID" },
1677
+ message: { type: "string", description: "Message to send" }
1678
+ },
1679
+ required: ["targetChannel", "targetUserId", "message"]
1680
+ },
1681
+ execute: async (args) => {
1682
+ const channel = args.targetChannel;
1683
+ const userId = args.targetUserId;
1684
+ const message = args.message;
1685
+ logger_default.info(COMPONENT13, `Sending inter-session message to ${channel}/${userId}`);
1686
+ try {
1687
+ const response = await processMessage(message, channel, userId);
1688
+ return `Message delivered to ${channel}/${userId}. Response: ${response.content.slice(0, 500)}`;
1689
+ } catch (error) {
1690
+ return `Error sending to ${channel}/${userId}: ${error.message}`;
1691
+ }
1692
+ }
1693
+ }
1694
+ );
1695
+ registerSkill(
1696
+ { name: "sessions_close", description: "Close a session", version: "1.0.0", source: "bundled", enabled: true },
1697
+ {
1698
+ name: "sessions_close",
1699
+ description: "Close a specific session by its session ID.",
1700
+ parameters: {
1701
+ type: "object",
1702
+ properties: {
1703
+ sessionId: { type: "string", description: "Session ID to close" }
1704
+ },
1705
+ required: ["sessionId"]
1706
+ },
1707
+ execute: async (args) => {
1708
+ const sessionId = args.sessionId;
1709
+ closeSession(sessionId);
1710
+ return `Session ${sessionId} closed.`;
1711
+ }
1712
+ }
1713
+ );
1714
+ }
1715
+ var COMPONENT13;
1716
+ var init_sessions = __esm({
1717
+ "src/skills/builtin/sessions.ts"() {
1718
+ init_registry();
1719
+ init_session();
1720
+ init_agent();
1721
+ init_logger();
1722
+ COMPONENT13 = "Sessions";
1723
+ }
1724
+ });
1725
+ init_sessions();
1726
+ export {
1727
+ registerSessionsSkill
1728
+ };
1729
+ //# sourceMappingURL=sessions.js.map