@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.
@@ -4,6 +4,18 @@ import { internalMutation, internalQuery, mutation, query, } from "./_generated/
4
4
  import { computeRetryDelayMs, DEFAULT_CONFIG, providerConfigValidator } from "./config.js";
5
5
  import { canTransitionWorkerStatus, isWorkerClaimable, isWorkerRunning, isWorkerTerminal, workerStatusValidator, } from "./workerLifecycle.js";
6
6
  const queueStatusValidator = v.union(v.literal("queued"), v.literal("processing"), v.literal("done"), v.literal("failed"), v.literal("dead_letter"));
7
+ const telegramAttachmentKindValidator = v.union(v.literal("photo"), v.literal("video"), v.literal("audio"), v.literal("voice"), v.literal("document"));
8
+ const telegramAttachmentStatusValidator = v.union(v.literal("ready"), v.literal("expired"));
9
+ const telegramAttachmentValidator = v.object({
10
+ kind: telegramAttachmentKindValidator,
11
+ status: telegramAttachmentStatusValidator,
12
+ storageId: v.id("_storage"),
13
+ telegramFileId: v.string(),
14
+ fileName: v.optional(v.string()),
15
+ mimeType: v.optional(v.string()),
16
+ sizeBytes: v.optional(v.number()),
17
+ expiresAt: v.number(),
18
+ });
7
19
  const queuePayloadValidator = v.object({
8
20
  provider: v.string(),
9
21
  providerUserId: v.string(),
@@ -11,6 +23,7 @@ const queuePayloadValidator = v.object({
11
23
  externalMessageId: v.optional(v.string()),
12
24
  rawUpdateJson: v.optional(v.string()),
13
25
  metadata: v.optional(v.record(v.string(), v.string())),
26
+ attachments: v.optional(v.array(telegramAttachmentValidator)),
14
27
  });
15
28
  const snapshotReasonValidator = v.union(v.literal("drain"), v.literal("signal"), v.literal("manual"));
16
29
  const DATA_SNAPSHOT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
@@ -102,6 +115,7 @@ const workerSpawnOpenClawEnvValidator = v.object({
102
115
  });
103
116
  const messageRuntimeConfigValidator = v.object({
104
117
  systemPrompt: v.optional(v.string()),
118
+ telegramAttachmentRetentionMs: v.optional(v.number()),
105
119
  });
106
120
  const globalSkillStatusValidator = v.union(v.literal("active"), v.literal("disabled"));
107
121
  const globalSkillReleaseChannelValidator = v.union(v.literal("stable"), v.literal("canary"));
@@ -133,6 +147,7 @@ const RUNTIME_CONFIG_KEYS = {
133
147
  provider: "provider",
134
148
  message: "message",
135
149
  };
150
+ const DEFAULT_TELEGRAM_ATTACHMENT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
136
151
  export const enqueueMessage = mutation({
137
152
  args: {
138
153
  conversationId: v.string(),
@@ -435,6 +450,30 @@ export const getMessageRuntimeConfig = internalQuery({
435
450
  return row.messageConfig;
436
451
  },
437
452
  });
453
+ export const getTelegramIngressRuntimeConfig = internalQuery({
454
+ args: {
455
+ agentKey: v.string(),
456
+ },
457
+ returns: v.object({
458
+ botToken: v.union(v.null(), v.string()),
459
+ attachmentRetentionMs: v.number(),
460
+ }),
461
+ handler: async (ctx, args) => {
462
+ const profile = await ctx.db
463
+ .query("agentProfiles")
464
+ .withIndex("by_agentKey", (q) => q.eq("agentKey", args.agentKey))
465
+ .unique();
466
+ const botToken = profile ? await resolveActiveTelegramBotToken(ctx, profile.secretsRef) : null;
467
+ const row = await ctx.db
468
+ .query("runtimeConfig")
469
+ .withIndex("by_key", (q) => q.eq("key", RUNTIME_CONFIG_KEYS.message))
470
+ .unique();
471
+ return {
472
+ botToken,
473
+ attachmentRetentionMs: resolveTelegramAttachmentRetentionMs(row?.messageConfig?.telegramAttachmentRetentionMs),
474
+ };
475
+ },
476
+ });
438
477
  export const upsertMessageRuntimeConfig = internalMutation({
439
478
  args: {
440
479
  messageConfig: messageRuntimeConfigValidator,
@@ -1407,18 +1446,7 @@ export const getHydrationBundleForClaimedJob = query({
1407
1446
  .query("conversationHydrationCache")
1408
1447
  .withIndex("by_conversationId", (q) => q.eq("conversationId", message.conversationId))
1409
1448
  .first();
1410
- let telegramBotToken = null;
1411
- const telegramSecretRefs = profile.secretsRef.filter((ref) => ref === "telegram.botToken" || ref.startsWith("telegram.botToken."));
1412
- for (const telegramSecretRef of telegramSecretRefs) {
1413
- const activeSecret = await ctx.db
1414
- .query("secrets")
1415
- .withIndex("by_secretRef_and_active", (q) => q.eq("secretRef", telegramSecretRef).eq("active", true))
1416
- .unique();
1417
- if (activeSecret) {
1418
- telegramBotToken = decryptSecretValue(activeSecret.encryptedValue, activeSecret.algorithm);
1419
- break;
1420
- }
1421
- }
1449
+ const telegramBotToken = await resolveActiveTelegramBotToken(ctx, profile.secretsRef);
1422
1450
  const contextHistory = conversationCache && conversationCache.snapshotKey === snapshotKey
1423
1451
  ? conversationCache.deltaContext
1424
1452
  : conversation.contextHistory;
@@ -2050,6 +2078,42 @@ export const expireOldDataSnapshots = internalMutation({
2050
2078
  return rows.length;
2051
2079
  },
2052
2080
  });
2081
+ export const expireOldTelegramAttachments = internalMutation({
2082
+ args: {
2083
+ nowMs: v.optional(v.number()),
2084
+ limit: v.optional(v.number()),
2085
+ },
2086
+ returns: v.number(),
2087
+ handler: async (ctx, args) => {
2088
+ const nowMs = args.nowMs ?? Date.now();
2089
+ const limit = args.limit ?? 100;
2090
+ const rows = await ctx.db
2091
+ .query("messageAttachments")
2092
+ .withIndex("by_status_and_expiresAt", (q) => q.eq("status", "ready").lte("expiresAt", nowMs))
2093
+ .take(limit);
2094
+ for (const row of rows) {
2095
+ await ctx.db.patch(row._id, {
2096
+ status: "expired",
2097
+ });
2098
+ const message = await ctx.db.get(row.messageId);
2099
+ if (message?.payload.attachments?.length) {
2100
+ await ctx.db.patch(message._id, {
2101
+ payload: {
2102
+ ...message.payload,
2103
+ attachments: message.payload.attachments.map((attachment) => attachment.storageId === row.storageId
2104
+ ? {
2105
+ ...attachment,
2106
+ status: "expired",
2107
+ }
2108
+ : attachment),
2109
+ },
2110
+ });
2111
+ }
2112
+ await ctx.storage.delete?.(row.storageId);
2113
+ }
2114
+ return rows.length;
2115
+ },
2116
+ });
2053
2117
  export const getWorkerStats = query({
2054
2118
  args: {},
2055
2119
  returns: v.object({
@@ -2218,6 +2282,23 @@ async function enqueueMessageRecord(ctx, args) {
2218
2282
  attempts: 0,
2219
2283
  maxAttempts: args.maxAttempts ?? DEFAULT_CONFIG.retry.maxAttempts,
2220
2284
  });
2285
+ for (const attachment of payload.attachments ?? []) {
2286
+ await ctx.db.insert("messageAttachments", {
2287
+ messageId,
2288
+ conversationId: args.conversationId,
2289
+ agentKey: args.agentKey,
2290
+ provider: payload.provider,
2291
+ kind: attachment.kind,
2292
+ status: attachment.status,
2293
+ storageId: attachment.storageId,
2294
+ telegramFileId: attachment.telegramFileId,
2295
+ fileName: attachment.fileName,
2296
+ mimeType: attachment.mimeType,
2297
+ sizeBytes: attachment.sizeBytes,
2298
+ createdAt: nowMs,
2299
+ expiresAt: attachment.expiresAt,
2300
+ });
2301
+ }
2221
2302
  try {
2222
2303
  await ctx.scheduler.runAfter(0, internal.scheduler.reconcileWorkerPoolFromEnqueue, {
2223
2304
  workspaceId: "default",
@@ -2354,10 +2435,16 @@ function dedupeMessagesById(messages) {
2354
2435
  }
2355
2436
  function normalizeMessageRuntimeConfig(messageConfig) {
2356
2437
  const systemPrompt = normalizeSystemPrompt(messageConfig?.systemPrompt);
2357
- if (systemPrompt === null) {
2438
+ const telegramAttachmentRetentionMs = normalizeTelegramAttachmentRetentionMs(messageConfig?.telegramAttachmentRetentionMs);
2439
+ if (systemPrompt === null && telegramAttachmentRetentionMs === undefined) {
2358
2440
  return null;
2359
2441
  }
2360
- return { systemPrompt };
2442
+ return {
2443
+ ...(systemPrompt === null ? {} : { systemPrompt }),
2444
+ ...(telegramAttachmentRetentionMs === undefined
2445
+ ? {}
2446
+ : { telegramAttachmentRetentionMs }),
2447
+ };
2361
2448
  }
2362
2449
  function normalizeSystemPrompt(systemPrompt) {
2363
2450
  if (typeof systemPrompt !== "string") {
@@ -2366,6 +2453,33 @@ function normalizeSystemPrompt(systemPrompt) {
2366
2453
  const normalizedSystemPrompt = systemPrompt.trim();
2367
2454
  return normalizedSystemPrompt.length > 0 ? normalizedSystemPrompt : null;
2368
2455
  }
2456
+ function normalizeTelegramAttachmentRetentionMs(retentionMs) {
2457
+ if (typeof retentionMs !== "number" || !Number.isFinite(retentionMs)) {
2458
+ return undefined;
2459
+ }
2460
+ const normalizedRetentionMs = Math.floor(retentionMs);
2461
+ if (normalizedRetentionMs <= 0) {
2462
+ return undefined;
2463
+ }
2464
+ return normalizedRetentionMs;
2465
+ }
2466
+ function resolveTelegramAttachmentRetentionMs(retentionMs) {
2467
+ return (normalizeTelegramAttachmentRetentionMs(retentionMs) ??
2468
+ DEFAULT_TELEGRAM_ATTACHMENT_RETENTION_MS);
2469
+ }
2470
+ async function resolveActiveTelegramBotToken(ctx, secretRefs) {
2471
+ const telegramSecretRefs = secretRefs.filter((ref) => ref === "telegram.botToken" || ref.startsWith("telegram.botToken."));
2472
+ for (const telegramSecretRef of telegramSecretRefs) {
2473
+ const activeSecret = await ctx.db
2474
+ .query("secrets")
2475
+ .withIndex("by_secretRef_and_active", (q) => q.eq("secretRef", telegramSecretRef).eq("active", true))
2476
+ .unique();
2477
+ if (activeSecret) {
2478
+ return decryptSecretValue(activeSecret.encryptedValue, activeSecret.algorithm);
2479
+ }
2480
+ }
2481
+ return null;
2482
+ }
2369
2483
  async function buildGlobalSkillMaterialization(skill) {
2370
2484
  const skillDirName = normalizeGlobalSkillDirName(skill.slug);
2371
2485
  const scriptExt = skill.moduleFormat === "cjs" ? "cjs" : "mjs";