@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/dist/component/queue.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { v } from "convex/values";
|
|
2
|
-
import { internal } from "./_generated/api.js";
|
|
3
|
-
import { internalMutation, internalQuery, mutation, query, } from "./_generated/server.js";
|
|
2
|
+
import { api, internal } from "./_generated/api.js";
|
|
3
|
+
import { action, internalMutation, internalQuery, mutation, query, } from "./_generated/server.js";
|
|
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"));
|
|
@@ -15,6 +15,14 @@ const telegramAttachmentValidator = v.object({
|
|
|
15
15
|
mimeType: v.optional(v.string()),
|
|
16
16
|
sizeBytes: v.optional(v.number()),
|
|
17
17
|
expiresAt: v.number(),
|
|
18
|
+
downloadUrl: v.optional(v.string()),
|
|
19
|
+
});
|
|
20
|
+
const telegramAttachmentCandidateValidator = v.object({
|
|
21
|
+
kind: telegramAttachmentKindValidator,
|
|
22
|
+
telegramFileId: v.string(),
|
|
23
|
+
fileName: v.optional(v.string()),
|
|
24
|
+
mimeType: v.optional(v.string()),
|
|
25
|
+
sizeBytes: v.optional(v.number()),
|
|
18
26
|
});
|
|
19
27
|
const queuePayloadValidator = v.object({
|
|
20
28
|
provider: v.string(),
|
|
@@ -474,6 +482,23 @@ export const getTelegramIngressRuntimeConfig = internalQuery({
|
|
|
474
482
|
};
|
|
475
483
|
},
|
|
476
484
|
});
|
|
485
|
+
export const prepareTelegramAttachmentsForEnqueue = action({
|
|
486
|
+
args: {
|
|
487
|
+
agentKey: v.string(),
|
|
488
|
+
attachments: v.array(telegramAttachmentCandidateValidator),
|
|
489
|
+
},
|
|
490
|
+
returns: v.array(telegramAttachmentValidator),
|
|
491
|
+
handler: async (ctx, args) => {
|
|
492
|
+
const ingressConfig = await ctx.runQuery(internal.queue.getTelegramIngressRuntimeConfig, {
|
|
493
|
+
agentKey: args.agentKey,
|
|
494
|
+
});
|
|
495
|
+
if (!ingressConfig.botToken) {
|
|
496
|
+
throw new Error(`missing active telegram bot token for agent '${args.agentKey}'`);
|
|
497
|
+
}
|
|
498
|
+
const expiresAt = Date.now() + ingressConfig.attachmentRetentionMs;
|
|
499
|
+
return await Promise.all(args.attachments.map((attachment) => persistTelegramAttachmentFromCandidate(ctx, ingressConfig.botToken, attachment, expiresAt)));
|
|
500
|
+
},
|
|
501
|
+
});
|
|
477
502
|
export const upsertMessageRuntimeConfig = internalMutation({
|
|
478
503
|
args: {
|
|
479
504
|
messageConfig: messageRuntimeConfigValidator,
|
|
@@ -1451,11 +1476,12 @@ export const getHydrationBundleForClaimedJob = query({
|
|
|
1451
1476
|
? conversationCache.deltaContext
|
|
1452
1477
|
: conversation.contextHistory;
|
|
1453
1478
|
const bridgeRuntimeConfig = await resolveBridgeRuntimeConfig(ctx, profile);
|
|
1479
|
+
const hydratedPayload = await hydrateQueuePayloadAttachments(ctx, message.payload);
|
|
1454
1480
|
return {
|
|
1455
1481
|
messageId: message._id,
|
|
1456
1482
|
conversationId: message.conversationId,
|
|
1457
1483
|
agentKey: message.agentKey,
|
|
1458
|
-
payload:
|
|
1484
|
+
payload: hydratedPayload,
|
|
1459
1485
|
conversationState: {
|
|
1460
1486
|
contextHistory,
|
|
1461
1487
|
pendingToolCalls: conversation.pendingToolCalls,
|
|
@@ -2310,6 +2336,21 @@ async function enqueueMessageRecord(ctx, args) {
|
|
|
2310
2336
|
}
|
|
2311
2337
|
return messageId;
|
|
2312
2338
|
}
|
|
2339
|
+
async function hydrateQueuePayloadAttachments(ctx, payload) {
|
|
2340
|
+
if (!payload.attachments?.length) {
|
|
2341
|
+
return payload;
|
|
2342
|
+
}
|
|
2343
|
+
const attachments = await Promise.all(payload.attachments.map(async (attachment) => ({
|
|
2344
|
+
...attachment,
|
|
2345
|
+
downloadUrl: attachment.status === "ready"
|
|
2346
|
+
? ((await ctx.storage.getUrl(attachment.storageId)) ?? undefined)
|
|
2347
|
+
: undefined,
|
|
2348
|
+
})));
|
|
2349
|
+
return {
|
|
2350
|
+
...payload,
|
|
2351
|
+
attachments,
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2313
2354
|
async function resolveConversationTargetForUserAgent(ctx, consumerUserId, agentKey, requireActive = false) {
|
|
2314
2355
|
const bindings = await ctx.db
|
|
2315
2356
|
.query("identityBindings")
|
|
@@ -2480,6 +2521,115 @@ async function resolveActiveTelegramBotToken(ctx, secretRefs) {
|
|
|
2480
2521
|
}
|
|
2481
2522
|
return null;
|
|
2482
2523
|
}
|
|
2524
|
+
async function persistTelegramAttachmentFromCandidate(ctx, telegramBotToken, attachment, expiresAt) {
|
|
2525
|
+
const filePath = await fetchTelegramFilePath(telegramBotToken, attachment.telegramFileId);
|
|
2526
|
+
const downloaded = await downloadTelegramFile(telegramBotToken, filePath);
|
|
2527
|
+
const resolvedMimeType = resolvePreferredTelegramAttachmentMimeType(attachment.mimeType, attachment.fileName, downloaded.mimeType, filePath);
|
|
2528
|
+
const uploadTarget = await ctx.runMutation(api.queue.generateMediaUploadUrl, {});
|
|
2529
|
+
const uploadResponse = await fetch(uploadTarget.uploadUrl, {
|
|
2530
|
+
method: "POST",
|
|
2531
|
+
headers: resolvedMimeType.length > 0
|
|
2532
|
+
? {
|
|
2533
|
+
"Content-Type": resolvedMimeType,
|
|
2534
|
+
}
|
|
2535
|
+
: undefined,
|
|
2536
|
+
body: downloaded.blob,
|
|
2537
|
+
});
|
|
2538
|
+
const uploadPayload = (await uploadResponse.json().catch(() => ({})));
|
|
2539
|
+
if (!uploadResponse.ok || !uploadPayload.storageId) {
|
|
2540
|
+
throw new Error(`Convex storage upload failed for Telegram ${attachment.kind} attachment`);
|
|
2541
|
+
}
|
|
2542
|
+
return {
|
|
2543
|
+
kind: attachment.kind,
|
|
2544
|
+
status: "ready",
|
|
2545
|
+
storageId: uploadPayload.storageId,
|
|
2546
|
+
telegramFileId: attachment.telegramFileId,
|
|
2547
|
+
fileName: attachment.fileName,
|
|
2548
|
+
mimeType: resolvedMimeType,
|
|
2549
|
+
sizeBytes: attachment.sizeBytes ?? downloaded.blob.size,
|
|
2550
|
+
expiresAt,
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
async function fetchTelegramFilePath(telegramBotToken, telegramFileId) {
|
|
2554
|
+
const telegramApiBaseUrl = `https://api.telegram.org/bot${encodeURIComponent(telegramBotToken)}`;
|
|
2555
|
+
const response = await fetch(`${telegramApiBaseUrl}/getFile?file_id=${encodeURIComponent(telegramFileId)}`);
|
|
2556
|
+
const payload = (await response.json().catch(() => ({})));
|
|
2557
|
+
if (!response.ok || payload.ok !== true || typeof payload.result?.file_path !== "string") {
|
|
2558
|
+
throw new Error(`Telegram getFile failed: ${typeof payload.description === "string" ? payload.description : "missing file_path"}`);
|
|
2559
|
+
}
|
|
2560
|
+
return payload.result.file_path;
|
|
2561
|
+
}
|
|
2562
|
+
async function downloadTelegramFile(telegramBotToken, filePath) {
|
|
2563
|
+
const response = await fetch(`https://api.telegram.org/file/bot${encodeURIComponent(telegramBotToken)}/${filePath}`);
|
|
2564
|
+
if (!response.ok) {
|
|
2565
|
+
throw new Error(`Telegram file download failed with status ${response.status}`);
|
|
2566
|
+
}
|
|
2567
|
+
const blob = await response.blob();
|
|
2568
|
+
const mimeType = response.headers.get("Content-Type") ??
|
|
2569
|
+
blob.type ??
|
|
2570
|
+
inferMimeTypeFromFilePath(filePath) ??
|
|
2571
|
+
"application/octet-stream";
|
|
2572
|
+
return {
|
|
2573
|
+
blob: mimeType === blob.type ? blob : new Blob([await blob.arrayBuffer()], { type: mimeType }),
|
|
2574
|
+
mimeType,
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
function resolvePreferredTelegramAttachmentMimeType(originalMimeType, fileName, downloadedMimeType, filePath) {
|
|
2578
|
+
const normalizedOriginalMimeType = normalizeNonGenericMimeType(originalMimeType);
|
|
2579
|
+
if (normalizedOriginalMimeType) {
|
|
2580
|
+
return normalizedOriginalMimeType;
|
|
2581
|
+
}
|
|
2582
|
+
const inferredFromFileName = inferMimeTypeFromFileName(fileName);
|
|
2583
|
+
if (inferredFromFileName) {
|
|
2584
|
+
return inferredFromFileName;
|
|
2585
|
+
}
|
|
2586
|
+
const normalizedDownloadedMimeType = normalizeNonGenericMimeType(downloadedMimeType);
|
|
2587
|
+
if (normalizedDownloadedMimeType) {
|
|
2588
|
+
return normalizedDownloadedMimeType;
|
|
2589
|
+
}
|
|
2590
|
+
return inferMimeTypeFromFilePath(filePath) ?? "application/octet-stream";
|
|
2591
|
+
}
|
|
2592
|
+
function normalizeNonGenericMimeType(mimeType) {
|
|
2593
|
+
if (typeof mimeType !== "string") {
|
|
2594
|
+
return null;
|
|
2595
|
+
}
|
|
2596
|
+
const normalizedMimeType = mimeType.trim().toLowerCase();
|
|
2597
|
+
if (!normalizedMimeType || normalizedMimeType === "application/octet-stream") {
|
|
2598
|
+
return null;
|
|
2599
|
+
}
|
|
2600
|
+
return normalizedMimeType;
|
|
2601
|
+
}
|
|
2602
|
+
function inferMimeTypeFromFileName(fileName) {
|
|
2603
|
+
if (typeof fileName !== "string") {
|
|
2604
|
+
return null;
|
|
2605
|
+
}
|
|
2606
|
+
return inferMimeTypeFromFilePath(fileName);
|
|
2607
|
+
}
|
|
2608
|
+
function inferMimeTypeFromFilePath(filePath) {
|
|
2609
|
+
const normalizedPath = filePath.toLowerCase();
|
|
2610
|
+
if (normalizedPath.endsWith(".jpg") || normalizedPath.endsWith(".jpeg")) {
|
|
2611
|
+
return "image/jpeg";
|
|
2612
|
+
}
|
|
2613
|
+
if (normalizedPath.endsWith(".png")) {
|
|
2614
|
+
return "image/png";
|
|
2615
|
+
}
|
|
2616
|
+
if (normalizedPath.endsWith(".webp")) {
|
|
2617
|
+
return "image/webp";
|
|
2618
|
+
}
|
|
2619
|
+
if (normalizedPath.endsWith(".mp4")) {
|
|
2620
|
+
return "video/mp4";
|
|
2621
|
+
}
|
|
2622
|
+
if (normalizedPath.endsWith(".mp3")) {
|
|
2623
|
+
return "audio/mpeg";
|
|
2624
|
+
}
|
|
2625
|
+
if (normalizedPath.endsWith(".ogg")) {
|
|
2626
|
+
return "audio/ogg";
|
|
2627
|
+
}
|
|
2628
|
+
if (normalizedPath.endsWith(".pdf")) {
|
|
2629
|
+
return "application/pdf";
|
|
2630
|
+
}
|
|
2631
|
+
return null;
|
|
2632
|
+
}
|
|
2483
2633
|
async function buildGlobalSkillMaterialization(skill) {
|
|
2484
2634
|
const skillDirName = normalizeGlobalSkillDirName(skill.slug);
|
|
2485
2635
|
const scriptExt = skill.moduleFormat === "cjs" ? "cjs" : "mjs";
|