@onco-foundry/agent-template 0.6.2 → 0.8.0
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/agent_spec_executor.js +5 -1
- package/dist/agent_version.d.ts +39 -1
- package/dist/agent_version.js +14 -2
- package/dist/agent_version_resolver.js +36 -0
- package/dist/fake_agent_version_ports.d.ts +4 -1
- package/dist/fake_agent_version_ports.js +17 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/skill_packages.d.ts +33 -0
- package/dist/skill_packages.js +74 -0
- package/package.json +5 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineAgent } from 'agent-lattice';
|
|
1
|
+
import { defineAgent, skill } from 'agent-lattice';
|
|
2
2
|
import { createTypedAgentTool } from '@onco-foundry/agent-runtime';
|
|
3
3
|
import { createTracer } from '@onco-foundry/trace-port';
|
|
4
4
|
import { clampThinkingBudgetTokens } from './agent_template.js';
|
|
@@ -12,6 +12,9 @@ export const createAgentSpecExecutor = (declaration) => ({
|
|
|
12
12
|
},
|
|
13
13
|
task: declaration.task,
|
|
14
14
|
async createSpec(input) {
|
|
15
|
+
if (input.skills?.some(item => Object.keys(item.files).some(path => path !== 'SKILL.md'))) {
|
|
16
|
+
throw new Error('通用执行器不挂载 Skill 资源文件;请使用提供只读工作区的执行器');
|
|
17
|
+
}
|
|
15
18
|
const memberTools = await Promise.all(input.members.map(async (member) => createTypedAgentTool(await member.agent.createCapability({
|
|
16
19
|
tracer: input.tracer,
|
|
17
20
|
modelClient: input.modelClient,
|
|
@@ -30,6 +33,7 @@ export const createAgentSpecExecutor = (declaration) => ({
|
|
|
30
33
|
...(input.model.apiKey === undefined ? {} : { apiKey: input.model.apiKey }),
|
|
31
34
|
modelClient: input.modelClient,
|
|
32
35
|
systemPrompt: input.systemPrompt,
|
|
36
|
+
...(input.skills === undefined ? {} : { skills: input.skills.map(item => skill({ name: item.name, description: item.description, instructions: item.instructions })) }),
|
|
33
37
|
outputSchema: declaration.output,
|
|
34
38
|
tools: [...input.tools, ...memberTools],
|
|
35
39
|
...(maxTokens === undefined ? {} : { maxTokens }),
|
package/dist/agent_version.d.ts
CHANGED
|
@@ -4,9 +4,18 @@ import type { Capability } from '@onco-foundry/capability-registry';
|
|
|
4
4
|
import type { CapabilityDelegation } from '@onco-foundry/agent-runtime';
|
|
5
5
|
import type { Tracer } from '@onco-foundry/trace-port';
|
|
6
6
|
import type { AgentTemplateCard, TemplateToolDefinition } from './agent_template.ts';
|
|
7
|
+
import type { ResolvedSkillPackage, SkillPackageReference, SkillPackageSummary } from './skill_packages.ts';
|
|
7
8
|
export declare const agentResourceVersionSchema: z.ZodString;
|
|
9
|
+
declare const resourceReferenceSchema: z.ZodObject<{
|
|
10
|
+
name: z.ZodString;
|
|
11
|
+
resource: z.ZodString;
|
|
12
|
+
version: z.ZodString;
|
|
13
|
+
}, z.core.$strict>;
|
|
8
14
|
export declare const agentVersionSchema: z.ZodObject<{
|
|
9
|
-
schemaVersion: z.
|
|
15
|
+
schemaVersion: z.ZodEnum<{
|
|
16
|
+
"agent-config-v1": "agent-config-v1";
|
|
17
|
+
"agent-config-v2": "agent-config-v2";
|
|
18
|
+
}>;
|
|
10
19
|
executor: z.ZodObject<{
|
|
11
20
|
id: z.ZodString;
|
|
12
21
|
version: z.ZodString;
|
|
@@ -44,6 +53,15 @@ export declare const agentVersionSchema: z.ZodObject<{
|
|
|
44
53
|
id: z.ZodString;
|
|
45
54
|
version: z.ZodString;
|
|
46
55
|
}, z.core.$strict>>;
|
|
56
|
+
resources: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
57
|
+
name: z.ZodString;
|
|
58
|
+
resource: z.ZodString;
|
|
59
|
+
version: z.ZodString;
|
|
60
|
+
}, z.core.$strict>>>;
|
|
61
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
62
|
+
id: z.ZodString;
|
|
63
|
+
version: z.ZodString;
|
|
64
|
+
}, z.core.$strict>>>;
|
|
47
65
|
limits: z.ZodOptional<z.ZodObject<{
|
|
48
66
|
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
49
67
|
maxTokens: z.ZodOptional<z.ZodNumber>;
|
|
@@ -57,6 +75,15 @@ export type AgentVersionPromptReference = AgentVersion['prompt'];
|
|
|
57
75
|
export type AgentVersionCommonPromptReference = AgentVersion['commonPrompts'][number];
|
|
58
76
|
export type AgentVersionToolReference = AgentVersion['tools'][number];
|
|
59
77
|
export type AgentVersionMemberReference = AgentVersion['members'][number];
|
|
78
|
+
export type AgentVersionResourceReference = z.infer<typeof resourceReferenceSchema>;
|
|
79
|
+
export type ResolvedAgentResource = AgentVersionResourceReference & {
|
|
80
|
+
checksum: string;
|
|
81
|
+
files: ReadonlyMap<string, string>;
|
|
82
|
+
};
|
|
83
|
+
export type AgentResourceSnapshot = AgentVersionResourceReference & {
|
|
84
|
+
checksum: string;
|
|
85
|
+
files: Record<string, string>;
|
|
86
|
+
};
|
|
60
87
|
export type ResolvedAgentModel = AgentVersionModel & {
|
|
61
88
|
apiKey?: string;
|
|
62
89
|
};
|
|
@@ -74,6 +101,8 @@ export type ResolvedAgentVersion = {
|
|
|
74
101
|
}>;
|
|
75
102
|
tools: AgentVersionToolReference[];
|
|
76
103
|
members: AgentVersionMemberReference[];
|
|
104
|
+
resources?: AgentResourceSnapshot[];
|
|
105
|
+
skills?: SkillPackageSummary[];
|
|
77
106
|
limits?: AgentVersion['limits'];
|
|
78
107
|
};
|
|
79
108
|
export type ResolvedAgentSnapshot = {
|
|
@@ -102,6 +131,8 @@ export type ResolvedAgent = {
|
|
|
102
131
|
version: string;
|
|
103
132
|
};
|
|
104
133
|
readSnapshot(): ResolvedAgentSnapshot;
|
|
134
|
+
readResources(): ResolvedAgentResource[];
|
|
135
|
+
readSkills(): ResolvedSkillPackage[];
|
|
105
136
|
};
|
|
106
137
|
export type ResolvedAgentMember = {
|
|
107
138
|
id: string;
|
|
@@ -114,8 +145,10 @@ export type AgentExecutorSpecInput<C> = {
|
|
|
114
145
|
model: ResolvedAgentModel;
|
|
115
146
|
systemPrompt: string;
|
|
116
147
|
tools: TemplateToolDefinition[];
|
|
148
|
+
resources: ResolvedAgentResource[];
|
|
117
149
|
members: ResolvedAgentMember[];
|
|
118
150
|
context: C;
|
|
151
|
+
skills?: ResolvedSkillPackage[];
|
|
119
152
|
tracer?: Tracer;
|
|
120
153
|
modelClient?: ModelClient;
|
|
121
154
|
submitOutputEndTurn?: boolean;
|
|
@@ -140,8 +173,12 @@ export type AgentVersionPorts<C = unknown> = {
|
|
|
140
173
|
readCommonPrompt(reference: AgentVersionCommonPromptReference, agentId: string): Promise<{
|
|
141
174
|
content: string;
|
|
142
175
|
}>;
|
|
176
|
+
readResource?(reference: AgentVersionResourceReference): Promise<{
|
|
177
|
+
files: ReadonlyMap<string, string>;
|
|
178
|
+
}>;
|
|
143
179
|
createTool(reference: AgentVersionToolReference, context: C): TemplateToolDefinition | Promise<TemplateToolDefinition>;
|
|
144
180
|
resolveExecutor(reference: AgentVersionExecutorReference): AgentVersionExecutor<C>;
|
|
181
|
+
readSkill?(reference: SkillPackageReference): Promise<ResolvedSkillPackage>;
|
|
145
182
|
};
|
|
146
183
|
export type ResolveAgentVersionInput<C = unknown> = {
|
|
147
184
|
agentId: string;
|
|
@@ -150,3 +187,4 @@ export type ResolveAgentVersionInput<C = unknown> = {
|
|
|
150
187
|
promptVersion?: string;
|
|
151
188
|
memberPromptVersions?: Record<string, string>;
|
|
152
189
|
};
|
|
190
|
+
export {};
|
package/dist/agent_version.js
CHANGED
|
@@ -26,8 +26,13 @@ const memberReferenceSchema = z.object({
|
|
|
26
26
|
id: z.string().min(1),
|
|
27
27
|
version: agentResourceVersionSchema,
|
|
28
28
|
}).strict();
|
|
29
|
+
const resourceReferenceSchema = z.object({
|
|
30
|
+
name: z.string().min(1),
|
|
31
|
+
resource: z.string().min(1),
|
|
32
|
+
version: agentResourceVersionSchema,
|
|
33
|
+
}).strict();
|
|
29
34
|
export const agentVersionSchema = z.object({
|
|
30
|
-
schemaVersion: z.
|
|
35
|
+
schemaVersion: z.enum(['agent-config-v1', 'agent-config-v2']),
|
|
31
36
|
executor: executorReferenceSchema,
|
|
32
37
|
model: z.object({
|
|
33
38
|
configVersion: z.string().min(1),
|
|
@@ -41,9 +46,16 @@ export const agentVersionSchema = z.object({
|
|
|
41
46
|
commonPrompts: z.array(commonPromptReferenceSchema),
|
|
42
47
|
tools: z.array(localToolReferenceSchema),
|
|
43
48
|
members: z.array(memberReferenceSchema),
|
|
49
|
+
resources: z.array(resourceReferenceSchema).refine(items => new Set(items.map(item => item.name)).size === items.length, '业务资源名称不能重复').optional(),
|
|
50
|
+
skills: z.array(z.object({ id: z.string().regex(/^[a-z][a-z0-9-]{0,79}$/), version: agentResourceVersionSchema }).strict()).max(32).optional(),
|
|
44
51
|
limits: z.object({
|
|
45
52
|
maxTurns: z.number().int().positive().optional(),
|
|
46
53
|
maxTokens: z.number().int().min(2).optional(),
|
|
47
54
|
thinkingBudgetTokens: z.number().int().positive().optional(),
|
|
48
55
|
}).strict().optional(),
|
|
49
|
-
}).strict()
|
|
56
|
+
}).strict().superRefine((value, ctx) => {
|
|
57
|
+
if (value.schemaVersion === 'agent-config-v1' && value.skills !== undefined)
|
|
58
|
+
ctx.addIssue({ code: 'custom', path: ['skills'], message: 'Skill 引用需要 agent-config-v2' });
|
|
59
|
+
if (value.schemaVersion === 'agent-config-v2' && value.skills === undefined)
|
|
60
|
+
ctx.addIssue({ code: 'custom', path: ['skills'], message: 'agent-config-v2 必须声明 skills' });
|
|
61
|
+
});
|
|
@@ -39,8 +39,12 @@ export const createAgentVersionResolver = (ports) => {
|
|
|
39
39
|
prompt: { ...record.prompt },
|
|
40
40
|
systemPromptChecksum: record.systemPromptChecksum,
|
|
41
41
|
commonPrompts: record.commonPrompts.map(({ content: _content, ...part }) => ({ ...part })),
|
|
42
|
+
...(record.resources.length ? { resources: record.resources.map(({ files, ...reference }) => ({
|
|
43
|
+
...reference, files: Object.fromEntries([...files].map(([name, content]) => [name, checksum(content)])),
|
|
44
|
+
})) } : {}),
|
|
42
45
|
tools: record.config.tools.map(tool => ({ ...tool })),
|
|
43
46
|
members: record.config.members.map(member => ({ ...member })),
|
|
47
|
+
...(record.skills === undefined ? {} : { skills: record.skills.map(({ id, version, name, description, checksum }) => ({ id, version, name, description, checksum })) }),
|
|
44
48
|
...(record.config.limits === undefined ? {} : { limits: { ...record.config.limits } }),
|
|
45
49
|
};
|
|
46
50
|
for (const member of record.config.members)
|
|
@@ -85,6 +89,23 @@ export const createAgentVersionResolver = (ports) => {
|
|
|
85
89
|
try {
|
|
86
90
|
const executor = ports.resolveExecutor(loaded.config.executor);
|
|
87
91
|
executor.validate?.({ agentId, config: loaded.config });
|
|
92
|
+
const skills = loaded.config.skills === undefined ? undefined : [];
|
|
93
|
+
const skillNames = new Set();
|
|
94
|
+
const skillIds = new Set();
|
|
95
|
+
for (const reference of loaded.config.skills ?? []) {
|
|
96
|
+
if (!ports.readSkill)
|
|
97
|
+
throw new AppError('当前宿主未提供 Skill 读取端口');
|
|
98
|
+
if (skillIds.has(reference.id))
|
|
99
|
+
throw new AppError(`重复的 Skill 引用:${reference.id}`);
|
|
100
|
+
const skill = await ports.readSkill(reference);
|
|
101
|
+
if (skill.id !== reference.id || skill.version !== reference.version || !/^[a-f0-9]{64}$/.test(skill.checksum))
|
|
102
|
+
throw new AppError(`Skill 解析结果与精确引用不符:${reference.id}`);
|
|
103
|
+
if (skillNames.has(skill.name))
|
|
104
|
+
throw new AppError(`Skill 名称冲突:${skill.name}`);
|
|
105
|
+
skillIds.add(reference.id);
|
|
106
|
+
skillNames.add(skill.name);
|
|
107
|
+
skills.push(structuredClone(skill));
|
|
108
|
+
}
|
|
88
109
|
const promptVersion = agentId === input.agentId
|
|
89
110
|
? input.promptVersion
|
|
90
111
|
: input.memberPromptVersions?.[agentId];
|
|
@@ -100,6 +121,15 @@ export const createAgentVersionResolver = (ports) => {
|
|
|
100
121
|
const { content } = await ports.readCommonPrompt(reference, agentId);
|
|
101
122
|
return { ...reference, content, checksum: checksum(content) };
|
|
102
123
|
}));
|
|
124
|
+
const resources = await Promise.all((loaded.config.resources ?? []).map(async (reference) => {
|
|
125
|
+
if (!ports.readResource)
|
|
126
|
+
throw new AppError(`未配置业务资源读取端口:${reference.name}`);
|
|
127
|
+
const resource = await ports.readResource(reference);
|
|
128
|
+
const files = new Map([...resource.files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
|
|
129
|
+
if (!files.size)
|
|
130
|
+
throw new AppError(`业务资源没有文件:${reference.resource}@${reference.version}`);
|
|
131
|
+
return { ...reference, files, checksum: checksum(JSON.stringify([...files])) };
|
|
132
|
+
}));
|
|
103
133
|
const ordered = [...commonPrompts].sort((a, b) => a.placement === b.placement
|
|
104
134
|
? a.order - b.order || a.id.localeCompare(b.id)
|
|
105
135
|
: a.placement === 'before' ? -1 : 1);
|
|
@@ -119,8 +149,10 @@ export const createAgentVersionResolver = (ports) => {
|
|
|
119
149
|
model: { ...loaded.config.model, ...modelCredential },
|
|
120
150
|
prompt: { ...promptReference, checksum: checksum(prompt.content) },
|
|
121
151
|
commonPrompts,
|
|
152
|
+
resources,
|
|
122
153
|
systemPrompt,
|
|
123
154
|
systemPromptChecksum: checksum(systemPrompt),
|
|
155
|
+
...(skills === undefined ? {} : { skills }),
|
|
124
156
|
};
|
|
125
157
|
records.set(agentId, record);
|
|
126
158
|
const createSpec = async (options = {}) => {
|
|
@@ -135,7 +167,9 @@ export const createAgentVersionResolver = (ports) => {
|
|
|
135
167
|
config: record.config,
|
|
136
168
|
model: record.model,
|
|
137
169
|
systemPrompt: record.systemPrompt,
|
|
170
|
+
...(record.skills === undefined ? {} : { skills: structuredClone(record.skills) }),
|
|
138
171
|
tools,
|
|
172
|
+
resources: structuredClone(record.resources),
|
|
139
173
|
members: await Promise.all(record.config.members.map(async (member) => ({
|
|
140
174
|
id: member.id,
|
|
141
175
|
version: member.version,
|
|
@@ -172,6 +206,8 @@ export const createAgentVersionResolver = (ports) => {
|
|
|
172
206
|
},
|
|
173
207
|
readSystemPrompt: () => ({ content: record.systemPrompt, version: record.prompt.version }),
|
|
174
208
|
readSnapshot: () => structuredClone(snapshotFor(agentId)),
|
|
209
|
+
readResources: () => structuredClone(record.resources),
|
|
210
|
+
readSkills: () => structuredClone(record.skills ?? []),
|
|
175
211
|
};
|
|
176
212
|
record.agent = agent;
|
|
177
213
|
for (const member of record.config.members)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TemplateToolDefinition } from './agent_template.ts';
|
|
2
|
-
import
|
|
2
|
+
import { type SkillPackageFiles, type SkillPackageReference } from './skill_packages.ts';
|
|
3
|
+
import type { AgentVersion, AgentVersionCommonPromptReference, AgentVersionExecutor, AgentVersionPorts, AgentVersionPromptReference, AgentVersionResourceReference } from './agent_version.ts';
|
|
3
4
|
export declare const createFakeAgentVersionPorts: <C>(options: {
|
|
4
5
|
executors: Record<string, AgentVersionExecutor<C>>;
|
|
5
6
|
modelApiKey?: string;
|
|
@@ -7,6 +8,8 @@ export declare const createFakeAgentVersionPorts: <C>(options: {
|
|
|
7
8
|
}) => AgentVersionPorts<C> & {
|
|
8
9
|
writeAgentVersion(agentId: string, version: string, config: AgentVersion, activate?: boolean): Promise<void>;
|
|
9
10
|
activateAgentVersion(agentId: string, version: string): Promise<void>;
|
|
11
|
+
setResource(reference: AgentVersionResourceReference, files: Record<string, string>): void;
|
|
10
12
|
setPrompt(reference: AgentVersionPromptReference, content: string): void;
|
|
11
13
|
setCommonPrompt(reference: AgentVersionCommonPromptReference, content: string): void;
|
|
14
|
+
writeSkill(reference: SkillPackageReference, files: SkillPackageFiles): Promise<void>;
|
|
12
15
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AppError } from '@onco-foundry/errors';
|
|
2
2
|
import { activateVersion, createFakeVersionedResourceStore, createVersionedResource, } from '@onco-foundry/resource-versioning';
|
|
3
|
+
import { createSkillPackages } from './skill_packages.js';
|
|
3
4
|
const promptKey = (reference) => `${reference.resource}@${reference.version}`;
|
|
4
5
|
const commonPromptKey = (reference) => `${reference.source}:${reference.id}@${reference.version}`;
|
|
5
6
|
const executorKey = (reference) => `${reference.id}@${reference.version}`;
|
|
@@ -8,6 +9,13 @@ export const createFakeAgentVersionPorts = (options) => {
|
|
|
8
9
|
const stores = new Map();
|
|
9
10
|
const prompts = new Map();
|
|
10
11
|
const commonPrompts = new Map();
|
|
12
|
+
const resources = new Map();
|
|
13
|
+
const skillStores = new Map();
|
|
14
|
+
const skills = createSkillPackages({ storeFor: id => {
|
|
15
|
+
if (!skillStores.has(id))
|
|
16
|
+
skillStores.set(id, createFakeVersionedResourceStore());
|
|
17
|
+
return skillStores.get(id);
|
|
18
|
+
} });
|
|
11
19
|
const ports = {
|
|
12
20
|
storeFor(agentId) {
|
|
13
21
|
let store = stores.get(agentId);
|
|
@@ -17,6 +25,8 @@ export const createFakeAgentVersionPorts = (options) => {
|
|
|
17
25
|
}
|
|
18
26
|
return store;
|
|
19
27
|
},
|
|
28
|
+
readSkill: reference => skills.read(reference),
|
|
29
|
+
async writeSkill(reference, files) { await skills.publish({ ...reference, files }); },
|
|
20
30
|
resolveModel: async () => options.modelApiKey === undefined ? {} : { apiKey: options.modelApiKey },
|
|
21
31
|
readPrompt: async (reference) => {
|
|
22
32
|
const content = prompts.get(promptKey(reference));
|
|
@@ -30,6 +40,13 @@ export const createFakeAgentVersionPorts = (options) => {
|
|
|
30
40
|
throw new AppError(`fake 共同 prompt 不存在:${commonPromptKey(reference)}`);
|
|
31
41
|
return { content };
|
|
32
42
|
},
|
|
43
|
+
readResource: async (reference) => {
|
|
44
|
+
const files = resources.get(promptKey(reference));
|
|
45
|
+
if (!files)
|
|
46
|
+
throw new AppError(`fake 业务资源不存在:${promptKey(reference)}`);
|
|
47
|
+
return { files: new Map(Object.entries(files)) };
|
|
48
|
+
},
|
|
49
|
+
setResource(reference, files) { resources.set(promptKey(reference), { ...files }); },
|
|
33
50
|
createTool(reference, context) {
|
|
34
51
|
const create = options.tools?.[toolKey(reference)];
|
|
35
52
|
if (create === undefined)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { clampThinkingBudgetTokens, defineAgentTemplate, promptManifestSchema, type AgentTemplate, type AgentTemplateCard, type AgentTemplateDeclaration, type CreateTemplateAgentOptions, type PromptManifestAdapter, type TemplateToolDefinition, } from './agent_template.ts';
|
|
2
2
|
export type { CapabilityDelegation } from '@onco-foundry/agent-runtime';
|
|
3
3
|
export { createAgentSpecExecutor, type AgentSpecExecutorDeclaration } from './agent_spec_executor.ts';
|
|
4
|
-
export { agentResourceVersionSchema, agentVersionSchema, type AgentVersion, type AgentVersionCommonPromptReference, type AgentVersionExecutor, type AgentVersionExecutorReference, type AgentVersionMemberReference, type AgentVersionModel, type AgentVersionPorts, type AgentVersionPromptReference, type AgentVersionToolReference, type CreateResolvedAgentSpecOptions, type ResolveAgentVersionInput, type ResolvedAgent, type ResolvedAgentMember, type ResolvedAgentModel, type ResolvedAgentSnapshot, type ResolvedAgentVersion, } from './agent_version.ts';
|
|
4
|
+
export { agentResourceVersionSchema, agentVersionSchema, type AgentVersion, type AgentVersionResourceReference, type AgentResourceSnapshot, type ResolvedAgentResource, type AgentVersionCommonPromptReference, type AgentVersionExecutor, type AgentVersionExecutorReference, type AgentVersionMemberReference, type AgentVersionModel, type AgentVersionPorts, type AgentVersionPromptReference, type AgentVersionToolReference, type CreateResolvedAgentSpecOptions, type ResolveAgentVersionInput, type ResolvedAgent, type ResolvedAgentMember, type ResolvedAgentModel, type ResolvedAgentSnapshot, type ResolvedAgentVersion, } from './agent_version.ts';
|
|
5
5
|
export { createAgentVersionResolver } from './agent_version_resolver.ts';
|
|
6
6
|
export { createFakeAgentVersionPorts } from './fake_agent_version_ports.ts';
|
|
7
|
+
export { createSkillPackages, type SkillPackageFiles, type SkillPackageReference, type SkillPackageSummary, type ResolvedSkillPackage } from './skill_packages.ts';
|
package/dist/index.js
CHANGED
|
@@ -3,3 +3,4 @@ export { createAgentSpecExecutor } from './agent_spec_executor.js';
|
|
|
3
3
|
export { agentResourceVersionSchema, agentVersionSchema, } from './agent_version.js';
|
|
4
4
|
export { createAgentVersionResolver } from './agent_version_resolver.js';
|
|
5
5
|
export { createFakeAgentVersionPorts } from './fake_agent_version_ports.js';
|
|
6
|
+
export { createSkillPackages } from './skill_packages.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type VersionedResourceStore } from '@onco-foundry/resource-versioning';
|
|
3
|
+
declare const filesSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
4
|
+
encoding: z.ZodEnum<{
|
|
5
|
+
base64: "base64";
|
|
6
|
+
utf8: "utf8";
|
|
7
|
+
}>;
|
|
8
|
+
content: z.ZodString;
|
|
9
|
+
}, z.core.$strict>>;
|
|
10
|
+
export type SkillPackageFiles = z.infer<typeof filesSchema>;
|
|
11
|
+
export type SkillPackageReference = {
|
|
12
|
+
id: string;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
export type SkillPackageSummary = SkillPackageReference & {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
checksum: string;
|
|
19
|
+
};
|
|
20
|
+
export type ResolvedSkillPackage = SkillPackageSummary & {
|
|
21
|
+
instructions: string;
|
|
22
|
+
files: SkillPackageFiles;
|
|
23
|
+
};
|
|
24
|
+
/** 文件是数据,不接收文件系统路径或链接;实际只读挂载由宿主运行器负责。 */
|
|
25
|
+
export declare const createSkillPackages: (deps: {
|
|
26
|
+
storeFor(id: string): VersionedResourceStore;
|
|
27
|
+
}) => {
|
|
28
|
+
publish(input: SkillPackageReference & {
|
|
29
|
+
files: SkillPackageFiles;
|
|
30
|
+
}): Promise<ResolvedSkillPackage>;
|
|
31
|
+
read: (reference: SkillPackageReference) => Promise<ResolvedSkillPackage>;
|
|
32
|
+
};
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { parse } from 'yaml';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createVersionedResource, loadActiveVersionedResource } from '@onco-foundry/resource-versioning';
|
|
5
|
+
import { AppError } from '@onco-foundry/errors';
|
|
6
|
+
const idSchema = z.string().regex(/^[a-z][a-z0-9-]{0,79}$/);
|
|
7
|
+
const versionSchema = z.string().regex(/^v[1-9]\d{0,8}$/);
|
|
8
|
+
const fileSchema = z.object({ encoding: z.enum(['utf8', 'base64']), content: z.string().max(24 * 1024 * 1024) }).strict();
|
|
9
|
+
const filesSchema = z.record(z.string(), fileSchema);
|
|
10
|
+
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
11
|
+
/** 文件是数据,不接收文件系统路径或链接;实际只读挂载由宿主运行器负责。 */
|
|
12
|
+
export const createSkillPackages = (deps) => {
|
|
13
|
+
const inspect = (input) => {
|
|
14
|
+
const files = filesSchema.parse(input);
|
|
15
|
+
const paths = Object.keys(files).sort();
|
|
16
|
+
if (paths.length === 0 || paths.length > 128)
|
|
17
|
+
throw new AppError('技能包文件数必须为 1 到 128');
|
|
18
|
+
let size = 0;
|
|
19
|
+
for (const path of paths) {
|
|
20
|
+
const parts = path.split('/');
|
|
21
|
+
if (path.length > 240 || parts.some(part => !part || part === '.' || part === '..' || /[\\:\x00-\x1f\x7f]/.test(part))) {
|
|
22
|
+
throw new AppError(`技能包文件路径非法:${path}`);
|
|
23
|
+
}
|
|
24
|
+
if (parts.at(-1) === 'SKILL.md' && path !== 'SKILL.md')
|
|
25
|
+
throw new AppError('技能包只能在根目录包含一个 SKILL.md');
|
|
26
|
+
for (let count = 1; count < parts.length; count++) {
|
|
27
|
+
if (Object.hasOwn(files, parts.slice(0, count).join('/')))
|
|
28
|
+
throw new AppError(`技能包文件与目录冲突:${path}`);
|
|
29
|
+
}
|
|
30
|
+
const file = files[path];
|
|
31
|
+
const bytes = Buffer.from(file.content, file.encoding === 'utf8' ? 'utf8' : 'base64');
|
|
32
|
+
if (file.encoding === 'base64' && bytes.toString('base64') !== file.content)
|
|
33
|
+
throw new AppError(`技能包 base64 非法:${path}`);
|
|
34
|
+
size += bytes.byteLength;
|
|
35
|
+
if (size > 16 * 1024 * 1024)
|
|
36
|
+
throw new AppError('技能包解码后不能超过 16 MiB');
|
|
37
|
+
}
|
|
38
|
+
const skill = files['SKILL.md'];
|
|
39
|
+
if (!skill || skill.encoding !== 'utf8' || Buffer.byteLength(skill.content) > 100_000)
|
|
40
|
+
throw new AppError('技能包必须包含 UTF-8 SKILL.md,且不能超过 100000 字节');
|
|
41
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]+)$/.exec(skill.content);
|
|
42
|
+
if (!match)
|
|
43
|
+
throw new AppError('SKILL.md 必须包含 YAML frontmatter 和正文');
|
|
44
|
+
const metadata = z.object({ name: idSchema, description: z.string().trim().min(1).max(2048) }).passthrough().parse(parse(match[1], { maxAliasCount: 0, uniqueKeys: true }));
|
|
45
|
+
const instructions = match[2].trim();
|
|
46
|
+
if (!instructions)
|
|
47
|
+
throw new AppError('SKILL.md 正文不能为空');
|
|
48
|
+
return { name: metadata.name, description: metadata.description, instructions, files: Object.fromEntries(paths.map(path => [path, files[path]])) };
|
|
49
|
+
};
|
|
50
|
+
const read = async (reference) => {
|
|
51
|
+
idSchema.parse(reference.id);
|
|
52
|
+
versionSchema.parse(reference.version);
|
|
53
|
+
const loaded = await loadActiveVersionedResource({ store: deps.storeFor(reference.id), versionOverride: reference.version });
|
|
54
|
+
const content = loaded.files.get('skill.json');
|
|
55
|
+
if (content === undefined || Buffer.byteLength(content) > 24 * 1024 * 1024)
|
|
56
|
+
throw new AppError('技能包缺少 skill.json 或内容过大');
|
|
57
|
+
const bundle = z.object({ schemaVersion: z.literal('skill-package-v1'), files: filesSchema }).strict().parse(JSON.parse(content));
|
|
58
|
+
return { ...reference, ...inspect(bundle.files), checksum: hash(content) };
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
async publish(input) {
|
|
62
|
+
idSchema.parse(input.id);
|
|
63
|
+
versionSchema.parse(input.version);
|
|
64
|
+
const inspected = inspect(input.files);
|
|
65
|
+
const content = JSON.stringify({ schemaVersion: 'skill-package-v1', files: inspected.files });
|
|
66
|
+
if (Buffer.byteLength(content) > 24 * 1024 * 1024)
|
|
67
|
+
throw new AppError('技能包编码后不能超过 24 MiB');
|
|
68
|
+
await createVersionedResource({ store: deps.storeFor(input.id), version: input.version,
|
|
69
|
+
files: { 'skill.json': content } });
|
|
70
|
+
return await read({ id: input.id, version: input.version });
|
|
71
|
+
},
|
|
72
|
+
read,
|
|
73
|
+
};
|
|
74
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onco-foundry/agent-template",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"agent-lattice": "0.26.0",
|
|
16
|
+
"yaml": "^2.9.1",
|
|
16
17
|
"zod": "^4.4.3",
|
|
17
18
|
"@onco-foundry/agent-runtime": "0.3.0",
|
|
18
|
-
"@onco-foundry/capability-registry": "0.1.1",
|
|
19
|
-
"@onco-foundry/resource-versioning": "0.3.0",
|
|
20
19
|
"@onco-foundry/trace-port": "0.3.3",
|
|
21
|
-
"@onco-foundry/
|
|
20
|
+
"@onco-foundry/resource-versioning": "0.3.0",
|
|
21
|
+
"@onco-foundry/errors": "0.1.1",
|
|
22
|
+
"@onco-foundry/capability-registry": "0.1.1"
|
|
22
23
|
},
|
|
23
24
|
"publishConfig": {
|
|
24
25
|
"access": "public"
|