@okrlinkhub/agent-factory 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,6 +25,30 @@ const queueStatusValidator = v.union(
25
25
  v.literal("dead_letter"),
26
26
  );
27
27
 
28
+ const telegramAttachmentKindValidator = v.union(
29
+ v.literal("photo"),
30
+ v.literal("video"),
31
+ v.literal("audio"),
32
+ v.literal("voice"),
33
+ v.literal("document"),
34
+ );
35
+
36
+ const telegramAttachmentStatusValidator = v.union(
37
+ v.literal("ready"),
38
+ v.literal("expired"),
39
+ );
40
+
41
+ const telegramAttachmentValidator = v.object({
42
+ kind: telegramAttachmentKindValidator,
43
+ status: telegramAttachmentStatusValidator,
44
+ storageId: v.id("_storage"),
45
+ telegramFileId: v.string(),
46
+ fileName: v.optional(v.string()),
47
+ mimeType: v.optional(v.string()),
48
+ sizeBytes: v.optional(v.number()),
49
+ expiresAt: v.number(),
50
+ });
51
+
28
52
  const queuePayloadValidator = v.object({
29
53
  provider: v.string(),
30
54
  providerUserId: v.string(),
@@ -32,6 +56,7 @@ const queuePayloadValidator = v.object({
32
56
  externalMessageId: v.optional(v.string()),
33
57
  rawUpdateJson: v.optional(v.string()),
34
58
  metadata: v.optional(v.record(v.string(), v.string())),
59
+ attachments: v.optional(v.array(telegramAttachmentValidator)),
35
60
  });
36
61
 
37
62
  const snapshotReasonValidator = v.union(
@@ -155,6 +180,7 @@ const workerSpawnOpenClawEnvValidator = v.object({
155
180
 
156
181
  const messageRuntimeConfigValidator = v.object({
157
182
  systemPrompt: v.optional(v.string()),
183
+ telegramAttachmentRetentionMs: v.optional(v.number()),
158
184
  });
159
185
 
160
186
  const globalSkillStatusValidator = v.union(v.literal("active"), v.literal("disabled"));
@@ -192,6 +218,8 @@ const RUNTIME_CONFIG_KEYS = {
192
218
  message: "message",
193
219
  } as const;
194
220
 
221
+ const DEFAULT_TELEGRAM_ATTACHMENT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
222
+
195
223
  export const enqueueMessage = mutation({
196
224
  args: {
197
225
  conversationId: v.string(),
@@ -533,6 +561,33 @@ export const getMessageRuntimeConfig = internalQuery({
533
561
  },
534
562
  });
535
563
 
564
+ export const getTelegramIngressRuntimeConfig = internalQuery({
565
+ args: {
566
+ agentKey: v.string(),
567
+ },
568
+ returns: v.object({
569
+ botToken: v.union(v.null(), v.string()),
570
+ attachmentRetentionMs: v.number(),
571
+ }),
572
+ handler: async (ctx, args) => {
573
+ const profile = await ctx.db
574
+ .query("agentProfiles")
575
+ .withIndex("by_agentKey", (q) => q.eq("agentKey", args.agentKey))
576
+ .unique();
577
+ const botToken = profile ? await resolveActiveTelegramBotToken(ctx, profile.secretsRef) : null;
578
+ const row = await ctx.db
579
+ .query("runtimeConfig")
580
+ .withIndex("by_key", (q) => q.eq("key", RUNTIME_CONFIG_KEYS.message))
581
+ .unique();
582
+ return {
583
+ botToken,
584
+ attachmentRetentionMs: resolveTelegramAttachmentRetentionMs(
585
+ row?.messageConfig?.telegramAttachmentRetentionMs,
586
+ ),
587
+ };
588
+ },
589
+ });
590
+
536
591
  export const upsertMessageRuntimeConfig = internalMutation({
537
592
  args: {
538
593
  messageConfig: messageRuntimeConfigValidator,
@@ -1694,22 +1749,7 @@ export const getHydrationBundleForClaimedJob = query({
1694
1749
  .withIndex("by_conversationId", (q) => q.eq("conversationId", message.conversationId))
1695
1750
  .first();
1696
1751
 
1697
- let telegramBotToken: string | null = null;
1698
- const telegramSecretRefs = profile.secretsRef.filter(
1699
- (ref) => ref === "telegram.botToken" || ref.startsWith("telegram.botToken."),
1700
- );
1701
- for (const telegramSecretRef of telegramSecretRefs) {
1702
- const activeSecret = await ctx.db
1703
- .query("secrets")
1704
- .withIndex("by_secretRef_and_active", (q) =>
1705
- q.eq("secretRef", telegramSecretRef).eq("active", true),
1706
- )
1707
- .unique();
1708
- if (activeSecret) {
1709
- telegramBotToken = decryptSecretValue(activeSecret.encryptedValue, activeSecret.algorithm);
1710
- break;
1711
- }
1712
- }
1752
+ const telegramBotToken = await resolveActiveTelegramBotToken(ctx, profile.secretsRef);
1713
1753
 
1714
1754
  const contextHistory =
1715
1755
  conversationCache && conversationCache.snapshotKey === snapshotKey
@@ -2424,6 +2464,49 @@ export const expireOldDataSnapshots = internalMutation({
2424
2464
  },
2425
2465
  });
2426
2466
 
2467
+ export const expireOldTelegramAttachments = internalMutation({
2468
+ args: {
2469
+ nowMs: v.optional(v.number()),
2470
+ limit: v.optional(v.number()),
2471
+ },
2472
+ returns: v.number(),
2473
+ handler: async (ctx, args) => {
2474
+ const nowMs = args.nowMs ?? Date.now();
2475
+ const limit = args.limit ?? 100;
2476
+ const rows = await ctx.db
2477
+ .query("messageAttachments")
2478
+ .withIndex("by_status_and_expiresAt", (q) =>
2479
+ q.eq("status", "ready").lte("expiresAt", nowMs),
2480
+ )
2481
+ .take(limit);
2482
+ for (const row of rows) {
2483
+ await ctx.db.patch(row._id, {
2484
+ status: "expired",
2485
+ });
2486
+ const message = await ctx.db.get(row.messageId);
2487
+ if (message?.payload.attachments?.length) {
2488
+ await ctx.db.patch(message._id, {
2489
+ payload: {
2490
+ ...message.payload,
2491
+ attachments: message.payload.attachments.map((attachment) =>
2492
+ attachment.storageId === row.storageId
2493
+ ? {
2494
+ ...attachment,
2495
+ status: "expired" as const,
2496
+ }
2497
+ : attachment,
2498
+ ),
2499
+ },
2500
+ });
2501
+ }
2502
+ await (ctx.storage as { delete?: (storageId: Id<"_storage">) => Promise<void> }).delete?.(
2503
+ row.storageId,
2504
+ );
2505
+ }
2506
+ return rows.length;
2507
+ },
2508
+ });
2509
+
2427
2510
  export const getWorkerStats = query({
2428
2511
  args: {},
2429
2512
  returns: v.object({
@@ -2638,6 +2721,16 @@ async function enqueueMessageRecord(
2638
2721
  externalMessageId?: string;
2639
2722
  rawUpdateJson?: string;
2640
2723
  metadata?: Record<string, string>;
2724
+ attachments?: Array<{
2725
+ kind: "photo" | "video" | "audio" | "voice" | "document";
2726
+ status: "ready" | "expired";
2727
+ storageId: Id<"_storage">;
2728
+ telegramFileId: string;
2729
+ fileName?: string;
2730
+ mimeType?: string;
2731
+ sizeBytes?: number;
2732
+ expiresAt: number;
2733
+ }>;
2641
2734
  };
2642
2735
  priority?: number;
2643
2736
  scheduledFor?: number;
@@ -2709,6 +2802,23 @@ async function enqueueMessageRecord(
2709
2802
  attempts: 0,
2710
2803
  maxAttempts: args.maxAttempts ?? DEFAULT_CONFIG.retry.maxAttempts,
2711
2804
  });
2805
+ for (const attachment of payload.attachments ?? []) {
2806
+ await ctx.db.insert("messageAttachments", {
2807
+ messageId,
2808
+ conversationId: args.conversationId,
2809
+ agentKey: args.agentKey,
2810
+ provider: payload.provider,
2811
+ kind: attachment.kind,
2812
+ status: attachment.status,
2813
+ storageId: attachment.storageId,
2814
+ telegramFileId: attachment.telegramFileId,
2815
+ fileName: attachment.fileName,
2816
+ mimeType: attachment.mimeType,
2817
+ sizeBytes: attachment.sizeBytes,
2818
+ createdAt: nowMs,
2819
+ expiresAt: attachment.expiresAt,
2820
+ });
2821
+ }
2712
2822
  try {
2713
2823
  await ctx.scheduler.runAfter(0, (internal.scheduler as any).reconcileWorkerPoolFromEnqueue, {
2714
2824
  workspaceId: "default",
@@ -2970,13 +3080,27 @@ function dedupeMessagesById<T extends { _id: string }>(messages: Array<T>): Arra
2970
3080
  }
2971
3081
 
2972
3082
  function normalizeMessageRuntimeConfig(
2973
- messageConfig: { systemPrompt?: string } | null | undefined,
2974
- ): { systemPrompt?: string } | null {
3083
+ messageConfig:
3084
+ | {
3085
+ systemPrompt?: string;
3086
+ telegramAttachmentRetentionMs?: number;
3087
+ }
3088
+ | null
3089
+ | undefined,
3090
+ ): { systemPrompt?: string; telegramAttachmentRetentionMs?: number } | null {
2975
3091
  const systemPrompt = normalizeSystemPrompt(messageConfig?.systemPrompt);
2976
- if (systemPrompt === null) {
3092
+ const telegramAttachmentRetentionMs = normalizeTelegramAttachmentRetentionMs(
3093
+ messageConfig?.telegramAttachmentRetentionMs,
3094
+ );
3095
+ if (systemPrompt === null && telegramAttachmentRetentionMs === undefined) {
2977
3096
  return null;
2978
3097
  }
2979
- return { systemPrompt };
3098
+ return {
3099
+ ...(systemPrompt === null ? {} : { systemPrompt }),
3100
+ ...(telegramAttachmentRetentionMs === undefined
3101
+ ? {}
3102
+ : { telegramAttachmentRetentionMs }),
3103
+ };
2980
3104
  }
2981
3105
 
2982
3106
  function normalizeSystemPrompt(systemPrompt?: string | null): string | null {
@@ -2987,6 +3111,47 @@ function normalizeSystemPrompt(systemPrompt?: string | null): string | null {
2987
3111
  return normalizedSystemPrompt.length > 0 ? normalizedSystemPrompt : null;
2988
3112
  }
2989
3113
 
3114
+ function normalizeTelegramAttachmentRetentionMs(
3115
+ retentionMs?: number | null,
3116
+ ): number | undefined {
3117
+ if (typeof retentionMs !== "number" || !Number.isFinite(retentionMs)) {
3118
+ return undefined;
3119
+ }
3120
+ const normalizedRetentionMs = Math.floor(retentionMs);
3121
+ if (normalizedRetentionMs <= 0) {
3122
+ return undefined;
3123
+ }
3124
+ return normalizedRetentionMs;
3125
+ }
3126
+
3127
+ function resolveTelegramAttachmentRetentionMs(retentionMs?: number | null): number {
3128
+ return (
3129
+ normalizeTelegramAttachmentRetentionMs(retentionMs) ??
3130
+ DEFAULT_TELEGRAM_ATTACHMENT_RETENTION_MS
3131
+ );
3132
+ }
3133
+
3134
+ async function resolveActiveTelegramBotToken(
3135
+ ctx: QueryCtx,
3136
+ secretRefs: Array<string>,
3137
+ ): Promise<string | null> {
3138
+ const telegramSecretRefs = secretRefs.filter(
3139
+ (ref) => ref === "telegram.botToken" || ref.startsWith("telegram.botToken."),
3140
+ );
3141
+ for (const telegramSecretRef of telegramSecretRefs) {
3142
+ const activeSecret = await ctx.db
3143
+ .query("secrets")
3144
+ .withIndex("by_secretRef_and_active", (q) =>
3145
+ q.eq("secretRef", telegramSecretRef).eq("active", true),
3146
+ )
3147
+ .unique();
3148
+ if (activeSecret) {
3149
+ return decryptSecretValue(activeSecret.encryptedValue, activeSecret.algorithm);
3150
+ }
3151
+ }
3152
+ return null;
3153
+ }
3154
+
2990
3155
  async function buildGlobalSkillMaterialization(skill: {
2991
3156
  slug: string;
2992
3157
  version: string;
@@ -206,6 +206,10 @@ async function runReconcileWorkerPool(
206
206
  nowMs,
207
207
  limit: 100,
208
208
  });
209
+ await ctx.runMutation((internal.queue as any).expireOldTelegramAttachments, {
210
+ nowMs,
211
+ limit: 100,
212
+ });
209
213
 
210
214
  return {
211
215
  activeWorkers: cycle.activeWorkers,
@@ -72,6 +72,26 @@ export default defineSchema({
72
72
  externalMessageId: v.optional(v.string()),
73
73
  rawUpdateJson: v.optional(v.string()),
74
74
  metadata: v.optional(v.record(v.string(), v.string())),
75
+ attachments: v.optional(
76
+ v.array(
77
+ v.object({
78
+ kind: v.union(
79
+ v.literal("photo"),
80
+ v.literal("video"),
81
+ v.literal("audio"),
82
+ v.literal("voice"),
83
+ v.literal("document"),
84
+ ),
85
+ status: v.union(v.literal("ready"), v.literal("expired")),
86
+ storageId: v.id("_storage"),
87
+ telegramFileId: v.string(),
88
+ fileName: v.optional(v.string()),
89
+ mimeType: v.optional(v.string()),
90
+ sizeBytes: v.optional(v.number()),
91
+ expiresAt: v.number(),
92
+ }),
93
+ ),
94
+ ),
75
95
  }),
76
96
  status: v.union(
77
97
  v.literal("queued"),
@@ -152,11 +172,37 @@ export default defineSchema({
152
172
  messageConfig: v.optional(
153
173
  v.object({
154
174
  systemPrompt: v.optional(v.string()),
175
+ telegramAttachmentRetentionMs: v.optional(v.number()),
155
176
  }),
156
177
  ),
157
178
  updatedAt: v.number(),
158
179
  }).index("by_key", ["key"]),
159
180
 
181
+ messageAttachments: defineTable({
182
+ messageId: v.id("messageQueue"),
183
+ conversationId: v.string(),
184
+ agentKey: v.string(),
185
+ provider: v.string(),
186
+ kind: v.union(
187
+ v.literal("photo"),
188
+ v.literal("video"),
189
+ v.literal("audio"),
190
+ v.literal("voice"),
191
+ v.literal("document"),
192
+ ),
193
+ status: v.union(v.literal("ready"), v.literal("expired")),
194
+ storageId: v.id("_storage"),
195
+ telegramFileId: v.string(),
196
+ fileName: v.optional(v.string()),
197
+ mimeType: v.optional(v.string()),
198
+ sizeBytes: v.optional(v.number()),
199
+ createdAt: v.number(),
200
+ expiresAt: v.number(),
201
+ })
202
+ .index("by_messageId", ["messageId"])
203
+ .index("by_status_and_expiresAt", ["status", "expiresAt"])
204
+ .index("by_conversationId_and_createdAt", ["conversationId", "createdAt"]),
205
+
160
206
  dataSnapshots: defineTable({
161
207
  workspaceId: v.string(),
162
208
  agentKey: v.string(),