Skip to content

Commit 3163230

Browse files
Copilothotlong
andauthored
fix(ai): enforce snake_case validation on skill/tool name references
- SkillSchema.tools now validates snake_case pattern for tool name references - AgentSchema.skills now validates snake_case pattern for skill name references - Added tests for snake_case enforcement on both references Agent-Logs-Url: https://github.com/objectstack-ai/spec/sessions/5cefdc4a-fa65-4b56-876d-3ca34809f7b3 Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 80e50da commit 3163230

4 files changed

Lines changed: 48 additions & 2 deletions

File tree

packages/spec/src/ai/agent.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,32 @@ describe('AgentSchema', () => {
285285
const result = AgentSchema.parse(agent);
286286
expect(result.permissions).toEqual(['agent.basic', 'data.read']);
287287
});
288+
289+
it('should enforce snake_case for skill name references', () => {
290+
expect(() => AgentSchema.parse({
291+
name: 'test_agent',
292+
label: 'Test',
293+
role: 'Test',
294+
instructions: 'Test',
295+
skills: ['valid_skill', 'another_skill'],
296+
})).not.toThrow();
297+
298+
expect(() => AgentSchema.parse({
299+
name: 'test_agent',
300+
label: 'Test',
301+
role: 'Test',
302+
instructions: 'Test',
303+
skills: ['InvalidSkill'],
304+
})).toThrow();
305+
306+
expect(() => AgentSchema.parse({
307+
name: 'test_agent',
308+
label: 'Test',
309+
role: 'Test',
310+
instructions: 'Test',
311+
skills: ['valid_skill', 'Invalid-Skill'],
312+
})).toThrow();
313+
});
288314
});
289315

290316
describe('Access Control', () => {

packages/spec/src/ai/agent.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export const AgentSchema = z.object({
137137
lifecycle: StateMachineSchema.optional().describe('State machine defining the agent conversation follow and constraints'),
138138

139139
/** Capabilities — Skill-based (primary) */
140-
skills: z.array(z.string()).optional().describe('Skill names to attach (Agent→Skill→Tool architecture)'),
140+
skills: z.array(z.string().regex(/^[a-z_][a-z0-9_]*$/)).optional().describe('Skill names to attach (Agent→Skill→Tool architecture)'),
141141

142142
/** Capabilities — Direct tool references (fallback / legacy) */
143143
tools: z.array(AIToolSchema).optional().describe('Direct tool references (legacy fallback)'),

packages/spec/src/ai/skill.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,26 @@ describe('SkillSchema', () => {
114114
});
115115
expect(result.instructions).toContain('knowledge base');
116116
});
117+
118+
it('should enforce snake_case for tool name references', () => {
119+
expect(() => SkillSchema.parse({
120+
name: 'valid_skill',
121+
label: 'Test',
122+
tools: ['valid_tool', 'another_tool'],
123+
})).not.toThrow();
124+
125+
expect(() => SkillSchema.parse({
126+
name: 'valid_skill',
127+
label: 'Test',
128+
tools: ['InvalidTool'],
129+
})).toThrow();
130+
131+
expect(() => SkillSchema.parse({
132+
name: 'valid_skill',
133+
label: 'Test',
134+
tools: ['valid_tool', 'Invalid-Tool'],
135+
})).toThrow();
136+
});
117137
});
118138

119139
describe('defineSkill', () => {

packages/spec/src/ai/skill.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export const SkillSchema = z.object({
7171
* References to tool names that belong to this skill.
7272
* Tools must be registered as first-class metadata (type: 'tool').
7373
*/
74-
tools: z.array(z.string()).describe('Tool names belonging to this skill'),
74+
tools: z.array(z.string().regex(/^[a-z_][a-z0-9_]*$/)).describe('Tool names belonging to this skill'),
7575

7676
/**
7777
* Natural language phrases that trigger skill activation.

0 commit comments

Comments
 (0)