augure 0.3.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin.js +582 -47
  2. package/package.json +20 -20
package/dist/bin.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/bin.ts
4
- import { createRequire } from "module";
4
+ import { createRequire as createRequire2 } from "module";
5
5
  import { defineCommand as defineCommand4, runMain } from "citty";
6
6
 
7
7
  // src/commands/start.ts
8
8
  import { defineCommand } from "citty";
9
- import { resolve as resolve2 } from "path";
9
+ import { resolve as resolve2, dirname as dirname4, join as join6 } from "path";
10
10
 
11
11
  // ../core/dist/config.js
12
12
  import { readFile } from "fs/promises";
@@ -35,7 +35,8 @@ var AppConfigSchema = z.object({
35
35
  telegram: z.object({
36
36
  enabled: z.boolean(),
37
37
  botToken: z.string(),
38
- allowedUsers: z.array(z.number())
38
+ allowedUsers: z.array(z.number()),
39
+ rejectMessage: z.string().optional()
39
40
  }).optional(),
40
41
  whatsapp: z.object({ enabled: z.boolean() }).optional(),
41
42
  web: z.object({ enabled: z.boolean(), port: z.number() }).optional()
@@ -120,6 +121,17 @@ var AppConfigSchema = z.object({
120
121
  }).optional(),
121
122
  persona: z.object({
122
123
  path: z.string().min(1).default("./config/personas")
124
+ }).optional(),
125
+ updates: z.object({
126
+ skills: z.object({
127
+ enabled: z.boolean().default(true),
128
+ checkInterval: z.string().min(1).default("6h")
129
+ }).optional(),
130
+ cli: z.object({
131
+ enabled: z.boolean().default(true),
132
+ checkInterval: z.string().min(1).default("24h"),
133
+ notifyChannel: z.string().min(1).default("telegram")
134
+ }).optional()
123
135
  }).optional()
124
136
  });
125
137
  function interpolateEnvVars(raw) {
@@ -150,7 +162,7 @@ var OpenRouterClient = class {
150
162
  this.maxTokens = config.maxTokens;
151
163
  this.baseUrl = config.baseUrl ?? "https://openrouter.ai/api/v1";
152
164
  }
153
- async chat(messages) {
165
+ async chat(messages, tools) {
154
166
  const response = await fetch(`${this.baseUrl}/chat/completions`, {
155
167
  method: "POST",
156
168
  headers: {
@@ -163,8 +175,19 @@ var OpenRouterClient = class {
163
175
  messages: messages.map((m) => ({
164
176
  role: m.role,
165
177
  content: m.content,
166
- ...m.toolCallId ? { tool_call_id: m.toolCallId } : {}
167
- }))
178
+ ...m.toolCallId ? { tool_call_id: m.toolCallId } : {},
179
+ ...m.toolCalls?.length ? {
180
+ tool_calls: m.toolCalls.map((tc) => ({
181
+ id: tc.id,
182
+ type: "function",
183
+ function: {
184
+ name: tc.name,
185
+ arguments: JSON.stringify(tc.arguments)
186
+ }
187
+ }))
188
+ } : {}
189
+ })),
190
+ ...tools?.length ? { tools } : {}
168
191
  })
169
192
  });
170
193
  if (!response.ok) {
@@ -175,11 +198,15 @@ var OpenRouterClient = class {
175
198
  const choice = data.choices[0];
176
199
  return {
177
200
  content: choice.message.content ?? "",
178
- toolCalls: (choice.message.tool_calls ?? []).map((tc) => ({
179
- id: tc.id,
180
- name: tc.function.name,
181
- arguments: JSON.parse(tc.function.arguments)
182
- })),
201
+ toolCalls: (choice.message.tool_calls ?? []).map((tc) => {
202
+ let args = {};
203
+ try {
204
+ args = tc.function.arguments ? JSON.parse(tc.function.arguments) : {};
205
+ } catch {
206
+ console.error(`[augure] Failed to parse tool call arguments for ${tc.function.name}:`, tc.function.arguments);
207
+ }
208
+ return { id: tc.id, name: tc.function.name, arguments: args };
209
+ }),
183
210
  usage: {
184
211
  inputTokens: data.usage.prompt_tokens,
185
212
  outputTokens: data.usage.completion_tokens
@@ -190,7 +217,7 @@ var OpenRouterClient = class {
190
217
 
191
218
  // ../core/dist/context.js
192
219
  function assembleContext(input) {
193
- const { systemPrompt, memoryContent, toolSchemas, conversationHistory, persona } = input;
220
+ const { systemPrompt, memoryContent, conversationHistory, persona } = input;
194
221
  let system = systemPrompt;
195
222
  if (persona) {
196
223
  system += `
@@ -203,13 +230,6 @@ ${persona}`;
203
230
 
204
231
  ## Memory
205
232
  ${memoryContent}`;
206
- }
207
- if (toolSchemas.length > 0) {
208
- const toolList = toolSchemas.map((s) => `- **${s.function.name}**: ${s.function.description}`).join("\n");
209
- system += `
210
-
211
- ## Available Tools
212
- ${toolList}`;
213
233
  }
214
234
  const messages = [{ role: "system", content: system }];
215
235
  messages.push(...conversationHistory);
@@ -258,7 +278,7 @@ var NullAuditLogger = class {
258
278
  // ../core/dist/agent.js
259
279
  var Agent = class {
260
280
  config;
261
- conversationHistory = [];
281
+ conversations = /* @__PURE__ */ new Map();
262
282
  state = "running";
263
283
  constructor(config) {
264
284
  this.config = config;
@@ -277,30 +297,33 @@ var Agent = class {
277
297
  return "Agent is in emergency stop mode. Send /resume to reactivate.";
278
298
  }
279
299
  const start = Date.now();
280
- this.conversationHistory.push({
300
+ const userId = incoming.userId;
301
+ let history = this.conversations.get(userId) ?? [];
302
+ history.push({
281
303
  role: "user",
282
304
  content: incoming.text
283
305
  });
284
306
  if (this.config.guard) {
285
- this.conversationHistory = this.config.guard.compact(this.conversationHistory);
307
+ history = this.config.guard.compact(history);
286
308
  }
309
+ this.conversations.set(userId, history);
287
310
  let memoryContent = this.config.memoryContent;
288
311
  if (this.config.retriever) {
289
312
  memoryContent = await this.config.retriever.retrieve();
290
313
  }
291
314
  const maxLoops = this.config.maxToolLoops ?? 10;
292
315
  let loopCount = 0;
316
+ const toolSchemas = this.config.tools.toFunctionSchemas();
293
317
  while (loopCount < maxLoops) {
294
318
  const messages = assembleContext({
295
319
  systemPrompt: this.config.systemPrompt,
296
320
  memoryContent,
297
- toolSchemas: this.config.tools.toFunctionSchemas(),
298
- conversationHistory: this.conversationHistory,
321
+ conversationHistory: history,
299
322
  persona: this.config.persona
300
323
  });
301
- const response = await this.config.llm.chat(messages);
324
+ const response = await this.config.llm.chat(messages, toolSchemas);
302
325
  if (response.toolCalls.length === 0) {
303
- this.conversationHistory.push({
326
+ history.push({
304
327
  role: "assistant",
305
328
  content: response.content
306
329
  });
@@ -321,18 +344,19 @@ var Agent = class {
321
344
  });
322
345
  }
323
346
  if (this.config.ingester) {
324
- this.config.ingester.ingest(this.conversationHistory).catch((err2) => console.error("[augure] Ingestion error:", err2));
347
+ this.config.ingester.ingest(history).catch((err2) => console.error("[augure] Ingestion error:", err2));
325
348
  }
326
349
  return response.content;
327
350
  }
328
- this.conversationHistory.push({
351
+ history.push({
329
352
  role: "assistant",
330
- content: response.content || ""
353
+ content: response.content || "",
354
+ toolCalls: response.toolCalls
331
355
  });
332
356
  for (const toolCall of response.toolCalls) {
333
357
  const toolStart = Date.now();
334
358
  const result = await this.config.tools.execute(toolCall.name, toolCall.arguments);
335
- this.conversationHistory.push({
359
+ history.push({
336
360
  role: "tool",
337
361
  content: result.output,
338
362
  toolCallId: toolCall.id
@@ -354,11 +378,22 @@ var Agent = class {
354
378
  }
355
379
  return "Max tool call loops reached. Please try again.";
356
380
  }
357
- getConversationHistory() {
358
- return [...this.conversationHistory];
381
+ getConversationHistory(userId) {
382
+ if (userId) {
383
+ return [...this.conversations.get(userId) ?? []];
384
+ }
385
+ const all = [];
386
+ for (const msgs of this.conversations.values()) {
387
+ all.push(...msgs);
388
+ }
389
+ return all;
359
390
  }
360
- clearHistory() {
361
- this.conversationHistory = [];
391
+ clearHistory(userId) {
392
+ if (userId) {
393
+ this.conversations.delete(userId);
394
+ } else {
395
+ this.conversations.clear();
396
+ }
362
397
  }
363
398
  };
364
399
 
@@ -528,19 +563,260 @@ var ContextGuard = class {
528
563
  }
529
564
  };
530
565
 
531
- // ../channels/dist/telegram.js
566
+ // ../channels/dist/telegram/telegram.js
532
567
  import { Bot } from "grammy";
568
+
569
+ // ../channels/dist/pipeline.js
570
+ function createOutgoingPipeline(middlewares, send) {
571
+ return async (message) => {
572
+ let index = 0;
573
+ const next = async () => {
574
+ if (index < middlewares.length) {
575
+ const mw = middlewares[index++];
576
+ await mw(message, next);
577
+ } else {
578
+ await send(message);
579
+ }
580
+ };
581
+ await next();
582
+ };
583
+ }
584
+
585
+ // ../channels/dist/middleware/escape-markdown.js
586
+ var SPECIAL_CHARS = /* @__PURE__ */ new Set([".", "!", ">", "#", "+", "-", "=", "|", "{", "}", "~"]);
587
+ function escapeMarkdownV2(text) {
588
+ if (!text)
589
+ return "";
590
+ const parts = [];
591
+ let i = 0;
592
+ while (i < text.length) {
593
+ if (text.startsWith("```", i)) {
594
+ const endIdx = text.indexOf("```", i + 3);
595
+ if (endIdx !== -1) {
596
+ parts.push(text.slice(i, endIdx + 3));
597
+ i = endIdx + 3;
598
+ continue;
599
+ }
600
+ }
601
+ if (text[i] === "`") {
602
+ const endIdx = text.indexOf("`", i + 1);
603
+ if (endIdx !== -1) {
604
+ parts.push(text.slice(i, endIdx + 1));
605
+ i = endIdx + 1;
606
+ continue;
607
+ }
608
+ }
609
+ if (text[i] === "*" || text[i] === "_") {
610
+ parts.push(text[i]);
611
+ i++;
612
+ continue;
613
+ }
614
+ if (text[i] === "[" || text[i] === "(") {
615
+ parts.push(text[i]);
616
+ i++;
617
+ continue;
618
+ }
619
+ if (text[i] === "]") {
620
+ parts.push(text[i]);
621
+ i++;
622
+ continue;
623
+ }
624
+ const char = text[i];
625
+ if (SPECIAL_CHARS.has(char)) {
626
+ parts.push(`\\${char}`);
627
+ } else {
628
+ parts.push(char);
629
+ }
630
+ i++;
631
+ }
632
+ return parts.join("");
633
+ }
634
+ function createEscapeMarkdownMiddleware() {
635
+ return async (message, next) => {
636
+ message.text = escapeMarkdownV2(message.text);
637
+ await next();
638
+ };
639
+ }
640
+
641
+ // ../channels/dist/middleware/split-message.js
642
+ var TELEGRAM_MAX = 4096;
643
+ function splitText(text, maxLength) {
644
+ if (text.length <= maxLength)
645
+ return [text];
646
+ const chunks = [];
647
+ let remaining = text;
648
+ let openCodeBlock = null;
649
+ while (remaining.length > 0) {
650
+ const prefix2 = openCodeBlock ? `${openCodeBlock}
651
+ ` : "";
652
+ const effectiveMax = maxLength - prefix2.length;
653
+ if (remaining.length <= effectiveMax) {
654
+ chunks.push(prefix2 + remaining);
655
+ break;
656
+ }
657
+ let splitAt = -1;
658
+ const searchArea = remaining.slice(0, effectiveMax);
659
+ const paraIdx = searchArea.lastIndexOf("\n\n");
660
+ if (paraIdx > effectiveMax * 0.3) {
661
+ splitAt = paraIdx;
662
+ }
663
+ if (splitAt === -1) {
664
+ const newlineIdx = searchArea.lastIndexOf("\n");
665
+ if (newlineIdx > effectiveMax * 0.3) {
666
+ splitAt = newlineIdx;
667
+ }
668
+ }
669
+ if (splitAt === -1) {
670
+ splitAt = effectiveMax;
671
+ }
672
+ let chunk = remaining.slice(0, splitAt);
673
+ remaining = remaining.slice(splitAt).replace(/^\n+/, "");
674
+ const fenceMatches = chunk.match(/```/g);
675
+ const fenceCount = fenceMatches ? fenceMatches.length : 0;
676
+ if (openCodeBlock) {
677
+ chunk = prefix2 + chunk;
678
+ if (fenceCount % 2 === 1) {
679
+ openCodeBlock = null;
680
+ } else {
681
+ chunk += "\n```";
682
+ }
683
+ } else {
684
+ if (fenceCount % 2 === 1) {
685
+ const lastFenceIdx = chunk.lastIndexOf("```");
686
+ const afterFence = chunk.slice(lastFenceIdx + 3);
687
+ const langMatch = afterFence.match(/^(\w*)/);
688
+ openCodeBlock = "```" + (langMatch?.[1] ?? "");
689
+ chunk += "\n```";
690
+ }
691
+ }
692
+ chunks.push(chunk);
693
+ }
694
+ return chunks.length === 0 ? [""] : chunks;
695
+ }
696
+ function createSplitMessageMiddleware(sendFn, maxLength = TELEGRAM_MAX) {
697
+ return async (message, next) => {
698
+ const chunks = splitText(message.text, maxLength);
699
+ if (chunks.length <= 1) {
700
+ await next();
701
+ return;
702
+ }
703
+ for (let i = 0; i < chunks.length; i++) {
704
+ await sendFn({
705
+ ...message,
706
+ text: chunks[i],
707
+ replyTo: i === 0 ? message.replyTo : void 0
708
+ });
709
+ if (i < chunks.length - 1) {
710
+ await new Promise((r) => setTimeout(r, 50));
711
+ }
712
+ }
713
+ };
714
+ }
715
+
716
+ // ../channels/dist/middleware/error-handler.js
717
+ function isRetryable(error) {
718
+ if (error instanceof Error) {
719
+ const err2 = error;
720
+ const status = err2.status ?? err2.error_code;
721
+ if (status === 429 || status !== void 0 && status >= 500)
722
+ return true;
723
+ if (status !== void 0 && status >= 400 && status < 500)
724
+ return false;
725
+ return true;
726
+ }
727
+ return false;
728
+ }
729
+ async function withRetry(fn, options) {
730
+ let lastError;
731
+ for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
732
+ try {
733
+ return await fn();
734
+ } catch (err2) {
735
+ lastError = err2;
736
+ if (!isRetryable(err2) || attempt === options.maxRetries) {
737
+ throw err2;
738
+ }
739
+ const delay = options.baseDelayMs * Math.pow(2, attempt);
740
+ await new Promise((r) => setTimeout(r, delay));
741
+ }
742
+ }
743
+ throw lastError;
744
+ }
745
+
746
+ // ../channels/dist/telegram/media.js
747
+ function registerMediaHandlers(bot, isAllowed, handlers, onRejected) {
748
+ bot.on("message:photo", async (ctx) => {
749
+ const userId = ctx.from.id;
750
+ if (!isAllowed(userId)) {
751
+ onRejected?.(userId, new Date(ctx.message.date * 1e3));
752
+ return;
753
+ }
754
+ const photos = ctx.message.photo;
755
+ const largest = photos[photos.length - 1];
756
+ const attachment = {
757
+ type: "photo",
758
+ fileId: largest.file_id,
759
+ caption: ctx.message.caption ?? void 0
760
+ };
761
+ const incoming = {
762
+ id: String(ctx.message.message_id),
763
+ channelType: "telegram",
764
+ userId: String(userId),
765
+ text: ctx.message.caption ?? "[Photo]",
766
+ timestamp: new Date(ctx.message.date * 1e3),
767
+ replyTo: ctx.message.reply_to_message ? String(ctx.message.reply_to_message.message_id) : void 0,
768
+ attachments: [attachment]
769
+ };
770
+ for (const handler of handlers) {
771
+ await handler(incoming);
772
+ }
773
+ });
774
+ bot.on("message:document", async (ctx) => {
775
+ const userId = ctx.from.id;
776
+ if (!isAllowed(userId)) {
777
+ onRejected?.(userId, new Date(ctx.message.date * 1e3));
778
+ return;
779
+ }
780
+ const doc = ctx.message.document;
781
+ const attachment = {
782
+ type: "document",
783
+ fileId: doc.file_id,
784
+ fileName: doc.file_name ?? void 0,
785
+ mimeType: doc.mime_type ?? void 0,
786
+ caption: ctx.message.caption ?? void 0
787
+ };
788
+ const incoming = {
789
+ id: String(ctx.message.message_id),
790
+ channelType: "telegram",
791
+ userId: String(userId),
792
+ text: ctx.message.caption ?? `[Document: ${doc.file_name ?? "unknown"}]`,
793
+ timestamp: new Date(ctx.message.date * 1e3),
794
+ replyTo: ctx.message.reply_to_message ? String(ctx.message.reply_to_message.message_id) : void 0,
795
+ attachments: [attachment]
796
+ };
797
+ for (const handler of handlers) {
798
+ await handler(incoming);
799
+ }
800
+ });
801
+ }
802
+
803
+ // ../channels/dist/telegram/telegram.js
533
804
  var TelegramChannel = class {
534
805
  type = "telegram";
535
806
  bot;
536
807
  allowedUsers;
537
808
  handlers = [];
809
+ sendPipeline;
538
810
  constructor(config) {
539
811
  this.bot = new Bot(config.botToken);
540
812
  this.allowedUsers = new Set(config.allowedUsers);
813
+ this.bot.catch((err2) => {
814
+ console.error("[augure:telegram] Bot error:", err2.message ?? err2);
815
+ });
541
816
  this.bot.on("message:text", async (ctx) => {
542
817
  const userId = ctx.from.id;
543
818
  if (!this.isUserAllowed(userId)) {
819
+ this.handleRejected(userId, ctx.message.date, config.rejectMessage);
544
820
  return;
545
821
  }
546
822
  const incoming = {
@@ -555,10 +831,35 @@ var TelegramChannel = class {
555
831
  await handler(incoming);
556
832
  }
557
833
  });
834
+ registerMediaHandlers(this.bot, (id) => this.isUserAllowed(id), this.handlers, (userId, ts) => this.handleRejected(userId, Math.floor(ts.getTime() / 1e3), config.rejectMessage));
835
+ const rawSend = async (msg) => {
836
+ await withRetry(() => this.bot.api.sendMessage(Number(msg.userId), msg.text, {
837
+ parse_mode: "MarkdownV2",
838
+ ...msg.replyTo ? { reply_parameters: { message_id: Number(msg.replyTo) } } : {}
839
+ }), { maxRetries: 3, baseDelayMs: 500 }).catch(async () => {
840
+ await this.bot.api.sendMessage(Number(msg.userId), msg.text, {
841
+ ...msg.replyTo ? { reply_parameters: { message_id: Number(msg.replyTo) } } : {}
842
+ }).catch((fallbackErr) => {
843
+ console.error("[augure:telegram] Fallback send also failed:", fallbackErr);
844
+ throw fallbackErr;
845
+ });
846
+ });
847
+ };
848
+ this.sendPipeline = createOutgoingPipeline([
849
+ createEscapeMarkdownMiddleware(),
850
+ createSplitMessageMiddleware(rawSend)
851
+ ], rawSend);
558
852
  }
559
853
  isUserAllowed(userId) {
560
854
  return this.allowedUsers.has(userId);
561
855
  }
856
+ handleRejected(userId, unixTimestamp, rejectMessage) {
857
+ console.warn(`[augure:telegram] Rejected message from unauthorized user ${userId} at ${new Date(unixTimestamp * 1e3).toISOString()}`);
858
+ if (rejectMessage) {
859
+ this.bot.api.sendMessage(userId, rejectMessage).catch(() => {
860
+ });
861
+ }
862
+ }
562
863
  onMessage(handler) {
563
864
  this.handlers.push(handler);
564
865
  }
@@ -569,10 +870,7 @@ var TelegramChannel = class {
569
870
  await this.bot.stop();
570
871
  }
571
872
  async send(message) {
572
- await this.bot.api.sendMessage(Number(message.userId), message.text, {
573
- parse_mode: "Markdown",
574
- ...message.replyTo ? { reply_parameters: { message_id: Number(message.replyTo) } } : {}
575
- });
873
+ await this.sendPipeline(message);
576
874
  }
577
875
  };
578
876
 
@@ -2870,8 +3168,150 @@ async function installBuiltins(manager) {
2870
3168
  }
2871
3169
  }
2872
3170
 
3171
+ // ../skills/dist/updater.js
3172
+ var SkillUpdater = class {
3173
+ config;
3174
+ constructor(config) {
3175
+ this.config = config;
3176
+ }
3177
+ /** Compare local skill versions with hub manifest */
3178
+ async checkForUpdates() {
3179
+ const [local, remote] = await Promise.all([
3180
+ this.config.manager.list(),
3181
+ this.config.hub.list()
3182
+ ]);
3183
+ const localMap = new Map(local.map((s) => [s.id, s.version]));
3184
+ const updates = [];
3185
+ for (const entry of remote) {
3186
+ const localVersion = localMap.get(entry.id);
3187
+ if (localVersion !== void 0 && entry.version > localVersion) {
3188
+ updates.push({
3189
+ id: entry.id,
3190
+ localVersion,
3191
+ hubVersion: entry.version
3192
+ });
3193
+ }
3194
+ }
3195
+ return updates;
3196
+ }
3197
+ /** Apply a single skill update with backup and rollback */
3198
+ async applyUpdate(skillId) {
3199
+ let backup;
3200
+ try {
3201
+ backup = await this.config.manager.get(skillId);
3202
+ } catch (err2) {
3203
+ return {
3204
+ skillId,
3205
+ success: false,
3206
+ fromVersion: 0,
3207
+ toVersion: 0,
3208
+ error: `Failed to backup: ${err2 instanceof Error ? err2.message : String(err2)}`
3209
+ };
3210
+ }
3211
+ const fromVersion = backup.meta.version;
3212
+ let newSkill;
3213
+ try {
3214
+ newSkill = await this.config.hub.download(skillId);
3215
+ } catch (err2) {
3216
+ return {
3217
+ skillId,
3218
+ success: false,
3219
+ fromVersion,
3220
+ toVersion: 0,
3221
+ error: `Failed to download: ${err2 instanceof Error ? err2.message : String(err2)}`
3222
+ };
3223
+ }
3224
+ const toVersion = newSkill.meta.version;
3225
+ const testResult = await this.config.tester.test(newSkill);
3226
+ if (testResult.success) {
3227
+ await this.config.manager.save(newSkill);
3228
+ return { skillId, success: true, fromVersion, toVersion };
3229
+ }
3230
+ await this.config.manager.save(backup);
3231
+ return {
3232
+ skillId,
3233
+ success: false,
3234
+ rolledBack: true,
3235
+ fromVersion,
3236
+ toVersion,
3237
+ error: `Update failed tests: ${testResult.error ?? "unknown"}`
3238
+ };
3239
+ }
3240
+ /** Check for updates and apply all available ones */
3241
+ async checkAndApply() {
3242
+ const updates = await this.checkForUpdates();
3243
+ const results = [];
3244
+ for (const update of updates) {
3245
+ try {
3246
+ const result = await this.applyUpdate(update.id);
3247
+ results.push(result);
3248
+ } catch (err2) {
3249
+ results.push({
3250
+ skillId: update.id,
3251
+ success: false,
3252
+ fromVersion: update.localVersion,
3253
+ toVersion: update.hubVersion,
3254
+ error: err2 instanceof Error ? err2.message : String(err2)
3255
+ });
3256
+ }
3257
+ }
3258
+ return results;
3259
+ }
3260
+ };
3261
+
2873
3262
  // ../core/dist/main.js
2874
3263
  import { resolve } from "path";
3264
+ import { createRequire } from "module";
3265
+
3266
+ // ../core/dist/version-checker.js
3267
+ var VersionChecker = class _VersionChecker {
3268
+ config;
3269
+ constructor(config) {
3270
+ this.config = config;
3271
+ }
3272
+ /** Check npm registry for latest version */
3273
+ async check() {
3274
+ try {
3275
+ const response = await fetch(`https://registry.npmjs.org/${this.config.packageName}/latest`);
3276
+ if (!response.ok) {
3277
+ return {
3278
+ updateAvailable: false,
3279
+ currentVersion: this.config.currentVersion,
3280
+ error: `npm registry returned ${response.status}`
3281
+ };
3282
+ }
3283
+ const data = await response.json();
3284
+ const latest = data.version;
3285
+ return {
3286
+ updateAvailable: _VersionChecker.compareVersions(this.config.currentVersion, latest) < 0,
3287
+ currentVersion: this.config.currentVersion,
3288
+ latestVersion: latest
3289
+ };
3290
+ } catch (err2) {
3291
+ return {
3292
+ updateAvailable: false,
3293
+ currentVersion: this.config.currentVersion,
3294
+ error: err2 instanceof Error ? err2.message : String(err2)
3295
+ };
3296
+ }
3297
+ }
3298
+ /** Compare two semver strings. Returns -1 if a < b, 0 if equal, 1 if a > b */
3299
+ static compareVersions(a, b) {
3300
+ const pa = a.split(".").map(Number);
3301
+ const pb = b.split(".").map(Number);
3302
+ for (let i = 0; i < 3; i++) {
3303
+ const va = pa[i] ?? 0;
3304
+ const vb = pb[i] ?? 0;
3305
+ if (va < vb)
3306
+ return -1;
3307
+ if (va > vb)
3308
+ return 1;
3309
+ }
3310
+ return 0;
3311
+ }
3312
+ };
3313
+
3314
+ // ../core/dist/main.js
2875
3315
  var SYSTEM_PROMPT = `You are Augure, a personal AI assistant. You are proactive, helpful, and concise.
2876
3316
  You speak the same language as the user. You have access to tools and persistent memory.
2877
3317
  Always be direct and actionable.`;
@@ -2886,6 +3326,7 @@ function resolveLLMClient(config, usage) {
2886
3326
  async function startAgent(configPath) {
2887
3327
  const config = await loadConfig(configPath);
2888
3328
  console.log(`[augure] Loaded config: ${config.identity.name}`);
3329
+ let telegram;
2889
3330
  const llm = resolveLLMClient(config.llm, "default");
2890
3331
  const ingestionLLM = resolveLLMClient(config.llm, "ingestion");
2891
3332
  const monitoringLLM = resolveLLMClient(config.llm, "monitoring");
@@ -2921,6 +3362,7 @@ async function startAgent(configPath) {
2921
3362
  });
2922
3363
  console.log(`[augure] Container pool created (max: ${config.security.maxConcurrentSandboxes})`);
2923
3364
  let skillManagerRef;
3365
+ let skillUpdater;
2924
3366
  if (config.skills) {
2925
3367
  const skillsPath = resolve(configPath, "..", config.skills.path);
2926
3368
  const codingLLM = resolveLLMClient(config.llm, "coding");
@@ -2956,6 +3398,26 @@ async function startAgent(configPath) {
2956
3398
  }
2957
3399
  const skillBridge = new SkillSchedulerBridge(scheduler, skillManager);
2958
3400
  await skillBridge.syncAll();
3401
+ if (hub && config.updates?.skills?.enabled !== false) {
3402
+ skillUpdater = new SkillUpdater({
3403
+ manager: skillManager,
3404
+ hub,
3405
+ tester: skillTester
3406
+ });
3407
+ try {
3408
+ const updateResults = await skillUpdater.checkAndApply();
3409
+ const updated = updateResults.filter((r) => r.success);
3410
+ const failed = updateResults.filter((r) => !r.success);
3411
+ if (updated.length > 0) {
3412
+ console.log(`[augure] Skills updated: ${updated.map((r) => `${r.skillId} (v${r.fromVersion}\u2192v${r.toVersion})`).join(", ")}`);
3413
+ }
3414
+ if (failed.length > 0) {
3415
+ console.log(`[augure] Skill updates failed: ${failed.map((r) => `${r.skillId}: ${r.error}`).join(", ")}`);
3416
+ }
3417
+ } catch (err2) {
3418
+ console.error("[augure] Skill update check failed:", err2);
3419
+ }
3420
+ }
2959
3421
  skillManagerRef = skillManager;
2960
3422
  console.log(`[augure] Skills system initialized at ${skillsPath}`);
2961
3423
  }
@@ -2971,6 +3433,19 @@ async function startAgent(configPath) {
2971
3433
  await personaResolver.loadAll();
2972
3434
  console.log(`[augure] Personas loaded from ${personaPath}`);
2973
3435
  }
3436
+ if (config.updates?.cli?.enabled !== false) {
3437
+ const require3 = createRequire(import.meta.url);
3438
+ const { version: version2 } = require3("augure/package.json");
3439
+ const versionChecker = new VersionChecker({
3440
+ currentVersion: version2,
3441
+ packageName: "augure",
3442
+ githubRepo: "FaureAlexis/augure"
3443
+ });
3444
+ const versionResult = await versionChecker.check();
3445
+ if (versionResult.updateAvailable) {
3446
+ console.log(`[augure] Update available: v${versionResult.latestVersion} (current: v${versionResult.currentVersion}). Run: npm update -g augure`);
3447
+ }
3448
+ }
2974
3449
  const guard = new ContextGuard({
2975
3450
  maxContextTokens: 2e5,
2976
3451
  reservedForOutput: config.llm.default.maxTokens ?? 8192
@@ -2987,22 +3462,24 @@ async function startAgent(configPath) {
2987
3462
  modelName: config.llm.default.model
2988
3463
  });
2989
3464
  if (config.channels.telegram?.enabled) {
2990
- const telegram = new TelegramChannel({
3465
+ telegram = new TelegramChannel({
2991
3466
  botToken: config.channels.telegram.botToken,
2992
- allowedUsers: config.channels.telegram.allowedUsers
3467
+ allowedUsers: config.channels.telegram.allowedUsers,
3468
+ rejectMessage: config.channels.telegram.rejectMessage
2993
3469
  });
3470
+ const tg = telegram;
2994
3471
  const commandCtx = {
2995
3472
  scheduler,
2996
3473
  pool,
2997
3474
  agent,
2998
3475
  skillManager: skillManagerRef
2999
3476
  };
3000
- telegram.onMessage(async (msg) => {
3477
+ tg.onMessage(async (msg) => {
3001
3478
  console.log(`[augure] Message from ${msg.userId}: ${msg.text}`);
3002
3479
  try {
3003
3480
  const cmdResult = await handleCommand(msg.text, commandCtx);
3004
3481
  if (cmdResult.handled) {
3005
- await telegram.send({
3482
+ await tg.send({
3006
3483
  channelType: "telegram",
3007
3484
  userId: msg.userId,
3008
3485
  text: cmdResult.response ?? "OK",
@@ -3014,7 +3491,7 @@ async function startAgent(configPath) {
3014
3491
  agent.setPersona(personaResolver.resolve(msg.text));
3015
3492
  }
3016
3493
  const response = await agent.handleMessage(msg);
3017
- await telegram.send({
3494
+ await tg.send({
3018
3495
  channelType: "telegram",
3019
3496
  userId: msg.userId,
3020
3497
  text: response,
@@ -3022,14 +3499,14 @@ async function startAgent(configPath) {
3022
3499
  });
3023
3500
  } catch (err2) {
3024
3501
  console.error("[augure] Error handling message:", err2);
3025
- await telegram.send({
3502
+ await tg.send({
3026
3503
  channelType: "telegram",
3027
3504
  userId: msg.userId,
3028
3505
  text: "An error occurred while processing your message."
3029
3506
  });
3030
3507
  }
3031
3508
  });
3032
- await telegram.start();
3509
+ await tg.start();
3033
3510
  console.log("[augure] Telegram bot started. Waiting for messages...");
3034
3511
  }
3035
3512
  const heartbeatIntervalMs = parseInterval(config.scheduler.heartbeatInterval);
@@ -3052,10 +3529,57 @@ async function startAgent(configPath) {
3052
3529
  scheduler.start();
3053
3530
  heartbeat.start();
3054
3531
  console.log(`[augure] Scheduler started with ${scheduler.listJobs().length} jobs. Heartbeat every ${config.scheduler.heartbeatInterval}.`);
3532
+ if (skillUpdater && config.updates?.skills?.checkInterval) {
3533
+ const skillCheckMs = parseInterval(config.updates.skills.checkInterval);
3534
+ setInterval(async () => {
3535
+ try {
3536
+ const results = await skillUpdater.checkAndApply();
3537
+ for (const r of results) {
3538
+ if (r.success) {
3539
+ console.log(`[augure] Skill auto-updated: ${r.skillId} v${r.fromVersion}\u2192v${r.toVersion}`);
3540
+ } else if (r.rolledBack) {
3541
+ console.log(`[augure] Skill update rolled back: ${r.skillId} - ${r.error}`);
3542
+ }
3543
+ }
3544
+ } catch (err2) {
3545
+ console.error("[augure] Periodic skill update check failed:", err2);
3546
+ }
3547
+ }, skillCheckMs);
3548
+ }
3549
+ if (config.updates?.cli?.enabled !== false && config.channels.telegram?.enabled) {
3550
+ const cliCheckMs = parseInterval(config.updates?.cli?.checkInterval ?? "24h");
3551
+ const require3 = createRequire(import.meta.url);
3552
+ const { version: version2 } = require3("augure/package.json");
3553
+ const versionChecker = new VersionChecker({
3554
+ currentVersion: version2,
3555
+ packageName: "augure",
3556
+ githubRepo: "FaureAlexis/augure"
3557
+ });
3558
+ setInterval(async () => {
3559
+ try {
3560
+ const result = await versionChecker.check();
3561
+ if (result.updateAvailable && telegram) {
3562
+ const userId = config.channels.telegram?.allowedUsers[0];
3563
+ if (userId !== void 0) {
3564
+ await telegram.send({
3565
+ channelType: "telegram",
3566
+ userId: String(userId),
3567
+ text: `Update available: Augure v${result.latestVersion} (current: v${result.currentVersion}).
3568
+ Run: \`npm update -g augure\``
3569
+ });
3570
+ }
3571
+ }
3572
+ } catch (err2) {
3573
+ console.error("[augure] CLI version check failed:", err2);
3574
+ }
3575
+ }, cliCheckMs);
3576
+ }
3055
3577
  const shutdown = async () => {
3056
3578
  console.log("\n[augure] Shutting down...");
3057
3579
  heartbeat.stop();
3058
3580
  scheduler.stop();
3581
+ if (telegram)
3582
+ await telegram.stop();
3059
3583
  await pool.destroyAll();
3060
3584
  await audit.close();
3061
3585
  console.log("[augure] All containers destroyed");
@@ -3087,10 +3611,21 @@ var startCommand = defineCommand({
3087
3611
  description: "Path to config file",
3088
3612
  alias: "c",
3089
3613
  default: "./augure.json5"
3614
+ },
3615
+ env: {
3616
+ type: "string",
3617
+ description: "Path to .env file",
3618
+ alias: "e"
3090
3619
  }
3091
3620
  },
3092
3621
  async run({ args }) {
3093
3622
  const configPath = resolve2(args.config);
3623
+ const envPath = args.env ? resolve2(args.env) : join6(dirname4(configPath), ".env");
3624
+ try {
3625
+ process.loadEnvFile(envPath);
3626
+ console.log(`${prefix} Loaded env from ${dim(envPath)}`);
3627
+ } catch {
3628
+ }
3094
3629
  console.log(`${prefix} Starting with config: ${dim(configPath)}`);
3095
3630
  try {
3096
3631
  await startAgent(configPath);
@@ -3442,7 +3977,7 @@ var skillsCommand = defineCommand3({
3442
3977
  });
3443
3978
 
3444
3979
  // src/bin.ts
3445
- var require2 = createRequire(import.meta.url);
3980
+ var require2 = createRequire2(import.meta.url);
3446
3981
  var { version } = require2("../package.json");
3447
3982
  var main = defineCommand4({
3448
3983
  meta: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "augure",
3
- "version": "0.3.0",
3
+ "version": "0.4.2",
4
4
  "description": "Augure — your proactive AI agent",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,15 @@
12
12
  "engines": {
13
13
  "node": ">=22.0.0"
14
14
  },
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "test": "vitest run",
19
+ "test:unit": "vitest run",
20
+ "typecheck": "tsc --noEmit",
21
+ "lint": "eslint src/",
22
+ "clean": "rm -rf dist .turbo"
23
+ },
15
24
  "dependencies": {
16
25
  "citty": "^0.2.1",
17
26
  "dockerode": "^4.0.9",
@@ -24,17 +33,17 @@
24
33
  "zod": "^4.3.6"
25
34
  },
26
35
  "devDependencies": {
36
+ "@augure/channels": "workspace:*",
37
+ "@augure/core": "workspace:*",
38
+ "@augure/memory": "workspace:*",
39
+ "@augure/sandbox": "workspace:*",
40
+ "@augure/scheduler": "workspace:*",
41
+ "@augure/skills": "workspace:*",
42
+ "@augure/tools": "workspace:*",
43
+ "@augure/types": "workspace:*",
27
44
  "@types/dockerode": "^4.0.1",
28
45
  "@types/node-cron": "^3.0.11",
29
- "tsup": "^8.5.1",
30
- "@augure/channels": "0.0.1",
31
- "@augure/memory": "0.0.1",
32
- "@augure/sandbox": "0.0.1",
33
- "@augure/core": "0.0.1",
34
- "@augure/scheduler": "0.0.1",
35
- "@augure/skills": "0.0.1",
36
- "@augure/types": "0.0.1",
37
- "@augure/tools": "0.0.1"
46
+ "tsup": "^8.5.1"
38
47
  },
39
48
  "keywords": [
40
49
  "ai",
@@ -52,14 +61,5 @@
52
61
  },
53
62
  "publishConfig": {
54
63
  "access": "public"
55
- },
56
- "scripts": {
57
- "build": "tsup",
58
- "dev": "tsup --watch",
59
- "test": "vitest run",
60
- "test:unit": "vitest run",
61
- "typecheck": "tsc --noEmit",
62
- "lint": "eslint src/",
63
- "clean": "rm -rf dist .turbo"
64
64
  }
65
- }
65
+ }