@okrlinkhub/agent-factory 2.0.1 → 2.0.3
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/client/index.d.ts +66 -64
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -90
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +32 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/identity.d.ts +6 -6
- package/dist/component/pushing.d.ts +40 -40
- package/dist/component/queue.d.ts +125 -97
- package/dist/component/queue.d.ts.map +1 -1
- package/dist/component/queue.js +153 -3
- package/dist/component/queue.js.map +1 -1
- package/dist/component/scheduler.d.ts +23 -23
- package/dist/component/schema.d.ts +21 -16
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +1 -0
- package/dist/component/schema.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +6 -137
- package/src/component/_generated/component.ts +38 -0
- package/src/component/lib.test.ts +1 -0
- package/src/component/queue.ts +260 -3
- package/src/component/schema.ts +1 -0
package/src/component/queue.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { v } from "convex/values";
|
|
2
|
-
import { internal } from "./_generated/api.js";
|
|
2
|
+
import { api, internal } from "./_generated/api.js";
|
|
3
3
|
import {
|
|
4
|
+
action,
|
|
4
5
|
internalMutation,
|
|
5
6
|
internalQuery,
|
|
6
7
|
mutation,
|
|
7
8
|
query,
|
|
8
9
|
} from "./_generated/server.js";
|
|
9
|
-
import type { MutationCtx, QueryCtx } from "./_generated/server.js";
|
|
10
|
+
import type { ActionCtx, MutationCtx, QueryCtx } from "./_generated/server.js";
|
|
10
11
|
import type { Id } from "./_generated/dataModel.js";
|
|
11
12
|
import { computeRetryDelayMs, DEFAULT_CONFIG, providerConfigValidator } from "./config.js";
|
|
12
13
|
import {
|
|
@@ -47,6 +48,15 @@ const telegramAttachmentValidator = v.object({
|
|
|
47
48
|
mimeType: v.optional(v.string()),
|
|
48
49
|
sizeBytes: v.optional(v.number()),
|
|
49
50
|
expiresAt: v.number(),
|
|
51
|
+
downloadUrl: v.optional(v.string()),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const telegramAttachmentCandidateValidator = v.object({
|
|
55
|
+
kind: telegramAttachmentKindValidator,
|
|
56
|
+
telegramFileId: v.string(),
|
|
57
|
+
fileName: v.optional(v.string()),
|
|
58
|
+
mimeType: v.optional(v.string()),
|
|
59
|
+
sizeBytes: v.optional(v.number()),
|
|
50
60
|
});
|
|
51
61
|
|
|
52
62
|
const queuePayloadValidator = v.object({
|
|
@@ -220,6 +230,26 @@ const RUNTIME_CONFIG_KEYS = {
|
|
|
220
230
|
|
|
221
231
|
const DEFAULT_TELEGRAM_ATTACHMENT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
222
232
|
|
|
233
|
+
type TelegramAttachmentCandidate = {
|
|
234
|
+
kind: "photo" | "video" | "audio" | "voice" | "document";
|
|
235
|
+
telegramFileId: string;
|
|
236
|
+
fileName?: string;
|
|
237
|
+
mimeType?: string;
|
|
238
|
+
sizeBytes?: number;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
type PreparedTelegramAttachment = {
|
|
242
|
+
kind: "photo" | "video" | "audio" | "voice" | "document";
|
|
243
|
+
status: "ready";
|
|
244
|
+
storageId: Id<"_storage">;
|
|
245
|
+
telegramFileId: string;
|
|
246
|
+
fileName?: string;
|
|
247
|
+
mimeType?: string;
|
|
248
|
+
sizeBytes?: number;
|
|
249
|
+
expiresAt: number;
|
|
250
|
+
downloadUrl?: string;
|
|
251
|
+
};
|
|
252
|
+
|
|
223
253
|
export const enqueueMessage = mutation({
|
|
224
254
|
args: {
|
|
225
255
|
conversationId: v.string(),
|
|
@@ -588,6 +618,39 @@ export const getTelegramIngressRuntimeConfig = internalQuery({
|
|
|
588
618
|
},
|
|
589
619
|
});
|
|
590
620
|
|
|
621
|
+
export const prepareTelegramAttachmentsForEnqueue = action({
|
|
622
|
+
args: {
|
|
623
|
+
agentKey: v.string(),
|
|
624
|
+
attachments: v.array(telegramAttachmentCandidateValidator),
|
|
625
|
+
},
|
|
626
|
+
returns: v.array(telegramAttachmentValidator),
|
|
627
|
+
handler: async (
|
|
628
|
+
ctx,
|
|
629
|
+
args,
|
|
630
|
+
): Promise<Array<PreparedTelegramAttachment>> => {
|
|
631
|
+
const ingressConfig: {
|
|
632
|
+
botToken: string | null;
|
|
633
|
+
attachmentRetentionMs: number;
|
|
634
|
+
} = await ctx.runQuery(internal.queue.getTelegramIngressRuntimeConfig, {
|
|
635
|
+
agentKey: args.agentKey,
|
|
636
|
+
});
|
|
637
|
+
if (!ingressConfig.botToken) {
|
|
638
|
+
throw new Error(`missing active telegram bot token for agent '${args.agentKey}'`);
|
|
639
|
+
}
|
|
640
|
+
const expiresAt = Date.now() + ingressConfig.attachmentRetentionMs;
|
|
641
|
+
return await Promise.all(
|
|
642
|
+
args.attachments.map((attachment) =>
|
|
643
|
+
persistTelegramAttachmentFromCandidate(
|
|
644
|
+
ctx,
|
|
645
|
+
ingressConfig.botToken as string,
|
|
646
|
+
attachment,
|
|
647
|
+
expiresAt,
|
|
648
|
+
),
|
|
649
|
+
),
|
|
650
|
+
);
|
|
651
|
+
},
|
|
652
|
+
});
|
|
653
|
+
|
|
591
654
|
export const upsertMessageRuntimeConfig = internalMutation({
|
|
592
655
|
args: {
|
|
593
656
|
messageConfig: messageRuntimeConfigValidator,
|
|
@@ -1756,12 +1819,13 @@ export const getHydrationBundleForClaimedJob = query({
|
|
|
1756
1819
|
? conversationCache.deltaContext
|
|
1757
1820
|
: conversation.contextHistory;
|
|
1758
1821
|
const bridgeRuntimeConfig = await resolveBridgeRuntimeConfig(ctx, profile);
|
|
1822
|
+
const hydratedPayload = await hydrateQueuePayloadAttachments(ctx, message.payload);
|
|
1759
1823
|
|
|
1760
1824
|
return {
|
|
1761
1825
|
messageId: message._id,
|
|
1762
1826
|
conversationId: message.conversationId,
|
|
1763
1827
|
agentKey: message.agentKey,
|
|
1764
|
-
payload:
|
|
1828
|
+
payload: hydratedPayload,
|
|
1765
1829
|
conversationState: {
|
|
1766
1830
|
contextHistory,
|
|
1767
1831
|
pendingToolCalls: conversation.pendingToolCalls,
|
|
@@ -2834,6 +2898,46 @@ async function enqueueMessageRecord(
|
|
|
2834
2898
|
return messageId;
|
|
2835
2899
|
}
|
|
2836
2900
|
|
|
2901
|
+
async function hydrateQueuePayloadAttachments(
|
|
2902
|
+
ctx: QueryCtx,
|
|
2903
|
+
payload: {
|
|
2904
|
+
provider: string;
|
|
2905
|
+
providerUserId: string;
|
|
2906
|
+
messageText: string;
|
|
2907
|
+
externalMessageId?: string;
|
|
2908
|
+
rawUpdateJson?: string;
|
|
2909
|
+
metadata?: Record<string, string>;
|
|
2910
|
+
attachments?: Array<{
|
|
2911
|
+
kind: "photo" | "video" | "audio" | "voice" | "document";
|
|
2912
|
+
status: "ready" | "expired";
|
|
2913
|
+
storageId: Id<"_storage">;
|
|
2914
|
+
telegramFileId: string;
|
|
2915
|
+
fileName?: string;
|
|
2916
|
+
mimeType?: string;
|
|
2917
|
+
sizeBytes?: number;
|
|
2918
|
+
expiresAt: number;
|
|
2919
|
+
downloadUrl?: string;
|
|
2920
|
+
}>;
|
|
2921
|
+
},
|
|
2922
|
+
) {
|
|
2923
|
+
if (!payload.attachments?.length) {
|
|
2924
|
+
return payload;
|
|
2925
|
+
}
|
|
2926
|
+
const attachments = await Promise.all(
|
|
2927
|
+
payload.attachments.map(async (attachment) => ({
|
|
2928
|
+
...attachment,
|
|
2929
|
+
downloadUrl:
|
|
2930
|
+
attachment.status === "ready"
|
|
2931
|
+
? ((await ctx.storage.getUrl(attachment.storageId)) ?? undefined)
|
|
2932
|
+
: undefined,
|
|
2933
|
+
})),
|
|
2934
|
+
);
|
|
2935
|
+
return {
|
|
2936
|
+
...payload,
|
|
2937
|
+
attachments,
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2837
2941
|
async function resolveConversationTargetForUserAgent(
|
|
2838
2942
|
ctx: QueryCtx | MutationCtx,
|
|
2839
2943
|
consumerUserId: string,
|
|
@@ -3152,6 +3256,159 @@ async function resolveActiveTelegramBotToken(
|
|
|
3152
3256
|
return null;
|
|
3153
3257
|
}
|
|
3154
3258
|
|
|
3259
|
+
async function persistTelegramAttachmentFromCandidate(
|
|
3260
|
+
ctx: Pick<ActionCtx, "runMutation">,
|
|
3261
|
+
telegramBotToken: string,
|
|
3262
|
+
attachment: TelegramAttachmentCandidate,
|
|
3263
|
+
expiresAt: number,
|
|
3264
|
+
): Promise<PreparedTelegramAttachment> {
|
|
3265
|
+
const filePath = await fetchTelegramFilePath(telegramBotToken, attachment.telegramFileId);
|
|
3266
|
+
const downloaded = await downloadTelegramFile(telegramBotToken, filePath);
|
|
3267
|
+
const resolvedMimeType = resolvePreferredTelegramAttachmentMimeType(
|
|
3268
|
+
attachment.mimeType,
|
|
3269
|
+
attachment.fileName,
|
|
3270
|
+
downloaded.mimeType,
|
|
3271
|
+
filePath,
|
|
3272
|
+
);
|
|
3273
|
+
const uploadTarget: { uploadUrl: string } = await ctx.runMutation(
|
|
3274
|
+
api.queue.generateMediaUploadUrl,
|
|
3275
|
+
{},
|
|
3276
|
+
);
|
|
3277
|
+
const uploadResponse = await fetch(uploadTarget.uploadUrl, {
|
|
3278
|
+
method: "POST",
|
|
3279
|
+
headers:
|
|
3280
|
+
resolvedMimeType.length > 0
|
|
3281
|
+
? {
|
|
3282
|
+
"Content-Type": resolvedMimeType,
|
|
3283
|
+
}
|
|
3284
|
+
: undefined,
|
|
3285
|
+
body: downloaded.blob,
|
|
3286
|
+
});
|
|
3287
|
+
const uploadPayload = (await uploadResponse.json().catch(() => ({}))) as {
|
|
3288
|
+
storageId?: Id<"_storage">;
|
|
3289
|
+
};
|
|
3290
|
+
if (!uploadResponse.ok || !uploadPayload.storageId) {
|
|
3291
|
+
throw new Error(`Convex storage upload failed for Telegram ${attachment.kind} attachment`);
|
|
3292
|
+
}
|
|
3293
|
+
return {
|
|
3294
|
+
kind: attachment.kind,
|
|
3295
|
+
status: "ready" as const,
|
|
3296
|
+
storageId: uploadPayload.storageId,
|
|
3297
|
+
telegramFileId: attachment.telegramFileId,
|
|
3298
|
+
fileName: attachment.fileName,
|
|
3299
|
+
mimeType: resolvedMimeType,
|
|
3300
|
+
sizeBytes: attachment.sizeBytes ?? downloaded.blob.size,
|
|
3301
|
+
expiresAt,
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
async function fetchTelegramFilePath(
|
|
3306
|
+
telegramBotToken: string,
|
|
3307
|
+
telegramFileId: string,
|
|
3308
|
+
): Promise<string> {
|
|
3309
|
+
const telegramApiBaseUrl = `https://api.telegram.org/bot${encodeURIComponent(telegramBotToken)}`;
|
|
3310
|
+
const response = await fetch(
|
|
3311
|
+
`${telegramApiBaseUrl}/getFile?file_id=${encodeURIComponent(telegramFileId)}`,
|
|
3312
|
+
);
|
|
3313
|
+
const payload = (await response.json().catch(() => ({}))) as {
|
|
3314
|
+
ok?: boolean;
|
|
3315
|
+
description?: string;
|
|
3316
|
+
result?: {
|
|
3317
|
+
file_path?: string;
|
|
3318
|
+
};
|
|
3319
|
+
};
|
|
3320
|
+
if (!response.ok || payload.ok !== true || typeof payload.result?.file_path !== "string") {
|
|
3321
|
+
throw new Error(
|
|
3322
|
+
`Telegram getFile failed: ${typeof payload.description === "string" ? payload.description : "missing file_path"}`,
|
|
3323
|
+
);
|
|
3324
|
+
}
|
|
3325
|
+
return payload.result.file_path;
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
async function downloadTelegramFile(telegramBotToken: string, filePath: string) {
|
|
3329
|
+
const response = await fetch(
|
|
3330
|
+
`https://api.telegram.org/file/bot${encodeURIComponent(telegramBotToken)}/${filePath}`,
|
|
3331
|
+
);
|
|
3332
|
+
if (!response.ok) {
|
|
3333
|
+
throw new Error(`Telegram file download failed with status ${response.status}`);
|
|
3334
|
+
}
|
|
3335
|
+
const blob = await response.blob();
|
|
3336
|
+
const mimeType =
|
|
3337
|
+
response.headers.get("Content-Type") ??
|
|
3338
|
+
blob.type ??
|
|
3339
|
+
inferMimeTypeFromFilePath(filePath) ??
|
|
3340
|
+
"application/octet-stream";
|
|
3341
|
+
return {
|
|
3342
|
+
blob: mimeType === blob.type ? blob : new Blob([await blob.arrayBuffer()], { type: mimeType }),
|
|
3343
|
+
mimeType,
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
|
|
3347
|
+
function resolvePreferredTelegramAttachmentMimeType(
|
|
3348
|
+
originalMimeType: string | undefined,
|
|
3349
|
+
fileName: string | undefined,
|
|
3350
|
+
downloadedMimeType: string,
|
|
3351
|
+
filePath: string,
|
|
3352
|
+
): string {
|
|
3353
|
+
const normalizedOriginalMimeType = normalizeNonGenericMimeType(originalMimeType);
|
|
3354
|
+
if (normalizedOriginalMimeType) {
|
|
3355
|
+
return normalizedOriginalMimeType;
|
|
3356
|
+
}
|
|
3357
|
+
const inferredFromFileName = inferMimeTypeFromFileName(fileName);
|
|
3358
|
+
if (inferredFromFileName) {
|
|
3359
|
+
return inferredFromFileName;
|
|
3360
|
+
}
|
|
3361
|
+
const normalizedDownloadedMimeType = normalizeNonGenericMimeType(downloadedMimeType);
|
|
3362
|
+
if (normalizedDownloadedMimeType) {
|
|
3363
|
+
return normalizedDownloadedMimeType;
|
|
3364
|
+
}
|
|
3365
|
+
return inferMimeTypeFromFilePath(filePath) ?? "application/octet-stream";
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
function normalizeNonGenericMimeType(mimeType?: string | null): string | null {
|
|
3369
|
+
if (typeof mimeType !== "string") {
|
|
3370
|
+
return null;
|
|
3371
|
+
}
|
|
3372
|
+
const normalizedMimeType = mimeType.trim().toLowerCase();
|
|
3373
|
+
if (!normalizedMimeType || normalizedMimeType === "application/octet-stream") {
|
|
3374
|
+
return null;
|
|
3375
|
+
}
|
|
3376
|
+
return normalizedMimeType;
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
function inferMimeTypeFromFileName(fileName?: string | null): string | null {
|
|
3380
|
+
if (typeof fileName !== "string") {
|
|
3381
|
+
return null;
|
|
3382
|
+
}
|
|
3383
|
+
return inferMimeTypeFromFilePath(fileName);
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3386
|
+
function inferMimeTypeFromFilePath(filePath: string): string | null {
|
|
3387
|
+
const normalizedPath = filePath.toLowerCase();
|
|
3388
|
+
if (normalizedPath.endsWith(".jpg") || normalizedPath.endsWith(".jpeg")) {
|
|
3389
|
+
return "image/jpeg";
|
|
3390
|
+
}
|
|
3391
|
+
if (normalizedPath.endsWith(".png")) {
|
|
3392
|
+
return "image/png";
|
|
3393
|
+
}
|
|
3394
|
+
if (normalizedPath.endsWith(".webp")) {
|
|
3395
|
+
return "image/webp";
|
|
3396
|
+
}
|
|
3397
|
+
if (normalizedPath.endsWith(".mp4")) {
|
|
3398
|
+
return "video/mp4";
|
|
3399
|
+
}
|
|
3400
|
+
if (normalizedPath.endsWith(".mp3")) {
|
|
3401
|
+
return "audio/mpeg";
|
|
3402
|
+
}
|
|
3403
|
+
if (normalizedPath.endsWith(".ogg")) {
|
|
3404
|
+
return "audio/ogg";
|
|
3405
|
+
}
|
|
3406
|
+
if (normalizedPath.endsWith(".pdf")) {
|
|
3407
|
+
return "application/pdf";
|
|
3408
|
+
}
|
|
3409
|
+
return null;
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3155
3412
|
async function buildGlobalSkillMaterialization(skill: {
|
|
3156
3413
|
slug: string;
|
|
3157
3414
|
version: string;
|