From fc9172f42acf9be58a6041eb8888738494aaa099 Mon Sep 17 00:00:00 2001 From: Yevanchen Date: Mon, 24 Aug 2026 09:20:21 +0800 Subject: [PATCH] perf(agent-package): index archive skill assets --- pkgs/agent-package/src/archive-assets.ts | 80 +++++++++++++++++-- .../tests/archive-entry-admission.test.ts | 52 ++++++++++++ 2 files changed, 126 insertions(+), 6 deletions(-) diff --git a/pkgs/agent-package/src/archive-assets.ts b/pkgs/agent-package/src/archive-assets.ts index db559297..fba1c49c 100644 --- a/pkgs/agent-package/src/archive-assets.ts +++ b/pkgs/agent-package/src/archive-assets.ts @@ -12,6 +12,12 @@ interface PackageAssetReadResult { issues: AgentResolutionIssue[]; } +interface SkillAssetBucket { + entries: Array<[path: string, contentBytes: Uint8Array]>; + skill: AgentPackage["manifest"]["skills"][number]; + skillPath: string; +} + export function readPackageAssets( agentPackage: AgentPackage, entries: Record, @@ -30,12 +36,10 @@ function readSkillAssets( assets: AgentPackageAsset[], issues: AgentResolutionIssue[], ): void { - for (const skill of agentPackage.manifest.skills) { - const skillPath = skill.skillId.endsWith("/") ? skill.skillId : `${skill.skillId}/`; - const skillEntries = Object.entries(entries).filter( - ([path, entry]) => path.startsWith(skillPath) && entry.byteLength > 0, - ); - + for (const { entries: skillEntries, skill, skillPath } of collectSkillAssetBuckets( + agentPackage, + entries, + )) { if (skillEntries.length === 0) { issues.push( createArchiveIssue({ @@ -75,3 +79,67 @@ function readSkillAssets( } } } + +function collectSkillAssetBuckets( + agentPackage: AgentPackage, + entries: Record, +): SkillAssetBucket[] { + const buckets: SkillAssetBucket[] = agentPackage.manifest.skills.map((skill) => ({ + entries: [], + skill, + skillPath: skill.skillId.endsWith("/") ? skill.skillId : `${skill.skillId}/`, + })); + + if (buckets.length === 0) { + return buckets; + } + + const archiveEntries = Object.entries(entries); + + if (buckets.length === 1) { + const [bucket] = buckets; + + if (bucket !== undefined) { + bucket.entries = archiveEntries.filter( + ([path, contentBytes]) => contentBytes.byteLength > 0 && path.startsWith(bucket.skillPath), + ); + } + + return buckets; + } + + const entriesBySkillPath = new Map(); + + for (const bucket of buckets) { + if (!entriesBySkillPath.has(bucket.skillPath)) { + entriesBySkillPath.set(bucket.skillPath, []); + } + } + + for (const [path, contentBytes] of archiveEntries) { + if (contentBytes.byteLength === 0) { + continue; + } + + // Probe directory prefixes once per entry so overlapping and duplicate + // skill declarations keep their existing matching behavior. + let slashIndex = path.indexOf("/"); + + while (slashIndex !== -1) { + const candidateRoot = path.slice(0, slashIndex + 1); + const matchingEntries = entriesBySkillPath.get(candidateRoot); + + if (matchingEntries !== undefined) { + matchingEntries.push([path, contentBytes]); + } + + slashIndex = path.indexOf("/", slashIndex + 1); + } + } + + for (const bucket of buckets) { + bucket.entries = entriesBySkillPath.get(bucket.skillPath) ?? []; + } + + return buckets; +} diff --git a/pkgs/agent-package/tests/archive-entry-admission.test.ts b/pkgs/agent-package/tests/archive-entry-admission.test.ts index 87ce2bff..368199bb 100644 --- a/pkgs/agent-package/tests/archive-entry-admission.test.ts +++ b/pkgs/agent-package/tests/archive-entry-admission.test.ts @@ -7,6 +7,8 @@ import { import type { AgentPackage } from "@mosoo/contracts/agent-manifest"; import { AGENT_MANIFEST_VERSION, AGENT_PACKAGE_VERSION } from "@mosoo/contracts/agent-manifest"; +import { readPackageAssets } from "../src/archive-assets"; + interface StoredZipEntry { body: Uint8Array; path: string; @@ -236,6 +238,56 @@ describe("agent package archive entry admission", () => { ]); }); + test("indexes skill assets without changing declaration or entry order", () => { + const agentPackage = createAgentPackageFixture({ + skills: [ + { + ownerName: null, + skillId: "skills/demo/nested/", + skillName: "Nested", + state: "active", + }, + { + ownerName: null, + skillId: "skills/demo/", + skillName: "Demo", + state: "active", + }, + { + ownerName: null, + skillId: "skills/demo/", + skillName: "Demo duplicate", + state: "active", + }, + { + ownerName: null, + skillId: "skills/empty/", + skillName: "Empty", + state: "active", + }, + ], + }); + const result = readPackageAssets(agentPackage, { + "skills/demo/nested/child.txt": textToArchiveBytes("child"), + "skills/demo/root.txt": textToArchiveBytes("root"), + "skills/empty/zero.txt": new Uint8Array(), + }); + + expect(result.assets.map((asset) => ({ filename: asset.filename, key: asset.key }))).toEqual([ + { filename: "child.txt", key: "skills/demo/nested/child.txt" }, + { filename: "nested/child.txt", key: "skills/demo/nested/child.txt" }, + { filename: "root.txt", key: "skills/demo/root.txt" }, + { filename: "nested/child.txt", key: "skills/demo/nested/child.txt" }, + { filename: "root.txt", key: "skills/demo/root.txt" }, + ]); + expect(result.issues).toEqual([ + expect.objectContaining({ + code: "package.skill.missing", + targetLabel: "Empty", + }), + ]); + }); + test("rejects package assets that point at a declared skill root", () => { const parsed = parseAgentPackageArchiveBytes( createStoredZipArchive([