oricore 1.5.0 → 1.5.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.
@@ -1,11 +1,15 @@
1
1
  import path from 'pathe';
2
2
  import { z } from 'zod';
3
+ import type { Context } from '../../core/context';
3
4
  import type { SkillManager, SkillMetadata } from '../../skill/skill';
4
- import { createTool } from '../tool';
5
+ import { createTool, type Tool } from '../tool';
5
6
  import { safeStringify } from '../../utils/safeStringify';
7
+ import { createTaskTool } from './task';
8
+ import { randomUUID } from '../../utils/randomUUID';
6
9
 
7
10
  function renderAvailableSkills(skills: SkillMetadata[]): string {
8
11
  return skills
12
+ .filter((skill) => skill.modelInvocable !== false)
9
13
  .map(
10
14
  (skill) =>
11
15
  `<skill>\n<name>${skill.name}</name>\n<description>${skill.description}</description>\n</skill>`,
@@ -14,7 +18,7 @@ function renderAvailableSkills(skills: SkillMetadata[]): string {
14
18
  }
15
19
 
16
20
  function generateDescription(skillManager: SkillManager): string {
17
- const skills = skillManager.getSkills();
21
+ const skills = skillManager.getSkills({ modelInvocable: true });
18
22
  return `Execute a skill within the main conversation
19
23
  <skills_instructions>
20
24
  When users ask you to perform tasks, check if any of the available skills below match the task. If a skill matches, use this tool to invoke it. Skills provide specialized knowledge and procedures for specific tasks.
@@ -24,22 +28,38 @@ ${renderAvailableSkills(skills)}
24
28
  </available_skills>`;
25
29
  }
26
30
 
27
- export function createSkillTool(opts: { skillManager: SkillManager }) {
31
+ export interface CreateSkillToolOpts {
32
+ skillManager: SkillManager;
33
+ context: Context;
34
+ tools: Tool[];
35
+ sessionId: string;
36
+ signal?: AbortSignal;
37
+ }
38
+
39
+ export function createSkillTool(opts: CreateSkillToolOpts) {
40
+ const { skillManager, context, tools, sessionId, signal } = opts;
41
+
28
42
  return createTool({
29
43
  name: 'skill',
30
- description: generateDescription(opts.skillManager),
44
+ description: generateDescription(skillManager),
31
45
  parameters: z.object({
32
46
  skill: z.string().describe('The skill name to execute'),
47
+ args: z
48
+ .string()
49
+ .optional()
50
+ .describe('Optional arguments to pass to the skill'),
33
51
  }),
34
52
  getDescription: ({ params }) => {
35
- return params.skill;
53
+ return params.args
54
+ ? `${params.skill} ${params.args}`
55
+ : params.skill;
36
56
  },
37
- async execute({ skill }) {
57
+ async execute({ skill, args }) {
38
58
  const trimmed = skill.trim();
39
59
  const skillName = trimmed.startsWith('/')
40
60
  ? trimmed.substring(1)
41
61
  : trimmed;
42
- const foundSkill = opts.skillManager.getSkill(skillName);
62
+ const foundSkill = skillManager.getSkill(skillName);
43
63
 
44
64
  if (!foundSkill) {
45
65
  return {
@@ -48,9 +68,67 @@ export function createSkillTool(opts: { skillManager: SkillManager }) {
48
68
  };
49
69
  }
50
70
 
51
- const body = await opts.skillManager.readSkillBody(foundSkill);
71
+ // Check if skill can be invoked by model
72
+ if (foundSkill.modelInvocable === false) {
73
+ return {
74
+ isError: true,
75
+ llmContent: `Skill "${skillName}" cannot be invoked by the model`,
76
+ };
77
+ }
78
+
79
+ const skillArgs = args || '';
80
+ const body = await skillManager.readSkillBody(foundSkill, skillArgs);
52
81
  const baseDir = path.dirname(foundSkill.path);
53
82
 
83
+ // If skill has context: 'fork', use task tool for isolated execution
84
+ if (foundSkill.context === 'fork') {
85
+ if (!context.agentManager) {
86
+ return {
87
+ isError: true,
88
+ llmContent: `Skill "${skillName}" requires fork execution but agent manager is not available`,
89
+ };
90
+ }
91
+
92
+ // Create filtered tools list based on allowedTools
93
+ // Exclude 'skill' tool itself to prevent recursive invocation loops
94
+ const allowedTools = foundSkill.allowedTools;
95
+ const filteredTools = allowedTools
96
+ ? tools.filter(
97
+ (t) =>
98
+ t.name !== 'skill' &&
99
+ allowedTools.some(
100
+ (allowed) =>
101
+ allowed.toLowerCase() === t.name.toLowerCase(),
102
+ ),
103
+ )
104
+ : tools.filter((t) => t.name !== 'skill');
105
+
106
+ // Create task tool with filtered tools
107
+ const taskTool = createTaskTool({
108
+ context,
109
+ tools: filteredTools,
110
+ sessionId,
111
+ signal,
112
+ });
113
+
114
+ // Execute skill as a task
115
+ const agentType = foundSkill.agent || 'general-purpose';
116
+ const prompt = `Base directory for this skill: ${baseDir}\n\n${body}`;
117
+
118
+ // Generate a unique toolCallId for tracking
119
+ const toolCallId = `skill-${skillName}-${randomUUID()}`;
120
+
121
+ return taskTool.execute(
122
+ {
123
+ description: `Execute skill: ${skillName}`,
124
+ prompt,
125
+ subagent_type: agentType,
126
+ },
127
+ toolCallId,
128
+ );
129
+ }
130
+
131
+ // Inline execution (default)
54
132
  const messages = [
55
133
  {
56
134
  type: 'text',