augure 0.8.0 → 0.9.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.
@@ -0,0 +1,2074 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ToolRegistry,
4
+ createBrowserTool,
5
+ datetimeTool,
6
+ emailTool,
7
+ githubTool,
8
+ httpTool,
9
+ memoryReadTool,
10
+ memoryWriteTool,
11
+ opencodeTool,
12
+ sandboxExecTool,
13
+ scheduleTool,
14
+ webSearchTool
15
+ } from "./chunk-63TMJ6FS.js";
16
+ import {
17
+ BrowserSessionManager
18
+ } from "./chunk-H6PBSIJ5.js";
19
+ import {
20
+ DockerContainerPool,
21
+ ensureImage
22
+ } from "./chunk-TP6A6GFU.js";
23
+ import {
24
+ FileMemoryStore,
25
+ MemoryIngester,
26
+ MemoryRetriever
27
+ } from "./chunk-M63XRSRV.js";
28
+ import {
29
+ CronScheduler,
30
+ Heartbeat,
31
+ JobStore,
32
+ parseInterval
33
+ } from "./chunk-3BGYMW62.js";
34
+ import {
35
+ SkillGenerator,
36
+ SkillHealer,
37
+ SkillHub,
38
+ SkillManager,
39
+ SkillRunner,
40
+ SkillSchedulerBridge,
41
+ SkillTester,
42
+ SkillUpdater,
43
+ createSkillTools,
44
+ installBuiltins
45
+ } from "./chunk-XOMIXLMX.js";
46
+ import {
47
+ noopLogger
48
+ } from "./chunk-62OPINAX.js";
49
+
50
+ // ../core/dist/config.js
51
+ import { readFile } from "fs/promises";
52
+ import JSON5 from "json5";
53
+ import { z } from "zod";
54
+ var LLMModelConfigSchema = z.object({
55
+ provider: z.enum(["openrouter", "anthropic", "openai"]),
56
+ apiKey: z.string().min(1),
57
+ model: z.string().min(1),
58
+ maxTokens: z.number().int().positive()
59
+ });
60
+ var LLMConfigSchema = z.object({
61
+ default: LLMModelConfigSchema,
62
+ reasoning: LLMModelConfigSchema.partial().optional(),
63
+ ingestion: LLMModelConfigSchema.partial().optional(),
64
+ monitoring: LLMModelConfigSchema.partial().optional(),
65
+ coding: LLMModelConfigSchema.partial().optional()
66
+ });
67
+ var AppConfigSchema = z.object({
68
+ identity: z.object({
69
+ name: z.string().min(1),
70
+ personality: z.string().min(1)
71
+ }),
72
+ llm: LLMConfigSchema,
73
+ channels: z.object({
74
+ telegram: z.object({
75
+ enabled: z.boolean(),
76
+ botToken: z.string(),
77
+ allowedUsers: z.array(z.number()),
78
+ rejectMessage: z.string().optional()
79
+ }).optional(),
80
+ whatsapp: z.object({ enabled: z.boolean() }).optional(),
81
+ web: z.object({ enabled: z.boolean(), port: z.number() }).optional()
82
+ }),
83
+ memory: z.object({
84
+ path: z.string().min(1),
85
+ autoIngest: z.boolean(),
86
+ maxRetrievalTokens: z.number().int().positive()
87
+ }),
88
+ scheduler: z.object({
89
+ heartbeatInterval: z.string().min(1),
90
+ jobs: z.array(z.object({
91
+ id: z.string().min(1),
92
+ cron: z.string().min(1).optional(),
93
+ runAt: z.string().min(1).optional(),
94
+ prompt: z.string().min(1),
95
+ channel: z.string().min(1)
96
+ }).refine((j) => j.cron || j.runAt, {
97
+ message: "Job must have either cron or runAt"
98
+ }))
99
+ }),
100
+ sandbox: z.object({
101
+ runtime: z.literal("docker"),
102
+ image: z.string().min(1).optional(),
103
+ defaults: z.object({
104
+ timeout: z.number().int().positive(),
105
+ memoryLimit: z.string().min(1),
106
+ cpuLimit: z.string().min(1)
107
+ }),
108
+ codeAgent: z.object({
109
+ command: z.string().min(1),
110
+ args: z.array(z.string()).optional(),
111
+ env: z.record(z.string(), z.string()).optional()
112
+ }).optional()
113
+ }),
114
+ tools: z.object({
115
+ webSearch: z.object({
116
+ provider: z.enum(["tavily", "exa", "searxng"]),
117
+ apiKey: z.string().optional(),
118
+ baseUrl: z.string().optional(),
119
+ maxResults: z.number().int().positive().optional()
120
+ }).optional(),
121
+ http: z.object({
122
+ defaultHeaders: z.record(z.string(), z.string()).optional(),
123
+ presets: z.record(z.string(), z.object({
124
+ baseUrl: z.string(),
125
+ headers: z.record(z.string(), z.string())
126
+ })).optional(),
127
+ timeoutMs: z.number().int().positive().optional(),
128
+ maxResponseBytes: z.number().int().positive().optional()
129
+ }).optional(),
130
+ email: z.object({
131
+ imap: z.object({
132
+ host: z.string(),
133
+ port: z.number(),
134
+ user: z.string(),
135
+ password: z.string()
136
+ }),
137
+ smtp: z.object({
138
+ host: z.string(),
139
+ port: z.number(),
140
+ user: z.string(),
141
+ password: z.string()
142
+ })
143
+ }).optional(),
144
+ github: z.object({ token: z.string() }).optional(),
145
+ browser: z.object({
146
+ provider: z.enum(["local", "browserbase"]),
147
+ browserbase: z.object({
148
+ apiKey: z.string().min(1),
149
+ projectId: z.string().optional()
150
+ }).optional(),
151
+ defaults: z.object({
152
+ timeout: z.number().int().positive().optional(),
153
+ headless: z.boolean().optional(),
154
+ viewport: z.object({
155
+ width: z.number().int().positive(),
156
+ height: z.number().int().positive()
157
+ }).optional()
158
+ }).optional()
159
+ }).optional()
160
+ }),
161
+ security: z.object({
162
+ sandboxOnly: z.boolean(),
163
+ allowedHosts: z.array(z.string()),
164
+ maxConcurrentSandboxes: z.number().int().positive()
165
+ }),
166
+ skills: z.object({
167
+ path: z.string().min(1).default("./skills"),
168
+ maxFailures: z.number().int().positive().default(3),
169
+ autoSuggest: z.boolean().default(true),
170
+ hub: z.object({
171
+ repo: z.string().min(1),
172
+ branch: z.string().min(1).default("main")
173
+ }).optional()
174
+ }).optional(),
175
+ audit: z.object({
176
+ path: z.string().min(1).default("./logs"),
177
+ enabled: z.boolean().default(true)
178
+ }).optional(),
179
+ persona: z.object({
180
+ path: z.string().min(1).default("./config/personas")
181
+ }).optional(),
182
+ updates: z.object({
183
+ skills: z.object({
184
+ enabled: z.boolean().default(true),
185
+ checkInterval: z.string().min(1).default("6h")
186
+ }).optional(),
187
+ cli: z.object({
188
+ enabled: z.boolean().default(true),
189
+ checkInterval: z.string().min(1).default("24h"),
190
+ notifyChannel: z.string().min(1).default("telegram")
191
+ }).optional()
192
+ }).optional(),
193
+ codeMode: z.object({
194
+ runtime: z.enum(["vm", "docker", "auto"]).default("auto"),
195
+ timeout: z.number().int().positive().default(30),
196
+ memoryLimit: z.number().int().positive().default(128)
197
+ }).optional(),
198
+ approval: z.object({
199
+ enabled: z.boolean(),
200
+ timeoutMs: z.number().int().positive().default(12e4)
201
+ }).optional(),
202
+ mcp: z.object({
203
+ enabled: z.boolean(),
204
+ port: z.number().int().positive().default(3100)
205
+ }).optional()
206
+ });
207
+ function interpolateEnvVars(raw) {
208
+ return raw.replace(/\$\{([^}]+)\}/g, (match, varName) => {
209
+ const value = process.env[varName];
210
+ if (value === void 0) {
211
+ throw new Error(`Missing environment variable: ${varName}`);
212
+ }
213
+ return value;
214
+ });
215
+ }
216
+ async function loadConfig(path) {
217
+ const raw = await readFile(path, "utf-8");
218
+ const interpolated = interpolateEnvVars(raw);
219
+ const parsed = JSON5.parse(interpolated);
220
+ return AppConfigSchema.parse(parsed);
221
+ }
222
+
223
+ // ../core/dist/llm.js
224
+ var OpenRouterClient = class {
225
+ apiKey;
226
+ model;
227
+ maxTokens;
228
+ baseUrl;
229
+ log;
230
+ constructor(config) {
231
+ this.apiKey = config.apiKey;
232
+ this.model = config.model;
233
+ this.maxTokens = config.maxTokens;
234
+ this.baseUrl = config.baseUrl ?? "https://openrouter.ai/api/v1";
235
+ this.log = config.logger ?? noopLogger;
236
+ }
237
+ async chat(messages, tools) {
238
+ this.log.debug(`Request: model=${this.model} messages=${messages.length} tools=${tools?.length ?? 0}`);
239
+ const fetchStart = Date.now();
240
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
241
+ method: "POST",
242
+ headers: {
243
+ "Content-Type": "application/json",
244
+ Authorization: `Bearer ${this.apiKey}`
245
+ },
246
+ body: JSON.stringify({
247
+ model: this.model,
248
+ max_tokens: this.maxTokens,
249
+ messages: messages.map((m) => ({
250
+ role: m.role,
251
+ content: m.content,
252
+ ...m.toolCallId ? { tool_call_id: m.toolCallId } : {},
253
+ ...m.toolCalls?.length ? {
254
+ tool_calls: m.toolCalls.map((tc) => ({
255
+ id: tc.id,
256
+ type: "function",
257
+ function: {
258
+ name: tc.name,
259
+ arguments: JSON.stringify(tc.arguments)
260
+ }
261
+ }))
262
+ } : {}
263
+ })),
264
+ ...tools?.length ? { tools } : {}
265
+ })
266
+ });
267
+ if (!response.ok) {
268
+ const body = await response.text();
269
+ throw new Error(`OpenRouter API error ${response.status}: ${body}`);
270
+ }
271
+ const data = await response.json();
272
+ const choice = data.choices[0];
273
+ this.log.debug(`Response: ${response.status} ${data.usage.prompt_tokens}+${data.usage.completion_tokens} tokens in ${Date.now() - fetchStart}ms`);
274
+ return {
275
+ content: choice.message.content ?? "",
276
+ toolCalls: (choice.message.tool_calls ?? []).map((tc) => {
277
+ let args = {};
278
+ try {
279
+ args = tc.function.arguments ? JSON.parse(tc.function.arguments) : {};
280
+ } catch {
281
+ this.log.warn(`Failed to parse tool call arguments for ${tc.function.name}`);
282
+ }
283
+ return { id: tc.id, name: tc.function.name, arguments: args };
284
+ }),
285
+ usage: {
286
+ inputTokens: data.usage.prompt_tokens,
287
+ outputTokens: data.usage.completion_tokens
288
+ }
289
+ };
290
+ }
291
+ };
292
+
293
+ // ../core/dist/context.js
294
+ function assembleContext(input) {
295
+ const { systemPrompt, memoryContent, conversationHistory, persona } = input;
296
+ let system = systemPrompt;
297
+ const now = /* @__PURE__ */ new Date();
298
+ const humanDate = new Intl.DateTimeFormat("en-US", {
299
+ weekday: "long",
300
+ year: "numeric",
301
+ month: "long",
302
+ day: "numeric",
303
+ hour: "2-digit",
304
+ minute: "2-digit",
305
+ timeZoneName: "short"
306
+ }).format(now);
307
+ system += `
308
+
309
+ Current date and time: ${now.toISOString()} (${humanDate})`;
310
+ if (persona) {
311
+ system += `
312
+
313
+ ## Active Persona
314
+ ${persona}`;
315
+ }
316
+ if (memoryContent) {
317
+ system += `
318
+
319
+ ## Memory
320
+ ${memoryContent}`;
321
+ }
322
+ const messages = [{ role: "system", content: system }];
323
+ messages.push(...conversationHistory);
324
+ return messages;
325
+ }
326
+
327
+ // ../code-mode/dist/typegen.js
328
+ var JSON_TO_TS = {
329
+ string: "string",
330
+ number: "number",
331
+ integer: "number",
332
+ boolean: "boolean",
333
+ array: "unknown[]",
334
+ object: "Record<string, unknown>"
335
+ };
336
+ function sanitizeName(name) {
337
+ return name.replace(/[-. ]/g, "_");
338
+ }
339
+ function toPascalCase(name) {
340
+ return sanitizeName(name).split("_").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
341
+ }
342
+ function mapType(schema) {
343
+ const t = schema.type;
344
+ if (schema.enum) {
345
+ return schema.enum.map((v) => `"${v}"`).join(" | ");
346
+ }
347
+ return JSON_TO_TS[t ?? "string"] ?? "unknown";
348
+ }
349
+ function generateDeclarations(registry) {
350
+ const tools = registry.list();
351
+ const blocks = [];
352
+ const apiEntries = [];
353
+ for (const tool of tools) {
354
+ const safeName = sanitizeName(tool.name);
355
+ const interfaceName = `${toPascalCase(tool.name)}Input`;
356
+ const params = tool.parameters;
357
+ const properties = params.properties ?? {};
358
+ const required = new Set(params.required ?? []);
359
+ const fields = [];
360
+ for (const [key, schema] of Object.entries(properties)) {
361
+ const optional = required.has(key) ? "" : "?";
362
+ const tsType = mapType(schema);
363
+ const desc = schema.description;
364
+ if (desc) {
365
+ fields.push(` /** ${desc} */
366
+ ${key}${optional}: ${tsType};`);
367
+ } else {
368
+ fields.push(` ${key}${optional}: ${tsType};`);
369
+ }
370
+ }
371
+ blocks.push(`interface ${interfaceName} {
372
+ ${fields.join("\n")}
373
+ }`);
374
+ apiEntries.push(` /** ${tool.description} */
375
+ ${safeName}: (input: ${interfaceName}) => Promise<{ success: boolean; output: string }>;`);
376
+ }
377
+ const apiBlock = `declare const api: {
378
+ ${apiEntries.join("\n")}
379
+ };`;
380
+ return [...blocks, "", apiBlock].join("\n");
381
+ }
382
+
383
+ // ../code-mode/dist/bridge.js
384
+ function createBridgeHandler(registry) {
385
+ return async (toolName, input) => {
386
+ try {
387
+ return await registry.execute(toolName, input);
388
+ } catch (err) {
389
+ return {
390
+ success: false,
391
+ output: `Bridge error calling ${toolName}: ${err instanceof Error ? err.message : String(err)}`
392
+ };
393
+ }
394
+ };
395
+ }
396
+ function generateHarnessCode(userCode) {
397
+ return `
398
+ const __logs = [];
399
+ const __originalLog = console.log;
400
+ const __originalWarn = console.warn;
401
+ const __originalError = console.error;
402
+ console.log = (...args) => __logs.push(args.map(String).join(" "));
403
+ console.warn = (...args) => __logs.push("[warn] " + args.map(String).join(" "));
404
+ console.error = (...args) => __logs.push("[error] " + args.map(String).join(" "));
405
+
406
+ let __toolCalls = 0;
407
+
408
+ const api = new Proxy({}, {
409
+ get: (_target, toolName) => {
410
+ return async (input) => {
411
+ __toolCalls++;
412
+ return await __bridge(String(toolName), input);
413
+ };
414
+ }
415
+ });
416
+
417
+ async function __run() {
418
+ ${userCode}
419
+ }
420
+
421
+ try {
422
+ const __result = await __run();
423
+ __originalLog(JSON.stringify({
424
+ success: true,
425
+ output: __result,
426
+ logs: __logs,
427
+ toolCalls: __toolCalls,
428
+ }));
429
+ } catch (err) {
430
+ __originalLog(JSON.stringify({
431
+ success: false,
432
+ error: err.message ?? String(err),
433
+ logs: __logs,
434
+ toolCalls: __toolCalls,
435
+ }));
436
+ }
437
+ `;
438
+ }
439
+
440
+ // ../code-mode/dist/vm-sandbox.js
441
+ import { createContext, runInContext } from "vm";
442
+ import { transform } from "esbuild";
443
+ var VmExecutor = class {
444
+ registry;
445
+ config;
446
+ constructor(registry, config) {
447
+ this.registry = registry;
448
+ this.config = config;
449
+ }
450
+ async execute(code) {
451
+ const start = performance.now();
452
+ try {
453
+ const harnessTs = generateHarnessCode(code);
454
+ const { code: harnessJs } = await transform(harnessTs, {
455
+ loader: "ts",
456
+ target: "es2024"
457
+ });
458
+ const bridgeHandler = createBridgeHandler(this.registry);
459
+ const consoleLogs = [];
460
+ const captureConsole = {
461
+ log: (...args) => consoleLogs.push(args.map(String).join(" ")),
462
+ warn: (...args) => consoleLogs.push("[warn] " + args.map(String).join(" ")),
463
+ error: (...args) => consoleLogs.push("[error] " + args.map(String).join(" "))
464
+ };
465
+ const context = createContext({
466
+ console: captureConsole,
467
+ __bridge: bridgeHandler,
468
+ JSON,
469
+ String,
470
+ Number,
471
+ Boolean,
472
+ Array,
473
+ Object,
474
+ Error,
475
+ Promise,
476
+ Map,
477
+ Set,
478
+ parseInt,
479
+ parseFloat,
480
+ isNaN,
481
+ isFinite,
482
+ setTimeout,
483
+ Date,
484
+ RegExp,
485
+ Math,
486
+ Symbol,
487
+ Uint8Array,
488
+ TextEncoder,
489
+ TextDecoder,
490
+ Buffer,
491
+ URL,
492
+ URLSearchParams
493
+ });
494
+ const wrappedCode = `(async () => { ${harnessJs} })()`;
495
+ const timeoutPromise = new Promise((_, reject) => {
496
+ const timer = setTimeout(() => reject(new Error("Timeout: code execution exceeded time limit")), this.config.timeout);
497
+ timer.unref?.();
498
+ });
499
+ const resultPromise = runInContext(wrappedCode, context, {
500
+ timeout: this.config.timeout
501
+ });
502
+ await Promise.race([resultPromise, timeoutPromise]);
503
+ const durationMs = performance.now() - start;
504
+ const lastLine = consoleLogs[consoleLogs.length - 1];
505
+ if (!lastLine) {
506
+ return {
507
+ success: false,
508
+ output: void 0,
509
+ logs: consoleLogs,
510
+ error: "No output produced by code execution",
511
+ durationMs,
512
+ toolCalls: 0
513
+ };
514
+ }
515
+ const parsed = JSON.parse(lastLine);
516
+ return {
517
+ success: parsed.success,
518
+ output: parsed.output,
519
+ logs: parsed.logs,
520
+ error: parsed.error,
521
+ durationMs,
522
+ toolCalls: parsed.toolCalls
523
+ };
524
+ } catch (err) {
525
+ const durationMs = performance.now() - start;
526
+ return {
527
+ success: false,
528
+ output: void 0,
529
+ logs: [],
530
+ error: err instanceof Error ? err.message : String(err),
531
+ durationMs,
532
+ toolCalls: 0
533
+ };
534
+ }
535
+ }
536
+ };
537
+
538
+ // ../code-mode/dist/docker-sandbox.js
539
+ var DOCKER_HARNESS = `
540
+ import { readFile, writeFile, unlink } from "node:fs/promises";
541
+
542
+ const __logs = [];
543
+ const __originalLog = console.log;
544
+ console.log = (...args) => __logs.push(args.map(String).join(" "));
545
+ console.warn = (...args) => __logs.push("[warn] " + args.map(String).join(" "));
546
+ console.error = (...args) => __logs.push("[error] " + args.map(String).join(" "));
547
+
548
+ let __toolCalls = 0;
549
+ let __reqId = 0;
550
+
551
+ const __BRIDGE_TIMEOUT = 120_000;
552
+ const api = new Proxy({}, {
553
+ get: (_target, toolName) => {
554
+ return async (args) => {
555
+ __toolCalls++;
556
+ const id = String(++__reqId);
557
+ const reqPath = \`/workspace/.bridge-req-\${id}.json\`;
558
+ const respPath = \`/workspace/.bridge-resp-\${id}.json\`;
559
+ await writeFile(reqPath, JSON.stringify({ id, tool: String(toolName), args }));
560
+ const deadline = Date.now() + __BRIDGE_TIMEOUT;
561
+ while (Date.now() < deadline) {
562
+ try {
563
+ const data = await readFile(respPath, "utf-8");
564
+ await unlink(respPath);
565
+ return JSON.parse(data);
566
+ } catch {
567
+ await new Promise(r => setTimeout(r, 50));
568
+ }
569
+ }
570
+ throw new Error(\`Bridge timeout: tool "\${String(toolName)}" did not respond within \${__BRIDGE_TIMEOUT}ms\`);
571
+ };
572
+ }
573
+ });
574
+
575
+ const __userCode = await readFile("/workspace/user-code.js", "utf-8");
576
+ const __fn = new Function("api", "__logs",
577
+ "return (async () => { " + __userCode + " })();"
578
+ );
579
+
580
+ try {
581
+ const __result = await __fn(api, __logs);
582
+ __originalLog(JSON.stringify({
583
+ success: true,
584
+ output: __result,
585
+ logs: __logs,
586
+ toolCalls: __toolCalls,
587
+ }));
588
+ } catch (err) {
589
+ __originalLog(JSON.stringify({
590
+ success: false,
591
+ error: err.message ?? String(err),
592
+ logs: __logs,
593
+ toolCalls: __toolCalls,
594
+ }));
595
+ }
596
+ `;
597
+ function sleep(ms) {
598
+ return new Promise((r) => setTimeout(r, ms));
599
+ }
600
+ function parseFiles(stdout) {
601
+ return stdout.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("/workspace/.bridge-req-") && l.endsWith(".json"));
602
+ }
603
+ var DockerExecutor = class {
604
+ config;
605
+ constructor(config) {
606
+ this.config = config;
607
+ }
608
+ async pollBridge(container, abortSignal) {
609
+ while (!abortSignal.aborted) {
610
+ try {
611
+ const ls = await container.exec("ls /workspace/.bridge-req-*.json 2>/dev/null || true");
612
+ const files = parseFiles(ls.stdout);
613
+ for (const reqFile of files) {
614
+ const reqJson = await container.exec(`cat ${reqFile}`);
615
+ const req = JSON.parse(reqJson.stdout);
616
+ const result = await this.config.registry.execute(req.tool, req.args);
617
+ const respFile = reqFile.replace("bridge-req", "bridge-resp");
618
+ const respB64 = Buffer.from(JSON.stringify(result)).toString("base64");
619
+ const tmpFile = `${respFile}.tmp`;
620
+ await container.exec(`sh -c 'echo "${respB64}" | base64 -d > ${tmpFile} && mv ${tmpFile} ${respFile}'`);
621
+ await container.exec(`rm ${reqFile}`);
622
+ }
623
+ } catch {
624
+ }
625
+ await sleep(100);
626
+ }
627
+ }
628
+ async execute(code) {
629
+ const start = Date.now();
630
+ let container;
631
+ try {
632
+ container = await this.config.pool.acquire({
633
+ trust: "sandboxed",
634
+ timeout: this.config.timeout,
635
+ memory: this.config.memoryLimit,
636
+ cpu: this.config.cpuLimit
637
+ });
638
+ } catch (err) {
639
+ return {
640
+ success: false,
641
+ output: void 0,
642
+ logs: [],
643
+ toolCalls: 0,
644
+ error: `Failed to acquire container: ${err instanceof Error ? err.message : String(err)}`,
645
+ durationMs: Date.now() - start
646
+ };
647
+ }
648
+ try {
649
+ await container.exec("mkdir -p /workspace");
650
+ const codeB64 = Buffer.from(code).toString("base64");
651
+ await container.exec(`sh -c 'echo "${codeB64}" | base64 -d > /workspace/user-code.js'`);
652
+ const harnessB64 = Buffer.from(DOCKER_HARNESS).toString("base64");
653
+ await container.exec(`sh -c 'echo "${harnessB64}" | base64 -d > /workspace/harness.ts'`);
654
+ const abortController = new AbortController();
655
+ const bridgePromise = this.pollBridge(container, abortController.signal);
656
+ const execResult = await container.exec("npx tsx /workspace/harness.ts", {
657
+ timeout: this.config.timeout,
658
+ cwd: "/workspace"
659
+ });
660
+ abortController.abort();
661
+ await bridgePromise;
662
+ if (execResult.exitCode === 0 && execResult.stdout.trim()) {
663
+ try {
664
+ const lastLine = execResult.stdout.trim().split("\n").pop();
665
+ const parsed = JSON.parse(lastLine);
666
+ return {
667
+ success: parsed.success,
668
+ output: parsed.output,
669
+ logs: parsed.logs ?? [],
670
+ error: parsed.error,
671
+ durationMs: Date.now() - start,
672
+ toolCalls: parsed.toolCalls ?? 0
673
+ };
674
+ } catch {
675
+ return {
676
+ success: true,
677
+ output: execResult.stdout.trim(),
678
+ logs: [],
679
+ durationMs: Date.now() - start,
680
+ toolCalls: 0
681
+ };
682
+ }
683
+ }
684
+ return {
685
+ success: false,
686
+ output: void 0,
687
+ logs: [],
688
+ toolCalls: 0,
689
+ error: execResult.stderr || execResult.stdout || "Unknown error",
690
+ durationMs: Date.now() - start
691
+ };
692
+ } catch (err) {
693
+ return {
694
+ success: false,
695
+ output: void 0,
696
+ logs: [],
697
+ toolCalls: 0,
698
+ error: err instanceof Error ? err.message : String(err),
699
+ durationMs: Date.now() - start
700
+ };
701
+ } finally {
702
+ await this.config.pool.release(container);
703
+ }
704
+ }
705
+ };
706
+
707
+ // ../code-mode/dist/tool.js
708
+ function createCodeModeTool(registry, executor) {
709
+ const declarations = generateDeclarations(registry);
710
+ return {
711
+ name: "execute_code",
712
+ description: `Execute TypeScript code with access to the agent's APIs. Write the body of an async function.
713
+
714
+ Available APIs:
715
+
716
+ \`\`\`typescript
717
+ ${declarations}
718
+ \`\`\`
719
+
720
+ Each API call returns { success: boolean, output: string }.
721
+ Use console.log() for intermediate output. Return your final result.`,
722
+ parameters: {
723
+ type: "object",
724
+ properties: {
725
+ code: {
726
+ type: "string",
727
+ description: "The body of an async TypeScript function. Use the 'api' object to call tools."
728
+ }
729
+ },
730
+ required: ["code"]
731
+ },
732
+ execute: async (params) => {
733
+ const { code } = params;
734
+ const result = await executor.execute(code);
735
+ if (result.success) {
736
+ const parts = [];
737
+ if (result.logs.length > 0) {
738
+ parts.push(`[logs]
739
+ ${result.logs.join("\n")}`);
740
+ }
741
+ parts.push(typeof result.output === "string" ? result.output : JSON.stringify(result.output));
742
+ return { success: true, output: parts.join("\n\n") };
743
+ }
744
+ return {
745
+ success: false,
746
+ output: result.error ?? "Code execution failed"
747
+ };
748
+ }
749
+ };
750
+ }
751
+
752
+ // ../code-mode/dist/auto-executor.js
753
+ var AutoExecutor = class {
754
+ primary;
755
+ fallback;
756
+ constructor(primary, fallback) {
757
+ this.primary = primary;
758
+ this.fallback = fallback;
759
+ }
760
+ async execute(code) {
761
+ try {
762
+ const result = await this.primary.execute(code);
763
+ return result;
764
+ } catch {
765
+ return this.fallback.execute(code);
766
+ }
767
+ }
768
+ };
769
+
770
+ // ../core/dist/audit.js
771
+ import { appendFile, mkdir } from "fs/promises";
772
+ import { join } from "path";
773
+ function summarize(text, maxLen = 200) {
774
+ if (text.length <= maxLen)
775
+ return text;
776
+ return text.slice(0, maxLen) + "...";
777
+ }
778
+ var FileAuditLogger = class {
779
+ basePath;
780
+ logger;
781
+ pendingWrite = Promise.resolve();
782
+ initialized = false;
783
+ constructor(basePath, logger) {
784
+ this.basePath = basePath;
785
+ this.logger = logger ?? noopLogger;
786
+ }
787
+ log(entry) {
788
+ this.pendingWrite = this.pendingWrite.then(() => this.writeEntry(entry)).catch((err) => this.logger.error("Audit write error:", err));
789
+ }
790
+ async close() {
791
+ await this.pendingWrite;
792
+ }
793
+ async writeEntry(entry) {
794
+ const dir = join(this.basePath, "actions");
795
+ if (!this.initialized) {
796
+ await mkdir(dir, { recursive: true });
797
+ this.initialized = true;
798
+ }
799
+ const date = entry.ts.slice(0, 10);
800
+ const filePath = join(dir, `${date}.jsonl`);
801
+ await appendFile(filePath, JSON.stringify(entry) + "\n");
802
+ }
803
+ };
804
+ var NullAuditLogger = class {
805
+ log(_entry) {
806
+ }
807
+ async close() {
808
+ }
809
+ };
810
+
811
+ // ../core/dist/agent.js
812
+ var Agent = class {
813
+ config;
814
+ log;
815
+ conversations = /* @__PURE__ */ new Map();
816
+ state = "running";
817
+ constructor(config) {
818
+ this.config = config;
819
+ this.log = config.logger ?? noopLogger;
820
+ }
821
+ getState() {
822
+ return this.state;
823
+ }
824
+ setState(s) {
825
+ this.state = s;
826
+ }
827
+ setPersona(text) {
828
+ this.config.persona = text;
829
+ }
830
+ async handleMessage(incoming) {
831
+ if (this.state === "killed") {
832
+ return "Agent is in emergency stop mode. Send /resume to reactivate.";
833
+ }
834
+ const start = Date.now();
835
+ const userId = incoming.userId;
836
+ let history = this.conversations.get(userId) ?? [];
837
+ history.push({
838
+ role: "user",
839
+ content: incoming.text
840
+ });
841
+ if (this.config.guard) {
842
+ history = this.config.guard.compact(history);
843
+ }
844
+ this.conversations.set(userId, history);
845
+ let memoryContent = this.config.memoryContent;
846
+ if (this.config.retriever) {
847
+ memoryContent = await this.config.retriever.retrieve();
848
+ }
849
+ const maxLoops = this.config.maxToolLoops ?? 10;
850
+ let loopCount = 0;
851
+ let totalInputTokens = 0;
852
+ let totalOutputTokens = 0;
853
+ let totalToolCalls = 0;
854
+ const toolSchemas = this.config.tools.toFunctionSchemas();
855
+ let effectiveSchemas = toolSchemas;
856
+ let codeModeTool;
857
+ if (this.config.codeModeExecutor) {
858
+ codeModeTool = createCodeModeTool(this.config.tools, this.config.codeModeExecutor);
859
+ effectiveSchemas = [{
860
+ type: "function",
861
+ function: {
862
+ name: codeModeTool.name,
863
+ description: codeModeTool.description,
864
+ parameters: codeModeTool.parameters
865
+ }
866
+ }];
867
+ }
868
+ while (loopCount < maxLoops) {
869
+ const messages = assembleContext({
870
+ systemPrompt: this.config.systemPrompt,
871
+ memoryContent,
872
+ conversationHistory: history,
873
+ persona: this.config.persona
874
+ });
875
+ this.log.debug(`LLM call #${loopCount + 1} (${messages.length} messages)`);
876
+ const response = await this.config.llm.chat(messages, effectiveSchemas);
877
+ totalInputTokens += response.usage.inputTokens;
878
+ totalOutputTokens += response.usage.outputTokens;
879
+ if (response.toolCalls.length === 0) {
880
+ history.push({
881
+ role: "assistant",
882
+ content: response.content
883
+ });
884
+ this.log.debug(`Response: ${response.usage.inputTokens}+${response.usage.outputTokens} tokens, ${Date.now() - start}ms`);
885
+ if (this.config.audit) {
886
+ this.config.audit.log({
887
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
888
+ trigger: incoming.channelType === "system" ? "heartbeat" : "user",
889
+ action: "chat",
890
+ inputSummary: summarize(incoming.text),
891
+ outputSummary: summarize(response.content),
892
+ tokens: {
893
+ input: response.usage.inputTokens,
894
+ output: response.usage.outputTokens,
895
+ model: this.config.modelName ?? ""
896
+ },
897
+ durationMs: Date.now() - start,
898
+ success: true
899
+ });
900
+ }
901
+ this.log.info(`\u2500\u2500 ${totalInputTokens}+${totalOutputTokens} tokens | ${loopCount + 1} LLM calls | ${totalToolCalls} tool calls | ${Date.now() - start}ms`);
902
+ if (this.config.ingester) {
903
+ this.config.ingester.ingest(history).catch((err) => this.log.error("Ingestion error:", err));
904
+ }
905
+ return response.content;
906
+ }
907
+ history.push({
908
+ role: "assistant",
909
+ content: response.content || "",
910
+ toolCalls: response.toolCalls
911
+ });
912
+ totalToolCalls += response.toolCalls.length;
913
+ for (const toolCall of response.toolCalls) {
914
+ const toolStart = Date.now();
915
+ this.log.debug(`Tool: ${toolCall.name}`);
916
+ const registeredTool = this.config.tools.get(toolCall.name);
917
+ if (registeredTool?.riskLevel === "high" && this.config.approvalGate) {
918
+ const approved = await this.config.approvalGate.request(incoming.userId, toolCall.name, toolCall.arguments);
919
+ if (!approved) {
920
+ history.push({
921
+ role: "tool",
922
+ content: "Tool call rejected by user.",
923
+ toolCallId: toolCall.id
924
+ });
925
+ continue;
926
+ }
927
+ }
928
+ let result;
929
+ if (codeModeTool && toolCall.name === "execute_code") {
930
+ result = await codeModeTool.execute(toolCall.arguments, {});
931
+ } else {
932
+ result = await this.config.tools.execute(toolCall.name, toolCall.arguments);
933
+ }
934
+ this.log.debug(`Tool ${toolCall.name}: ${result.success ? "ok" : "fail"} (${Date.now() - toolStart}ms)`);
935
+ history.push({
936
+ role: "tool",
937
+ content: result.output,
938
+ toolCallId: toolCall.id
939
+ });
940
+ if (this.config.audit) {
941
+ this.config.audit.log({
942
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
943
+ trigger: incoming.channelType === "system" ? "heartbeat" : "user",
944
+ action: toolCall.name,
945
+ inputSummary: summarize(JSON.stringify(toolCall.arguments)),
946
+ outputSummary: summarize(result.output),
947
+ durationMs: Date.now() - toolStart,
948
+ success: result.success,
949
+ error: result.success ? void 0 : result.output
950
+ });
951
+ }
952
+ }
953
+ loopCount++;
954
+ }
955
+ return "Max tool call loops reached. Please try again.";
956
+ }
957
+ getConversationHistory(userId) {
958
+ if (userId) {
959
+ return [...this.conversations.get(userId) ?? []];
960
+ }
961
+ const all = [];
962
+ for (const msgs of this.conversations.values()) {
963
+ all.push(...msgs);
964
+ }
965
+ return all;
966
+ }
967
+ clearHistory(userId) {
968
+ if (userId) {
969
+ this.conversations.delete(userId);
970
+ } else {
971
+ this.conversations.clear();
972
+ }
973
+ }
974
+ };
975
+
976
+ // ../core/dist/commands.js
977
+ async function handleCommand(text, ctx) {
978
+ const trimmed = text.trim();
979
+ if (!trimmed.startsWith("/")) {
980
+ return { handled: false };
981
+ }
982
+ const parts = trimmed.slice(1).split(/\s+/);
983
+ const command = parts[0]?.toLowerCase();
984
+ const arg = parts[1];
985
+ switch (command) {
986
+ case "pause": {
987
+ if (arg) {
988
+ if (!ctx.skillManager) {
989
+ return { handled: true, response: "Skills system is not configured." };
990
+ }
991
+ await ctx.skillManager.updateStatus(arg, "paused");
992
+ return { handled: true, response: `Skill "${arg}" paused.` };
993
+ }
994
+ ctx.scheduler.stop();
995
+ ctx.agent.setState("paused");
996
+ return { handled: true, response: "Agent paused. Scheduler stopped. Direct messages still accepted." };
997
+ }
998
+ case "resume": {
999
+ ctx.scheduler.start();
1000
+ ctx.agent.setState("running");
1001
+ return { handled: true, response: "Agent resumed. Scheduler restarted." };
1002
+ }
1003
+ case "kill": {
1004
+ ctx.scheduler.stop();
1005
+ await ctx.pool.destroyAll();
1006
+ ctx.agent.setState("killed");
1007
+ return { handled: true, response: "Emergency stop. All containers destroyed. Agent in read-only mode." };
1008
+ }
1009
+ case "status": {
1010
+ const state = ctx.agent.getState();
1011
+ return { handled: true, response: `Agent state: ${state}` };
1012
+ }
1013
+ default:
1014
+ return { handled: false };
1015
+ }
1016
+ }
1017
+
1018
+ // ../core/dist/persona.js
1019
+ import { readdir, readFile as readFile2 } from "fs/promises";
1020
+ import { join as join2 } from "path";
1021
+ import matter from "gray-matter";
1022
+ var PersonaResolver = class {
1023
+ personaDir;
1024
+ personas = [];
1025
+ constructor(personaDir) {
1026
+ this.personaDir = personaDir;
1027
+ }
1028
+ async loadAll() {
1029
+ this.personas = [];
1030
+ let entries;
1031
+ try {
1032
+ entries = await readdir(this.personaDir);
1033
+ } catch {
1034
+ return;
1035
+ }
1036
+ for (const entry of entries) {
1037
+ if (!entry.endsWith(".md"))
1038
+ continue;
1039
+ const content = await readFile2(join2(this.personaDir, entry), "utf-8");
1040
+ const { data, content: body } = matter(content);
1041
+ const meta = {
1042
+ id: data.id ?? entry.replace(".md", ""),
1043
+ name: data.name ?? entry.replace(".md", ""),
1044
+ triggers: data.triggers,
1045
+ priority: data.priority ?? 0
1046
+ };
1047
+ this.personas.push({ meta, body: body.trim() });
1048
+ }
1049
+ this.personas.sort((a, b) => a.meta.id.localeCompare(b.meta.id));
1050
+ }
1051
+ listAll() {
1052
+ return [...this.personas];
1053
+ }
1054
+ resolve(message, activeSkillId) {
1055
+ const defaultPersona = this.personas.find((p) => p.meta.id === "default");
1056
+ const baseParts = [];
1057
+ if (defaultPersona) {
1058
+ baseParts.push(defaultPersona.body);
1059
+ }
1060
+ let bestMatch;
1061
+ let bestScore = 0;
1062
+ const lowerMessage = message.toLowerCase();
1063
+ for (const persona of this.personas) {
1064
+ if (persona.meta.id === "default")
1065
+ continue;
1066
+ let score = 0;
1067
+ if (persona.meta.triggers?.keywords) {
1068
+ for (const kw of persona.meta.triggers.keywords) {
1069
+ if (lowerMessage.includes(kw.toLowerCase())) {
1070
+ score++;
1071
+ }
1072
+ }
1073
+ }
1074
+ if (activeSkillId && persona.meta.triggers?.skills) {
1075
+ for (const pattern of persona.meta.triggers.skills) {
1076
+ if (matchGlob(pattern, activeSkillId)) {
1077
+ score += 2;
1078
+ }
1079
+ }
1080
+ }
1081
+ if (score === 0)
1082
+ continue;
1083
+ const priority = persona.meta.priority ?? 0;
1084
+ const finalScore = score + priority;
1085
+ if (finalScore > bestScore) {
1086
+ bestScore = finalScore;
1087
+ bestMatch = persona;
1088
+ }
1089
+ }
1090
+ if (bestMatch) {
1091
+ baseParts.push(bestMatch.body);
1092
+ }
1093
+ return baseParts.join("\n\n");
1094
+ }
1095
+ };
1096
+ function matchGlob(pattern, value) {
1097
+ if (pattern.endsWith("*")) {
1098
+ return value.startsWith(pattern.slice(0, -1));
1099
+ }
1100
+ return pattern === value;
1101
+ }
1102
+
1103
+ // ../core/dist/context-guard.js
1104
+ var DEFAULT_CONFIG = {
1105
+ maxContextTokens: 2e5,
1106
+ reservedForOutput: 8192,
1107
+ maxConversationTurns: 50
1108
+ };
1109
+ var ContextGuard = class {
1110
+ config;
1111
+ constructor(config = {}) {
1112
+ this.config = { ...DEFAULT_CONFIG, ...config };
1113
+ }
1114
+ estimateTokens(text) {
1115
+ return Math.ceil(text.length / 4);
1116
+ }
1117
+ compact(messages) {
1118
+ let result = [...messages];
1119
+ if (result.length > this.config.maxConversationTurns) {
1120
+ result = result.slice(-this.config.maxConversationTurns);
1121
+ }
1122
+ const recentToolCutoff = Math.max(0, result.length - 10);
1123
+ result = result.map((msg, i) => {
1124
+ if (msg.role === "tool" && i < recentToolCutoff) {
1125
+ return { ...msg, content: "[Tool result truncated]" };
1126
+ }
1127
+ return msg;
1128
+ });
1129
+ result = result.map((msg) => {
1130
+ if (msg.role === "tool" && msg.content.length > 2e3) {
1131
+ return { ...msg, content: msg.content.slice(0, 2e3) + "... [truncated]" };
1132
+ }
1133
+ return msg;
1134
+ });
1135
+ const budget = this.config.maxContextTokens - this.config.reservedForOutput;
1136
+ let totalTokens = result.reduce((sum, m) => sum + this.estimateTokens(m.content), 0);
1137
+ while (totalTokens > budget && result.length > 1) {
1138
+ const removed = result.shift();
1139
+ totalTokens -= this.estimateTokens(removed.content);
1140
+ }
1141
+ return result;
1142
+ }
1143
+ };
1144
+
1145
+ // ../core/dist/approval.js
1146
+ import { randomUUID } from "crypto";
1147
+ var ApprovalGate = class {
1148
+ pending = /* @__PURE__ */ new Map();
1149
+ channel;
1150
+ timeoutMs;
1151
+ log;
1152
+ constructor(config) {
1153
+ this.channel = config.channel;
1154
+ this.timeoutMs = config.timeoutMs ?? 12e4;
1155
+ this.log = config.logger ?? noopLogger;
1156
+ if (this.channel.onApprovalResponse) {
1157
+ this.channel.onApprovalResponse((response) => {
1158
+ const entry = this.pending.get(response.requestId);
1159
+ if (entry) {
1160
+ this.pending.delete(response.requestId);
1161
+ entry.resolve(response.approved);
1162
+ }
1163
+ });
1164
+ }
1165
+ }
1166
+ async request(userId, toolName, args) {
1167
+ if (!this.channel.sendApprovalRequest) {
1168
+ this.log.warn(`Channel does not support approval requests \u2014 auto-approving ${toolName}`);
1169
+ return true;
1170
+ }
1171
+ const requestId = randomUUID();
1172
+ const argsStr = JSON.stringify(args, null, 2);
1173
+ const text = `Tool "${toolName}" requires approval.
1174
+
1175
+ Arguments:
1176
+ ${argsStr}`;
1177
+ const buttons = [
1178
+ { label: "Approve", callbackData: `approve:${requestId}` },
1179
+ { label: "Reject", callbackData: `reject:${requestId}` }
1180
+ ];
1181
+ const resultPromise = new Promise((resolve2) => {
1182
+ const timer = setTimeout(() => {
1183
+ this.pending.delete(requestId);
1184
+ this.log.warn(`Approval timed out for ${toolName} (request ${requestId})`);
1185
+ resolve2(false);
1186
+ }, this.timeoutMs);
1187
+ this.pending.set(requestId, {
1188
+ resolve: (approved) => {
1189
+ clearTimeout(timer);
1190
+ resolve2(approved);
1191
+ }
1192
+ });
1193
+ });
1194
+ await this.channel.sendApprovalRequest(userId, text, buttons, requestId);
1195
+ return resultPromise;
1196
+ }
1197
+ };
1198
+
1199
+ // ../core/dist/logger.js
1200
+ import { styleText } from "util";
1201
+ var LEVELS = {
1202
+ debug: 0,
1203
+ info: 1,
1204
+ warn: 2,
1205
+ error: 3,
1206
+ silent: 4
1207
+ };
1208
+ function tag(level) {
1209
+ switch (level) {
1210
+ case "debug":
1211
+ return styleText("magenta", "DBG");
1212
+ case "info":
1213
+ return styleText("cyan", "INF");
1214
+ case "warn":
1215
+ return styleText("yellow", "WRN");
1216
+ case "error":
1217
+ return styleText("red", "ERR");
1218
+ }
1219
+ }
1220
+ function createLogger(opts = {}) {
1221
+ const min = LEVELS[opts.level ?? "info"];
1222
+ const scope = opts.scope;
1223
+ function emit(level, msg, args) {
1224
+ if (LEVELS[level] < min)
1225
+ return;
1226
+ const ts = styleText("dim", (/* @__PURE__ */ new Date()).toISOString().slice(11, 23));
1227
+ const lvl = tag(level);
1228
+ const sc = scope ? ` ${styleText("dim", scope)}` : "";
1229
+ const prefix = `${styleText("yellow", "\u25B2")} ${ts} ${lvl}${sc}`;
1230
+ const fn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
1231
+ if (args.length > 0) {
1232
+ fn(prefix, msg, ...args);
1233
+ } else {
1234
+ fn(prefix, msg);
1235
+ }
1236
+ }
1237
+ return {
1238
+ debug: (msg, ...args) => emit("debug", msg, args),
1239
+ info: (msg, ...args) => emit("info", msg, args),
1240
+ warn: (msg, ...args) => emit("warn", msg, args),
1241
+ error: (msg, ...args) => emit("error", msg, args),
1242
+ child: (childScope) => createLogger({
1243
+ level: opts.level,
1244
+ scope: scope ? `${scope}:${childScope}` : childScope
1245
+ })
1246
+ };
1247
+ }
1248
+
1249
+ // ../channels/dist/telegram/telegram.js
1250
+ import { Bot, InlineKeyboard } from "grammy";
1251
+
1252
+ // ../channels/dist/pipeline.js
1253
+ function createOutgoingPipeline(middlewares, send) {
1254
+ return async (message) => {
1255
+ let index = 0;
1256
+ const next = async () => {
1257
+ if (index < middlewares.length) {
1258
+ const mw = middlewares[index++];
1259
+ await mw(message, next);
1260
+ } else {
1261
+ await send(message);
1262
+ }
1263
+ };
1264
+ await next();
1265
+ };
1266
+ }
1267
+
1268
+ // ../channels/dist/middleware/split-message.js
1269
+ var TELEGRAM_MAX = 4096;
1270
+ function splitText(text, maxLength) {
1271
+ if (text.length <= maxLength)
1272
+ return [text];
1273
+ const chunks = [];
1274
+ let remaining = text;
1275
+ let openCodeBlock = null;
1276
+ while (remaining.length > 0) {
1277
+ const prefix = openCodeBlock ? `${openCodeBlock}
1278
+ ` : "";
1279
+ const effectiveMax = maxLength - prefix.length;
1280
+ if (remaining.length <= effectiveMax) {
1281
+ chunks.push(prefix + remaining);
1282
+ break;
1283
+ }
1284
+ let splitAt = -1;
1285
+ const searchArea = remaining.slice(0, effectiveMax);
1286
+ const paraIdx = searchArea.lastIndexOf("\n\n");
1287
+ if (paraIdx > effectiveMax * 0.3) {
1288
+ splitAt = paraIdx;
1289
+ }
1290
+ if (splitAt === -1) {
1291
+ const newlineIdx = searchArea.lastIndexOf("\n");
1292
+ if (newlineIdx > effectiveMax * 0.3) {
1293
+ splitAt = newlineIdx;
1294
+ }
1295
+ }
1296
+ if (splitAt === -1) {
1297
+ splitAt = effectiveMax;
1298
+ }
1299
+ let chunk = remaining.slice(0, splitAt);
1300
+ remaining = remaining.slice(splitAt).replace(/^\n+/, "");
1301
+ const fenceMatches = chunk.match(/```/g);
1302
+ const fenceCount = fenceMatches ? fenceMatches.length : 0;
1303
+ if (openCodeBlock) {
1304
+ chunk = prefix + chunk;
1305
+ if (fenceCount % 2 === 1) {
1306
+ openCodeBlock = null;
1307
+ } else {
1308
+ chunk += "\n```";
1309
+ }
1310
+ } else {
1311
+ if (fenceCount % 2 === 1) {
1312
+ const lastFenceIdx = chunk.lastIndexOf("```");
1313
+ const afterFence = chunk.slice(lastFenceIdx + 3);
1314
+ const langMatch = afterFence.match(/^(\w*)/);
1315
+ openCodeBlock = "```" + (langMatch?.[1] ?? "");
1316
+ chunk += "\n```";
1317
+ }
1318
+ }
1319
+ chunks.push(chunk);
1320
+ }
1321
+ return chunks.length === 0 ? [""] : chunks;
1322
+ }
1323
+ function createSplitMessageMiddleware(sendFn, maxLength = TELEGRAM_MAX) {
1324
+ return async (message, next) => {
1325
+ const chunks = splitText(message.text, maxLength);
1326
+ if (chunks.length <= 1) {
1327
+ await next();
1328
+ return;
1329
+ }
1330
+ for (let i = 0; i < chunks.length; i++) {
1331
+ await sendFn({
1332
+ ...message,
1333
+ text: chunks[i],
1334
+ replyTo: i === 0 ? message.replyTo : void 0
1335
+ });
1336
+ if (i < chunks.length - 1) {
1337
+ await new Promise((r) => setTimeout(r, 50));
1338
+ }
1339
+ }
1340
+ };
1341
+ }
1342
+
1343
+ // ../channels/dist/middleware/error-handler.js
1344
+ function isRetryable(error) {
1345
+ if (error instanceof Error) {
1346
+ const err = error;
1347
+ const status = err.status ?? err.error_code;
1348
+ if (status === 429 || status !== void 0 && status >= 500)
1349
+ return true;
1350
+ if (status !== void 0 && status >= 400 && status < 500)
1351
+ return false;
1352
+ return true;
1353
+ }
1354
+ return false;
1355
+ }
1356
+ async function withRetry(fn, options) {
1357
+ let lastError;
1358
+ for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
1359
+ try {
1360
+ return await fn();
1361
+ } catch (err) {
1362
+ lastError = err;
1363
+ if (!isRetryable(err) || attempt === options.maxRetries) {
1364
+ throw err;
1365
+ }
1366
+ const delay = options.baseDelayMs * Math.pow(2, attempt);
1367
+ await new Promise((r) => setTimeout(r, delay));
1368
+ }
1369
+ }
1370
+ throw lastError;
1371
+ }
1372
+
1373
+ // ../channels/dist/middleware/markdown-to-html.js
1374
+ function markdownToTelegramHtml(text) {
1375
+ if (!text)
1376
+ return "";
1377
+ const placeholders = [];
1378
+ const PH_PREFIX = "\uFFFCPH";
1379
+ const PH_SUFFIX = "\uFFFC";
1380
+ function hold(html) {
1381
+ const idx = placeholders.length;
1382
+ placeholders.push(html);
1383
+ return `${PH_PREFIX}${idx}${PH_SUFFIX}`;
1384
+ }
1385
+ let out = text;
1386
+ out = out.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => hold(lang ? `<pre><code class="language-${escapeHtml(lang)}">${escapeHtml(code.trimEnd())}</code></pre>` : `<pre>${escapeHtml(code.trimEnd())}</pre>`));
1387
+ out = out.replace(/`([^`\n]+)`/g, (_, code) => hold(`<code>${escapeHtml(code)}</code>`));
1388
+ out = out.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, linkText, url) => hold(`<a href="${escapeHtml(url)}">${escapeHtml(linkText)}</a>`));
1389
+ out = escapeHtml(out);
1390
+ out = out.replace(/\*\*\*(.+?)\*\*\*/g, "<b><i>$1</i></b>");
1391
+ out = out.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
1392
+ out = out.replace(/\*([^*\n]+?)\*/g, "<i>$1</i>");
1393
+ out = out.replace(/~~(.+?)~~/g, "<s>$1</s>");
1394
+ out = out.replace(/^#{1,6}\s+(.+)$/gm, "<b>$1</b>");
1395
+ out = out.replace(/^&gt;\s?(.*)$/gm, "<blockquote>$1</blockquote>");
1396
+ out = out.replace(/<\/blockquote>\n<blockquote>/g, "\n");
1397
+ const phPattern = new RegExp(`${PH_PREFIX}(\\d+)${PH_SUFFIX}`, "g");
1398
+ out = out.replace(phPattern, (_, idx) => placeholders[Number(idx)]);
1399
+ return out;
1400
+ }
1401
+ function escapeHtml(text) {
1402
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1403
+ }
1404
+
1405
+ // ../channels/dist/telegram/media.js
1406
+ function registerMediaHandlers(bot, isAllowed, handlers, onRejected) {
1407
+ bot.on("message:photo", async (ctx) => {
1408
+ const userId = ctx.from.id;
1409
+ if (!isAllowed(userId)) {
1410
+ onRejected?.(userId, new Date(ctx.message.date * 1e3));
1411
+ return;
1412
+ }
1413
+ const photos = ctx.message.photo;
1414
+ const largest = photos[photos.length - 1];
1415
+ const attachment = {
1416
+ type: "photo",
1417
+ fileId: largest.file_id,
1418
+ caption: ctx.message.caption ?? void 0
1419
+ };
1420
+ const incoming = {
1421
+ id: String(ctx.message.message_id),
1422
+ channelType: "telegram",
1423
+ userId: String(userId),
1424
+ text: ctx.message.caption ?? "[Photo]",
1425
+ timestamp: new Date(ctx.message.date * 1e3),
1426
+ replyTo: ctx.message.reply_to_message ? String(ctx.message.reply_to_message.message_id) : void 0,
1427
+ attachments: [attachment]
1428
+ };
1429
+ for (const handler of handlers) {
1430
+ await handler(incoming);
1431
+ }
1432
+ });
1433
+ bot.on("message:document", async (ctx) => {
1434
+ const userId = ctx.from.id;
1435
+ if (!isAllowed(userId)) {
1436
+ onRejected?.(userId, new Date(ctx.message.date * 1e3));
1437
+ return;
1438
+ }
1439
+ const doc = ctx.message.document;
1440
+ const attachment = {
1441
+ type: "document",
1442
+ fileId: doc.file_id,
1443
+ fileName: doc.file_name ?? void 0,
1444
+ mimeType: doc.mime_type ?? void 0,
1445
+ caption: ctx.message.caption ?? void 0
1446
+ };
1447
+ const incoming = {
1448
+ id: String(ctx.message.message_id),
1449
+ channelType: "telegram",
1450
+ userId: String(userId),
1451
+ text: ctx.message.caption ?? `[Document: ${doc.file_name ?? "unknown"}]`,
1452
+ timestamp: new Date(ctx.message.date * 1e3),
1453
+ replyTo: ctx.message.reply_to_message ? String(ctx.message.reply_to_message.message_id) : void 0,
1454
+ attachments: [attachment]
1455
+ };
1456
+ for (const handler of handlers) {
1457
+ await handler(incoming);
1458
+ }
1459
+ });
1460
+ }
1461
+
1462
+ // ../channels/dist/telegram/telegram.js
1463
+ var TelegramChannel = class {
1464
+ type = "telegram";
1465
+ bot;
1466
+ allowedUsers;
1467
+ handlers = [];
1468
+ approvalHandlers = [];
1469
+ sendPipeline;
1470
+ log;
1471
+ constructor(config) {
1472
+ this.log = config.logger ?? noopLogger;
1473
+ this.bot = new Bot(config.botToken);
1474
+ this.allowedUsers = new Set(config.allowedUsers);
1475
+ this.bot.catch((err) => {
1476
+ this.log.error("Bot error:", err.message ?? err);
1477
+ });
1478
+ this.bot.on("message:text", async (ctx) => {
1479
+ const userId = ctx.from.id;
1480
+ if (!this.isUserAllowed(userId)) {
1481
+ this.handleRejected(userId, ctx.message.date, config.rejectMessage);
1482
+ return;
1483
+ }
1484
+ const incoming = {
1485
+ id: String(ctx.message.message_id),
1486
+ channelType: "telegram",
1487
+ userId: String(userId),
1488
+ text: ctx.message.text,
1489
+ timestamp: new Date(ctx.message.date * 1e3),
1490
+ replyTo: ctx.message.reply_to_message ? String(ctx.message.reply_to_message.message_id) : void 0
1491
+ };
1492
+ for (const handler of this.handlers) {
1493
+ await handler(incoming);
1494
+ }
1495
+ });
1496
+ registerMediaHandlers(this.bot, (id) => this.isUserAllowed(id), this.handlers, (userId, ts) => this.handleRejected(userId, Math.floor(ts.getTime() / 1e3), config.rejectMessage));
1497
+ this.bot.on("callback_query:data", async (ctx) => {
1498
+ if (!this.isUserAllowed(ctx.from.id)) {
1499
+ await ctx.answerCallbackQuery({ text: "Not authorized" });
1500
+ return;
1501
+ }
1502
+ const data = ctx.callbackQuery.data;
1503
+ const match = /^(approve|reject):(.+)$/.exec(data);
1504
+ if (!match)
1505
+ return;
1506
+ const [, action, requestId] = match;
1507
+ const approved = action === "approve";
1508
+ const userId = String(ctx.from.id);
1509
+ await ctx.answerCallbackQuery({ text: approved ? "Approved" : "Rejected" });
1510
+ try {
1511
+ await ctx.editMessageReplyMarkup({ reply_markup: void 0 });
1512
+ const originalText = ctx.callbackQuery.message && "text" in ctx.callbackQuery.message ? ctx.callbackQuery.message.text ?? "" : "";
1513
+ await ctx.editMessageText(`${originalText}
1514
+
1515
+ ${approved ? "Approved" : "Rejected"}`);
1516
+ } catch {
1517
+ }
1518
+ for (const handler of this.approvalHandlers) {
1519
+ handler({ requestId, approved, userId });
1520
+ }
1521
+ });
1522
+ const convertAndSend = async (msg) => {
1523
+ const htmlText = markdownToTelegramHtml(msg.text);
1524
+ const replyOpts = msg.replyTo ? { reply_parameters: { message_id: Number(msg.replyTo) } } : {};
1525
+ await withRetry(() => this.bot.api.sendMessage(Number(msg.userId), htmlText, {
1526
+ parse_mode: "HTML",
1527
+ ...replyOpts
1528
+ }), { maxRetries: 3, baseDelayMs: 500 }).catch(async () => {
1529
+ await this.bot.api.sendMessage(Number(msg.userId), msg.text, replyOpts).catch((fallbackErr) => {
1530
+ this.log.error("Fallback send also failed:", fallbackErr);
1531
+ throw fallbackErr;
1532
+ });
1533
+ });
1534
+ };
1535
+ this.sendPipeline = createOutgoingPipeline([createSplitMessageMiddleware(convertAndSend)], convertAndSend);
1536
+ }
1537
+ isUserAllowed(userId) {
1538
+ return this.allowedUsers.has(userId);
1539
+ }
1540
+ handleRejected(userId, unixTimestamp, rejectMessage) {
1541
+ this.log.warn(`Rejected message from unauthorized user ${userId} at ${new Date(unixTimestamp * 1e3).toISOString()}`);
1542
+ if (rejectMessage) {
1543
+ this.bot.api.sendMessage(userId, rejectMessage).catch(() => {
1544
+ });
1545
+ }
1546
+ }
1547
+ onMessage(handler) {
1548
+ this.handlers.push(handler);
1549
+ }
1550
+ async start() {
1551
+ await this.bot.start();
1552
+ }
1553
+ async stop() {
1554
+ await this.bot.stop();
1555
+ }
1556
+ async send(message) {
1557
+ await this.sendPipeline(message);
1558
+ }
1559
+ async sendApprovalRequest(userId, text, buttons, _requestId) {
1560
+ const keyboard = new InlineKeyboard();
1561
+ for (const btn of buttons) {
1562
+ keyboard.text(btn.label, btn.callbackData);
1563
+ }
1564
+ await this.bot.api.sendMessage(Number(userId), text, {
1565
+ reply_markup: keyboard
1566
+ });
1567
+ }
1568
+ onApprovalResponse(handler) {
1569
+ this.approvalHandlers.push(handler);
1570
+ }
1571
+ };
1572
+
1573
+ // ../core/dist/main.js
1574
+ import Dockerode from "dockerode";
1575
+ import { resolve } from "path";
1576
+ import { createRequire } from "module";
1577
+
1578
+ // ../core/dist/version-checker.js
1579
+ var VersionChecker = class _VersionChecker {
1580
+ config;
1581
+ constructor(config) {
1582
+ this.config = config;
1583
+ }
1584
+ /** Check npm registry for latest version */
1585
+ async check() {
1586
+ try {
1587
+ const response = await fetch(`https://registry.npmjs.org/${this.config.packageName}/latest`);
1588
+ if (!response.ok) {
1589
+ return {
1590
+ updateAvailable: false,
1591
+ currentVersion: this.config.currentVersion,
1592
+ error: `npm registry returned ${response.status}`
1593
+ };
1594
+ }
1595
+ const data = await response.json();
1596
+ const latest = data.version;
1597
+ return {
1598
+ updateAvailable: _VersionChecker.compareVersions(this.config.currentVersion, latest) < 0,
1599
+ currentVersion: this.config.currentVersion,
1600
+ latestVersion: latest
1601
+ };
1602
+ } catch (err) {
1603
+ return {
1604
+ updateAvailable: false,
1605
+ currentVersion: this.config.currentVersion,
1606
+ error: err instanceof Error ? err.message : String(err)
1607
+ };
1608
+ }
1609
+ }
1610
+ /** Compare two semver strings (MAJOR.MINOR.PATCH only, pre-release suffixes stripped). Returns -1 if a < b, 0 if equal, 1 if a > b */
1611
+ static compareVersions(a, b) {
1612
+ const clean = (v) => v.replace(/^v/, "").split("-")[0];
1613
+ const pa = clean(a).split(".").map(Number);
1614
+ const pb = clean(b).split(".").map(Number);
1615
+ for (let i = 0; i < 3; i++) {
1616
+ const va = pa[i] ?? 0;
1617
+ const vb = pb[i] ?? 0;
1618
+ if (va < vb)
1619
+ return -1;
1620
+ if (va > vb)
1621
+ return 1;
1622
+ }
1623
+ return 0;
1624
+ }
1625
+ };
1626
+
1627
+ // ../core/dist/main.js
1628
+ var BASE_SYSTEM_PROMPT = `You are Augure, a personal AI assistant. You are proactive, helpful, and concise.
1629
+ You speak the same language as the user. You have access to tools and persistent memory.
1630
+ Always be direct and actionable.
1631
+
1632
+ ## Your capabilities
1633
+
1634
+ You have access to tools that let you interact with the outside world. Use the datetime tool when the user needs precise time information beyond what is shown in the current date above. Use memory tools to remember and recall information across conversations. Use the schedule tool to create recurring or one-shot tasks.
1635
+
1636
+ If a tool is marked as [NOT CONFIGURED], let the user know it needs to be set up first and share the documentation link from the tool description.`;
1637
+ var SKILLS_PROMPT = `
1638
+ ## Skills
1639
+
1640
+ You can create and manage "skills" \u2014 autonomous code units that run in isolated Docker containers. Skills are powerful: they let you automate tasks, run on a schedule, and self-heal when they break.
1641
+
1642
+ - Use skill_list to see existing skills and their status
1643
+ - Use skill_generate to create a new skill from a natural language description
1644
+ - Use skill_run to execute a skill manually
1645
+ - Use skill_heal to fix a broken skill
1646
+ - Use skill_install to install a skill from the hub
1647
+
1648
+ When a user asks to automate a recurring task (e.g. "check this every morning", "send me a summary daily"), suggest creating a skill with a cron trigger. Skills can also be triggered manually or by events.`;
1649
+ function resolveLLMClient(config, usage, logger) {
1650
+ const override = usage !== "default" ? config[usage] : void 0;
1651
+ return new OpenRouterClient({
1652
+ apiKey: override?.apiKey ?? config.default.apiKey,
1653
+ model: override?.model ?? config.default.model,
1654
+ maxTokens: override?.maxTokens ?? config.default.maxTokens,
1655
+ logger: logger.child("llm")
1656
+ });
1657
+ }
1658
+ async function startAgent(configPath, opts) {
1659
+ const log = createLogger({ level: opts?.debug ? "debug" : "info" });
1660
+ const config = await loadConfig(configPath);
1661
+ log.info(`Loaded config: ${config.identity.name}`);
1662
+ log.debug(`Config path: ${configPath}`);
1663
+ let telegram;
1664
+ const llm = resolveLLMClient(config.llm, "default", log);
1665
+ const ingestionLLM = resolveLLMClient(config.llm, "ingestion", log);
1666
+ const monitoringLLM = resolveLLMClient(config.llm, "monitoring", log);
1667
+ const memoryPath = resolve(configPath, "..", config.memory.path);
1668
+ const memory = new FileMemoryStore(memoryPath);
1669
+ log.info(`Memory store: ${memoryPath}`);
1670
+ const retriever = new MemoryRetriever(memory, {
1671
+ maxTokens: config.memory.maxRetrievalTokens
1672
+ });
1673
+ const ingester = config.memory.autoIngest ? new MemoryIngester(ingestionLLM, memory) : void 0;
1674
+ const tools = new ToolRegistry();
1675
+ tools.register(memoryReadTool);
1676
+ tools.register(memoryWriteTool);
1677
+ tools.register(scheduleTool);
1678
+ tools.register(datetimeTool);
1679
+ tools.register(webSearchTool);
1680
+ tools.register(httpTool);
1681
+ tools.register(emailTool);
1682
+ tools.register(sandboxExecTool);
1683
+ tools.register(opencodeTool);
1684
+ tools.register(githubTool);
1685
+ let browserManager;
1686
+ if (config.tools?.browser) {
1687
+ const browserLlm = config.llm.coding ?? config.llm.default;
1688
+ browserManager = new BrowserSessionManager({
1689
+ config: config.tools.browser,
1690
+ llm: {
1691
+ provider: browserLlm.provider ?? config.llm.default.provider,
1692
+ apiKey: browserLlm.apiKey ?? config.llm.default.apiKey,
1693
+ model: browserLlm.model ?? config.llm.default.model,
1694
+ maxTokens: browserLlm.maxTokens ?? config.llm.default.maxTokens
1695
+ },
1696
+ ttlMs: 12e4,
1697
+ logger: log.child("browser")
1698
+ });
1699
+ tools.register(createBrowserTool(browserManager));
1700
+ log.info("Browser tool registered", { provider: config.tools.browser.provider });
1701
+ }
1702
+ const jobStorePath = resolve(configPath, "..", "jobs.json");
1703
+ const jobStore = new JobStore(jobStorePath);
1704
+ const scheduler = new CronScheduler({ store: jobStore, logger: log.child("scheduler") });
1705
+ await scheduler.loadPersistedJobs();
1706
+ log.info(`Loaded ${scheduler.listJobs().length} persisted jobs`);
1707
+ for (const job of config.scheduler.jobs) {
1708
+ if (!scheduler.listJobs().some((j) => j.id === job.id)) {
1709
+ scheduler.addJob({ ...job, enabled: true });
1710
+ }
1711
+ }
1712
+ const docker = new Dockerode();
1713
+ const sandboxImage = config.sandbox.image ?? "augure-sandbox:latest";
1714
+ const sandboxLog = log.child("sandbox");
1715
+ await ensureImage(docker, sandboxImage, sandboxLog);
1716
+ const pool = new DockerContainerPool(docker, {
1717
+ image: sandboxImage,
1718
+ maxTotal: config.security.maxConcurrentSandboxes,
1719
+ logger: sandboxLog
1720
+ });
1721
+ log.info(`Container pool: max=${config.security.maxConcurrentSandboxes}`);
1722
+ let skillManagerRef;
1723
+ let skillUpdater;
1724
+ if (config.skills) {
1725
+ const skillsPath = resolve(configPath, "..", config.skills.path);
1726
+ const codingLLM = resolveLLMClient(config.llm, "coding", log);
1727
+ const skillManager = new SkillManager(skillsPath);
1728
+ const skillGenerator = new SkillGenerator(codingLLM);
1729
+ const skillRunner = new SkillRunner({
1730
+ pool,
1731
+ manager: skillManager,
1732
+ defaults: config.sandbox.defaults,
1733
+ browserManager
1734
+ });
1735
+ const skillTester = new SkillTester({
1736
+ pool,
1737
+ defaults: config.sandbox.defaults
1738
+ });
1739
+ const skillHealer = new SkillHealer({
1740
+ manager: skillManager,
1741
+ generator: skillGenerator,
1742
+ tester: skillTester,
1743
+ maxAttempts: config.skills.maxFailures,
1744
+ skillsPath
1745
+ });
1746
+ await installBuiltins(skillManager);
1747
+ const hub = config.skills.hub ? new SkillHub({ repo: config.skills.hub.repo, branch: config.skills.hub.branch ?? "main" }) : void 0;
1748
+ const skillTools = createSkillTools({
1749
+ manager: skillManager,
1750
+ runner: skillRunner,
1751
+ generator: skillGenerator,
1752
+ healer: skillHealer,
1753
+ hub
1754
+ });
1755
+ for (const tool of skillTools) {
1756
+ tools.register(tool);
1757
+ }
1758
+ const skillBridge = new SkillSchedulerBridge(scheduler, skillManager);
1759
+ await skillBridge.syncAll();
1760
+ if (hub && config.updates?.skills?.enabled !== false) {
1761
+ skillUpdater = new SkillUpdater({
1762
+ manager: skillManager,
1763
+ hub,
1764
+ tester: skillTester
1765
+ });
1766
+ try {
1767
+ const updateResults = await skillUpdater.checkAndApply();
1768
+ const updated = updateResults.filter((r) => r.success);
1769
+ const failed = updateResults.filter((r) => !r.success);
1770
+ if (updated.length > 0) {
1771
+ log.info(`Skills updated: ${updated.map((r) => `${r.skillId} (v${r.fromVersion}\u2192v${r.toVersion})`).join(", ")}`);
1772
+ }
1773
+ if (failed.length > 0) {
1774
+ log.warn(`Skill updates failed: ${failed.map((r) => `${r.skillId}: ${r.error}`).join(", ")}`);
1775
+ }
1776
+ } catch (err) {
1777
+ log.error("Skill update check failed:", err);
1778
+ }
1779
+ }
1780
+ skillManagerRef = skillManager;
1781
+ log.info(`Skills initialized: ${skillsPath}`);
1782
+ }
1783
+ tools.setContext({ config, memory, scheduler, pool });
1784
+ let codeModeExecutor;
1785
+ if (config.codeMode) {
1786
+ const cmConfig = config.codeMode;
1787
+ if (cmConfig.runtime === "vm") {
1788
+ codeModeExecutor = new VmExecutor(tools, {
1789
+ timeout: cmConfig.timeout * 1e3,
1790
+ // VmExecutor expects ms
1791
+ memoryLimit: cmConfig.memoryLimit
1792
+ });
1793
+ } else if (cmConfig.runtime === "docker") {
1794
+ codeModeExecutor = new DockerExecutor({
1795
+ registry: tools,
1796
+ pool,
1797
+ timeout: cmConfig.timeout,
1798
+ // DockerExecutor expects seconds
1799
+ memoryLimit: config.sandbox.defaults.memoryLimit,
1800
+ cpuLimit: config.sandbox.defaults.cpuLimit
1801
+ });
1802
+ } else {
1803
+ const vmExec = new VmExecutor(tools, {
1804
+ timeout: cmConfig.timeout * 1e3,
1805
+ memoryLimit: cmConfig.memoryLimit
1806
+ });
1807
+ const dockerExec = new DockerExecutor({
1808
+ registry: tools,
1809
+ pool,
1810
+ timeout: cmConfig.timeout,
1811
+ memoryLimit: config.sandbox.defaults.memoryLimit,
1812
+ cpuLimit: config.sandbox.defaults.cpuLimit
1813
+ });
1814
+ codeModeExecutor = new AutoExecutor(vmExec, dockerExec);
1815
+ }
1816
+ log.info(`Code Mode enabled: runtime=${cmConfig.runtime}, timeout=${cmConfig.timeout}s`);
1817
+ }
1818
+ const auditConfig = config.audit ?? { path: "./logs", enabled: true };
1819
+ const auditPath = resolve(configPath, "..", auditConfig.path);
1820
+ const audit = auditConfig.enabled ? new FileAuditLogger(auditPath, log.child("audit")) : new NullAuditLogger();
1821
+ log.info(`Audit: ${auditConfig.enabled ? auditPath : "disabled"}`);
1822
+ let personaResolver;
1823
+ if (config.persona) {
1824
+ const personaPath = resolve(configPath, "..", config.persona.path);
1825
+ personaResolver = new PersonaResolver(personaPath);
1826
+ await personaResolver.loadAll();
1827
+ log.info(`Personas: ${personaPath}`);
1828
+ }
1829
+ let cliVersion;
1830
+ try {
1831
+ const require2 = createRequire(import.meta.url);
1832
+ const pkg = require2("augure/package.json");
1833
+ cliVersion = pkg.version;
1834
+ } catch {
1835
+ }
1836
+ if (cliVersion && config.updates?.cli?.enabled !== false) {
1837
+ const versionChecker = new VersionChecker({
1838
+ currentVersion: cliVersion,
1839
+ packageName: "augure"
1840
+ });
1841
+ const versionResult = await versionChecker.check();
1842
+ if (versionResult.updateAvailable) {
1843
+ log.warn(`Update available: v${versionResult.latestVersion} (current: v${versionResult.currentVersion}). Run: npm update -g augure`);
1844
+ }
1845
+ }
1846
+ const guard = new ContextGuard({
1847
+ maxContextTokens: 2e5,
1848
+ reservedForOutput: config.llm.default.maxTokens ?? 8192
1849
+ });
1850
+ const systemPrompt = config.skills ? BASE_SYSTEM_PROMPT + SKILLS_PROMPT : BASE_SYSTEM_PROMPT;
1851
+ const activeChannel = telegram;
1852
+ const approvalGate = config.approval?.enabled && activeChannel ? new ApprovalGate({
1853
+ channel: activeChannel,
1854
+ timeoutMs: config.approval.timeoutMs,
1855
+ logger: log.child("approval")
1856
+ }) : void 0;
1857
+ if (approvalGate) {
1858
+ log.info(`Approval gate enabled (timeout: ${config.approval.timeoutMs ?? 12e4}ms)`);
1859
+ }
1860
+ const agent = new Agent({
1861
+ llm,
1862
+ tools,
1863
+ systemPrompt,
1864
+ memoryContent: "",
1865
+ retriever,
1866
+ ingester,
1867
+ audit,
1868
+ guard,
1869
+ modelName: config.llm.default.model,
1870
+ logger: log.child("agent"),
1871
+ codeModeExecutor,
1872
+ approvalGate
1873
+ });
1874
+ const heartbeatIntervalMs = parseInterval(config.scheduler.heartbeatInterval);
1875
+ const heartbeat = new Heartbeat({
1876
+ llm: monitoringLLM,
1877
+ memory,
1878
+ intervalMs: heartbeatIntervalMs,
1879
+ logger: log.child("heartbeat"),
1880
+ onAction: async (action) => {
1881
+ log.info(`Heartbeat action: ${action}`);
1882
+ const response = await agent.handleMessage({
1883
+ id: `heartbeat-${Date.now()}`,
1884
+ channelType: "system",
1885
+ userId: "system",
1886
+ text: `[Heartbeat] ${action}`,
1887
+ timestamp: /* @__PURE__ */ new Date()
1888
+ });
1889
+ log.debug(`Heartbeat response: ${response.slice(0, 200)}`);
1890
+ }
1891
+ });
1892
+ scheduler.onJobTrigger(async (job) => {
1893
+ log.info(`Job triggered: ${job.id}`);
1894
+ const response = await agent.handleMessage({
1895
+ id: `job-${job.id}-${Date.now()}`,
1896
+ channelType: "system",
1897
+ userId: "system",
1898
+ text: job.prompt,
1899
+ timestamp: /* @__PURE__ */ new Date()
1900
+ });
1901
+ if (telegram && config.channels.telegram?.enabled) {
1902
+ const userId = config.channels.telegram.allowedUsers[0];
1903
+ if (userId !== void 0) {
1904
+ await telegram.send({
1905
+ channelType: "telegram",
1906
+ userId: String(userId),
1907
+ text: response
1908
+ });
1909
+ }
1910
+ }
1911
+ log.debug(`Job ${job.id} completed`);
1912
+ });
1913
+ scheduler.start();
1914
+ heartbeat.start();
1915
+ log.info(`Scheduler started: ${scheduler.listJobs().length} jobs, heartbeat every ${config.scheduler.heartbeatInterval}`);
1916
+ if (config.channels.telegram?.enabled) {
1917
+ const telegramLog = log.child("telegram");
1918
+ telegram = new TelegramChannel({
1919
+ botToken: config.channels.telegram.botToken,
1920
+ allowedUsers: config.channels.telegram.allowedUsers,
1921
+ rejectMessage: config.channels.telegram.rejectMessage,
1922
+ logger: telegramLog
1923
+ });
1924
+ const tg = telegram;
1925
+ const commandCtx = {
1926
+ scheduler,
1927
+ pool,
1928
+ agent,
1929
+ skillManager: skillManagerRef
1930
+ };
1931
+ tg.onMessage(async (msg) => {
1932
+ log.info(`Message from ${msg.userId}: ${msg.text}`);
1933
+ try {
1934
+ const cmdResult = await handleCommand(msg.text, commandCtx);
1935
+ if (cmdResult.handled) {
1936
+ await tg.send({
1937
+ channelType: "telegram",
1938
+ userId: msg.userId,
1939
+ text: cmdResult.response ?? "OK",
1940
+ replyTo: msg.id
1941
+ });
1942
+ return;
1943
+ }
1944
+ if (personaResolver) {
1945
+ agent.setPersona(personaResolver.resolve(msg.text));
1946
+ }
1947
+ const response = await agent.handleMessage(msg);
1948
+ await tg.send({
1949
+ channelType: "telegram",
1950
+ userId: msg.userId,
1951
+ text: response,
1952
+ replyTo: msg.id
1953
+ });
1954
+ } catch (err) {
1955
+ log.error("Error handling message:", err);
1956
+ await tg.send({
1957
+ channelType: "telegram",
1958
+ userId: msg.userId,
1959
+ text: "An error occurred while processing your message."
1960
+ });
1961
+ }
1962
+ });
1963
+ log.info("Telegram bot starting...");
1964
+ }
1965
+ const updateTimers = [];
1966
+ if (skillUpdater && config.updates?.skills?.checkInterval) {
1967
+ const su = skillUpdater;
1968
+ const skillCheckMs = parseInterval(config.updates.skills.checkInterval);
1969
+ updateTimers.push(setInterval(async () => {
1970
+ try {
1971
+ const results = await su.checkAndApply();
1972
+ for (const r of results) {
1973
+ if (r.success) {
1974
+ log.info(`Skill auto-updated: ${r.skillId} v${r.fromVersion}\u2192v${r.toVersion}`);
1975
+ } else if (r.rolledBack) {
1976
+ log.warn(`Skill update rolled back: ${r.skillId} - ${r.error}`);
1977
+ }
1978
+ }
1979
+ } catch (err) {
1980
+ log.error("Periodic skill update check failed:", err);
1981
+ }
1982
+ }, skillCheckMs));
1983
+ }
1984
+ if (cliVersion && config.updates?.cli?.enabled !== false && config.channels.telegram?.enabled) {
1985
+ const cliCheckMs = parseInterval(config.updates?.cli?.checkInterval ?? "24h");
1986
+ const versionChecker = new VersionChecker({
1987
+ currentVersion: cliVersion,
1988
+ packageName: "augure"
1989
+ });
1990
+ updateTimers.push(setInterval(async () => {
1991
+ try {
1992
+ const result = await versionChecker.check();
1993
+ if (result.updateAvailable && telegram) {
1994
+ const userId = config.channels.telegram?.allowedUsers[0];
1995
+ if (userId !== void 0) {
1996
+ await telegram.send({
1997
+ channelType: "telegram",
1998
+ userId: String(userId),
1999
+ text: `Update available: Augure v${result.latestVersion} (current: v${result.currentVersion}).
2000
+ Run: \`npm update -g augure\``
2001
+ });
2002
+ }
2003
+ }
2004
+ } catch (err) {
2005
+ log.error("CLI version check failed:", err);
2006
+ }
2007
+ }, cliCheckMs));
2008
+ }
2009
+ let mcpHttpServer;
2010
+ if (config.mcp?.enabled || opts?.mcp) {
2011
+ const { createMcpServer: createMcpServer2 } = await import("./mcp-server-AUASYDPU.js");
2012
+ const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
2013
+ const { createServer } = await import("http");
2014
+ const mcpPort = config.mcp?.port ?? 3100;
2015
+ const mcpServer = createMcpServer2({
2016
+ tools,
2017
+ memory,
2018
+ scheduler,
2019
+ personaResolver,
2020
+ logger: log.child("mcp")
2021
+ });
2022
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID() });
2023
+ await mcpServer.connect(transport);
2024
+ mcpHttpServer = createServer(async (req, res) => {
2025
+ if (req.url === "/mcp") {
2026
+ await transport.handleRequest(req, res);
2027
+ } else {
2028
+ res.writeHead(404);
2029
+ res.end("Not found");
2030
+ }
2031
+ });
2032
+ mcpHttpServer.listen(mcpPort);
2033
+ log.info(`MCP server listening on port ${mcpPort}`);
2034
+ }
2035
+ const shutdown = async () => {
2036
+ log.info("Shutting down...");
2037
+ for (const timer of updateTimers)
2038
+ clearInterval(timer);
2039
+ heartbeat.stop();
2040
+ scheduler.stop();
2041
+ if (mcpHttpServer)
2042
+ mcpHttpServer.close();
2043
+ if (telegram)
2044
+ await telegram.stop();
2045
+ if (browserManager)
2046
+ await browserManager.closeAll();
2047
+ await pool.destroyAll();
2048
+ await audit.close();
2049
+ log.info("All containers destroyed");
2050
+ process.exit(0);
2051
+ };
2052
+ process.on("SIGINT", shutdown);
2053
+ process.on("SIGTERM", shutdown);
2054
+ if (telegram) {
2055
+ await telegram.start();
2056
+ }
2057
+ }
2058
+
2059
+ export {
2060
+ loadConfig,
2061
+ OpenRouterClient,
2062
+ assembleContext,
2063
+ summarize,
2064
+ FileAuditLogger,
2065
+ NullAuditLogger,
2066
+ Agent,
2067
+ handleCommand,
2068
+ PersonaResolver,
2069
+ ContextGuard,
2070
+ ApprovalGate,
2071
+ createLogger,
2072
+ VersionChecker,
2073
+ startAgent
2074
+ };