@okrlinkhub/agent-factory 2.0.3 → 3.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.
Files changed (38) hide show
  1. package/README.md +102 -0
  2. package/dist/client/index.d.ts +53 -21
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +74 -3
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/api.d.ts +2 -0
  7. package/dist/component/_generated/api.d.ts.map +1 -1
  8. package/dist/component/_generated/api.js.map +1 -1
  9. package/dist/component/_generated/component.d.ts +119 -2
  10. package/dist/component/_generated/component.d.ts.map +1 -1
  11. package/dist/component/identity.d.ts +6 -6
  12. package/dist/component/lib.d.ts +2 -1
  13. package/dist/component/lib.d.ts.map +1 -1
  14. package/dist/component/lib.js +2 -1
  15. package/dist/component/lib.js.map +1 -1
  16. package/dist/component/messageTemplates.d.ts +37 -0
  17. package/dist/component/messageTemplates.d.ts.map +1 -0
  18. package/dist/component/messageTemplates.js +177 -0
  19. package/dist/component/messageTemplates.js.map +1 -0
  20. package/dist/component/pushing.d.ts +37 -37
  21. package/dist/component/queue.d.ts +113 -90
  22. package/dist/component/queue.d.ts.map +1 -1
  23. package/dist/component/queue.js +122 -47
  24. package/dist/component/queue.js.map +1 -1
  25. package/dist/component/scheduler.d.ts +23 -23
  26. package/dist/component/schema.d.ts +86 -44
  27. package/dist/component/schema.d.ts.map +1 -1
  28. package/dist/component/schema.js +19 -1
  29. package/dist/component/schema.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/client/index.ts +76 -3
  32. package/src/component/_generated/api.ts +2 -0
  33. package/src/component/_generated/component.ts +159 -2
  34. package/src/component/lib.test.ts +125 -0
  35. package/src/component/lib.ts +8 -0
  36. package/src/component/messageTemplates.ts +205 -0
  37. package/src/component/queue.ts +165 -49
  38. package/src/component/schema.ts +22 -1
@@ -138,7 +138,6 @@ const globalSkillManifestItemValidator = v.object({
138
138
  version: v.string(),
139
139
  moduleFormat: globalSkillModuleFormatValidator,
140
140
  entryPoint: v.string(),
141
- sourceJs: v.string(),
142
141
  sha256: v.string(),
143
142
  skillDirName: v.string(),
144
143
  files: v.array(globalSkillManifestFileValidator),
@@ -587,7 +586,7 @@ export const deployGlobalSkill = mutation({
587
586
  displayName: v.optional(v.string()),
588
587
  description: v.optional(v.string()),
589
588
  version: v.string(),
590
- sourceJs: v.string(),
589
+ files: v.array(globalSkillManifestFileValidator),
591
590
  entryPoint: v.optional(v.string()),
592
591
  moduleFormat: v.optional(globalSkillModuleFormatValidator),
593
592
  releaseChannel: v.optional(globalSkillReleaseChannelValidator),
@@ -611,23 +610,17 @@ export const deployGlobalSkill = mutation({
611
610
  const releaseChannel = args.releaseChannel ?? "stable";
612
611
  const moduleFormat = args.moduleFormat ?? "esm";
613
612
  const actor = args.actor?.trim() || "system";
614
- const sourceJs = args.sourceJs.trim();
613
+ const files = await normalizeGlobalSkillBundleFiles(args.files, moduleFormat);
615
614
  if (!/^[a-z0-9][a-z0-9-_]{1,127}$/.test(slug)) {
616
615
  throw new Error("Invalid skill slug. Use lowercase letters, numbers, '-' and '_'.");
617
616
  }
618
617
  if (!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/.test(version)) {
619
618
  throw new Error("Invalid skill version. Use semantic versioning format.");
620
619
  }
621
- if (sourceJs.length < 16) {
622
- throw new Error("Skill source is too short.");
623
- }
624
- if (sourceJs.length > 200_000) {
625
- throw new Error("Skill source too large (max 200KB).");
626
- }
627
620
  if (!entryPoint) {
628
621
  throw new Error("entryPoint is required.");
629
622
  }
630
- const sha256 = await computeSha256Hex(sourceJs);
623
+ const sha256 = await computeGlobalSkillBundleSha256(files);
631
624
  const existingSkill = await ctx.db
632
625
  .query("globalSkills")
633
626
  .withIndex("by_slug", (q) => q.eq("slug", slug))
@@ -663,14 +656,16 @@ export const deployGlobalSkill = mutation({
663
656
  version,
664
657
  moduleFormat,
665
658
  entryPoint,
666
- sourceJs,
659
+ files,
667
660
  sha256,
668
661
  createdBy: actor,
669
662
  createdAt: nowMs,
670
663
  });
671
664
  }
672
- else if (existingVersion.sha256 !== sha256) {
673
- throw new Error(`Skill ${slug}@${version} already exists with a different source.`);
665
+ else if (existingVersion.sha256 !== sha256 ||
666
+ existingVersion.moduleFormat !== moduleFormat ||
667
+ existingVersion.entryPoint !== entryPoint) {
668
+ throw new Error(`Skill ${slug}@${version} already exists with a different bundle.`);
674
669
  }
675
670
  const activeReleases = await ctx.db
676
671
  .query("globalSkillReleases")
@@ -805,7 +800,7 @@ export const getWorkerGlobalSkillsManifest = query({
805
800
  version: version.version,
806
801
  moduleFormat: version.moduleFormat,
807
802
  entryPoint: version.entryPoint,
808
- sourceJs: version.sourceJs,
803
+ files: version.files,
809
804
  sha256: version.sha256,
810
805
  });
811
806
  manifestSkills.push({
@@ -813,7 +808,6 @@ export const getWorkerGlobalSkillsManifest = query({
813
808
  version: version.version,
814
809
  moduleFormat: version.moduleFormat,
815
810
  entryPoint: version.entryPoint,
816
- sourceJs: version.sourceJs,
817
811
  sha256: version.sha256,
818
812
  skillDirName: materializedSkill.skillDirName,
819
813
  files: materializedSkill.files,
@@ -825,7 +819,11 @@ export const getWorkerGlobalSkillsManifest = query({
825
819
  return a.version.localeCompare(b.version);
826
820
  });
827
821
  const fingerprintSeed = manifestSkills
828
- .map((row) => `${row.slug}@${row.version}:${row.sha256}`)
822
+ .map((row) => `${row.slug}@${row.version}:${row.sha256}:${row.files
823
+ .filter((file) => file.path !== ".af-global-skill.json")
824
+ .map((file) => `${file.path}:${file.sha256}`)
825
+ .sort()
826
+ .join(",")}`)
829
827
  .join("|");
830
828
  const manifestVersion = await computeSha256Hex(fingerprintSeed || "empty");
831
829
  return {
@@ -1642,6 +1640,57 @@ export const sendMessageToUserAgent = mutation({
1642
1640
  });
1643
1641
  },
1644
1642
  });
1643
+ export const sendMessageTemplateToUserAgent = mutation({
1644
+ args: {
1645
+ consumerUserId: v.string(),
1646
+ agentKey: v.string(),
1647
+ templateId: v.id("messageTemplates"),
1648
+ metadata: v.optional(v.record(v.string(), v.string())),
1649
+ nowMs: v.optional(v.number()),
1650
+ providerConfig: v.optional(providerConfigValidator),
1651
+ },
1652
+ returns: v.object({
1653
+ messageId: v.id("messageQueue"),
1654
+ usageCount: v.number(),
1655
+ }),
1656
+ handler: async (ctx, args) => {
1657
+ const template = await ctx.db.get(args.templateId);
1658
+ if (!template || !template.enabled) {
1659
+ throw new Error("Message template not found");
1660
+ }
1661
+ const nowMs = args.nowMs ?? Date.now();
1662
+ const target = await resolveConversationTargetForUserAgent(ctx, args.consumerUserId, args.agentKey, true);
1663
+ const providerUserId = target.telegramUserId ?? target.telegramChatId ?? args.consumerUserId;
1664
+ const usageCount = template.usageCount + 1;
1665
+ const messageId = await enqueueMessageRecord(ctx, {
1666
+ conversationId: target.conversationId,
1667
+ agentKey: args.agentKey,
1668
+ payload: {
1669
+ provider: target.provider,
1670
+ providerUserId,
1671
+ messageText: template.text,
1672
+ metadata: {
1673
+ ...(args.metadata ?? {}),
1674
+ consumerUserId: args.consumerUserId,
1675
+ source: "message_template",
1676
+ templateId: String(template._id),
1677
+ templateKey: template.templateKey,
1678
+ ...(target.telegramChatId ? { telegramChatId: target.telegramChatId } : {}),
1679
+ ...(target.telegramUserId ? { telegramUserId: target.telegramUserId } : {}),
1680
+ },
1681
+ },
1682
+ scheduledFor: nowMs,
1683
+ providerConfig: args.providerConfig,
1684
+ });
1685
+ await ctx.db.patch(template._id, {
1686
+ usageCount,
1687
+ });
1688
+ return {
1689
+ messageId,
1690
+ usageCount,
1691
+ };
1692
+ },
1693
+ });
1645
1694
  export const listSnapshotsForConversation = query({
1646
1695
  args: {
1647
1696
  conversationId: v.string(),
@@ -2632,26 +2681,11 @@ function inferMimeTypeFromFilePath(filePath) {
2632
2681
  }
2633
2682
  async function buildGlobalSkillMaterialization(skill) {
2634
2683
  const skillDirName = normalizeGlobalSkillDirName(skill.slug);
2635
- const scriptExt = skill.moduleFormat === "cjs" ? "cjs" : "mjs";
2636
- const scriptRelativePath = `scripts/index.${scriptExt}`;
2637
- const skillMd = [
2638
- "---",
2639
- `name: ${skill.slug}`,
2640
- `description: Global skill ${skill.slug}@${skill.version} provisioned by agent-factory.`,
2641
- "---",
2642
- "",
2643
- "# Global Skill",
2644
- "",
2645
- "This skill is generated automatically from agent-factory globalSkills.",
2646
- "",
2647
- `- slug: ${skill.slug}`,
2648
- `- version: ${skill.version}`,
2649
- `- entryPoint: ${skill.entryPoint}`,
2650
- `- moduleFormat: ${skill.moduleFormat}`,
2651
- "",
2652
- "Do not edit manually.",
2653
- "",
2654
- ].join("\n");
2684
+ const bundleFiles = await normalizeGlobalSkillBundleFiles(skill.files, skill.moduleFormat);
2685
+ const bundleSha256 = await computeGlobalSkillBundleSha256(bundleFiles);
2686
+ if (bundleSha256 !== skill.sha256) {
2687
+ throw new Error(`Global skill bundle checksum mismatch for ${skill.slug}@${skill.version}.`);
2688
+ }
2655
2689
  const markerJson = `${JSON.stringify({
2656
2690
  slug: skill.slug,
2657
2691
  version: skill.version,
@@ -2659,19 +2693,9 @@ async function buildGlobalSkillMaterialization(skill) {
2659
2693
  managedBy: "agent-factory",
2660
2694
  entryPoint: skill.entryPoint,
2661
2695
  moduleFormat: skill.moduleFormat,
2662
- generatedAt: Date.now(),
2663
2696
  }, null, 2)}\n`;
2664
2697
  const files = [
2665
- {
2666
- path: "SKILL.md",
2667
- content: `${skillMd}\n`,
2668
- sha256: await computeSha256Hex(`${skillMd}\n`),
2669
- },
2670
- {
2671
- path: scriptRelativePath,
2672
- content: `${skill.sourceJs.trimEnd()}\n`,
2673
- sha256: await computeSha256Hex(`${skill.sourceJs.trimEnd()}\n`),
2674
- },
2698
+ ...bundleFiles,
2675
2699
  {
2676
2700
  path: ".af-global-skill.json",
2677
2701
  content: markerJson,
@@ -2680,10 +2704,61 @@ async function buildGlobalSkillMaterialization(skill) {
2680
2704
  ];
2681
2705
  return { skillDirName, files };
2682
2706
  }
2707
+ async function normalizeGlobalSkillBundleFiles(rawFiles, moduleFormat) {
2708
+ if (!Array.isArray(rawFiles) || rawFiles.length === 0) {
2709
+ throw new Error("Global skill bundle must contain at least one file.");
2710
+ }
2711
+ const byPath = new Map();
2712
+ let totalBytes = 0;
2713
+ for (const rawFile of rawFiles) {
2714
+ const path = normalizeRelativePath(rawFile.path);
2715
+ if (path === ".af-global-skill.json") {
2716
+ throw new Error("Bundle files must not include .af-global-skill.json.");
2717
+ }
2718
+ if (byPath.has(path)) {
2719
+ throw new Error(`Duplicate bundle file path: ${path}`);
2720
+ }
2721
+ const content = typeof rawFile.content === "string" ? rawFile.content : "";
2722
+ totalBytes += new TextEncoder().encode(content).length;
2723
+ if (totalBytes > 200_000) {
2724
+ throw new Error("Global skill bundle too large (max 200KB).");
2725
+ }
2726
+ const sha256 = typeof rawFile.sha256 === "string" ? rawFile.sha256.trim() : "";
2727
+ if (!sha256) {
2728
+ throw new Error(`Missing bundle checksum for ${path}.`);
2729
+ }
2730
+ const computedSha = await computeSha256Hex(content);
2731
+ if (computedSha !== sha256) {
2732
+ throw new Error(`Global skill checksum mismatch for ${path}.`);
2733
+ }
2734
+ byPath.set(path, { path, content, sha256 });
2735
+ }
2736
+ const entryScriptPath = `scripts/index.${moduleFormat === "cjs" ? "cjs" : "mjs"}`;
2737
+ if (!byPath.has("SKILL.md")) {
2738
+ throw new Error("Global skill bundle must include SKILL.md.");
2739
+ }
2740
+ if (!byPath.has(entryScriptPath)) {
2741
+ throw new Error(`Global skill bundle must include ${entryScriptPath}.`);
2742
+ }
2743
+ return [...byPath.values()].sort((left, right) => left.path.localeCompare(right.path));
2744
+ }
2745
+ async function computeGlobalSkillBundleSha256(files) {
2746
+ const fingerprint = files
2747
+ .map((file) => `${file.path}\n${file.sha256}\n${file.content}`)
2748
+ .join("\n---\n");
2749
+ return await computeSha256Hex(fingerprint);
2750
+ }
2683
2751
  function normalizeGlobalSkillDirName(slug) {
2684
2752
  const normalized = slug.trim().toLowerCase().replace(/[^a-z0-9-_]/g, "-");
2685
2753
  return normalized.length > 0 ? normalized : "unnamed-skill";
2686
2754
  }
2755
+ function normalizeRelativePath(value) {
2756
+ const normalized = String(value || "").replaceAll("\\", "/").replace(/^\/+/, "");
2757
+ if (!normalized || normalized === "." || normalized.includes("../")) {
2758
+ throw new Error(`invalid_relative_path:${value}`);
2759
+ }
2760
+ return normalized;
2761
+ }
2687
2762
  function getBridgeSecretRefsForProfile(agentKey, bridgeConfig) {
2688
2763
  if (!bridgeConfig?.enabled) {
2689
2764
  return [];