augure 0.7.1 → 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.
- package/dist/bin.js +1031 -9116
- package/dist/chunk-3BGYMW62.js +272 -0
- package/dist/chunk-62OPINAX.js +16 -0
- package/dist/chunk-63TMJ6FS.js +5091 -0
- package/dist/chunk-DZMTL3S3.js +120 -0
- package/dist/chunk-FYS2JH42.js +31 -0
- package/dist/chunk-H6PBSIJ5.js +145 -0
- package/dist/chunk-KC5S36DB.js +2074 -0
- package/dist/chunk-M63XRSRV.js +164 -0
- package/dist/chunk-TP6A6GFU.js +348 -0
- package/dist/chunk-XOMIXLMX.js +1294 -0
- package/dist/dist-6CLLI773.js +15 -0
- package/dist/dist-BKJP4DMX.js +45 -0
- package/dist/dist-FE4CO3VD.js +11 -0
- package/dist/dist-FMYMCIS2.js +30 -0
- package/dist/dist-JEXFY3AR.js +41 -0
- package/dist/dist-LIV4W4QA.js +13 -0
- package/dist/dist-QR2YZUIK.js +12 -0
- package/dist/mcp-server-AUASYDPU.js +8 -0
- package/package.json +11 -9
|
@@ -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(/^>\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, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
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
|
+
};
|