This commit is contained in:
Timothy Jaeryang Baek 2026-10-13 19:51:58 +04:00
parent 50a62831b3
commit 7840194631
3 changed files with 30 additions and 14 deletions

View file

@ -2844,7 +2844,6 @@ async def process_chat_payload(request, form_data, user, metadata, model):
)
available_skills = []
terminal_skills = []
view_skill_ids = []
if is_note_chat:
note_id = (chat.meta or {}).get('note_id')
note = await Notes.get_note_by_id(note_id) if note_id else None
@ -2931,7 +2930,6 @@ async def process_chat_payload(request, form_data, user, metadata, model):
append=True,
)
else:
view_skill_ids.append(skill.id)
skill_manifest += (
f'<skill>\n<id>{skill.id}</id>\n<name>{skill.name}</name>\n'
f'<description>{skill.description or ""}</description>\n</skill>\n'
@ -2973,7 +2971,6 @@ async def process_chat_payload(request, form_data, user, metadata, model):
append=True,
)
else:
view_skill_ids.append(sid)
skill_manifest += format_terminal_skill_manifest_entry(skill)
if skill_manifest:
@ -3221,7 +3218,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
{
**extra_params,
'__event_emitter__': event_emitter,
'__skill_ids__': view_skill_ids,
'__skill_ids__': sorted(resolved_skill_ids),
},
features,
model,

View file

@ -162,8 +162,12 @@ def grep_skill_content(files: list[dict], pattern: str, case_insensitive: bool =
}
candidates = [*result['matches'], entry]
candidate = {
**result, 'matches': candidates, 'truncated': True, 'notice': notice,
'omitted_matches': [], 'omitted_matches_truncated': False,
**result,
'matches': candidates,
'truncated': True,
'notice': notice,
'omitted_matches': [],
'omitted_matches_truncated': False,
}
if (
len(candidates) <= max_results
@ -175,10 +179,18 @@ def grep_skill_content(files: list[dict], pattern: str, case_insensitive: bool =
continue
accepting = False
result.update(truncated=True, notice=notice, omitted_matches=[], omitted_matches_truncated=False)
locations = [*result['omitted_matches'], {
'path': file['path'], 'offset': start, 'section': heading['title'] if heading else None,
}]
if len(locations) <= 30 and len(JSONCodec.dumps(locations, ensure_ascii=False)) <= SKILL_GREP_LOCATIONS_MAX_CHARS:
locations = [
*result['omitted_matches'],
{
'path': file['path'],
'offset': start,
'section': heading['title'] if heading else None,
},
]
if (
len(locations) <= 30
and len(JSONCodec.dumps(locations, ensure_ascii=False)) <= SKILL_GREP_LOCATIONS_MAX_CHARS
):
result['omitted_matches'] = locations
else:
accepting_locations = False
@ -225,9 +237,7 @@ def format_skill_content(page: dict, skill_id: str, tools_enabled: bool) -> str:
if page['next_offset'] is not None:
content += '\nSkill instructions truncated.'
if tools_enabled:
content += (
f'\nread_skill_file(id={json.dumps(skill_id)}, path="SKILL.md", offset={page["next_offset"]}).'
)
content += f'\nread_skill_file(id={json.dumps(skill_id)}, path="SKILL.md", offset={page["next_offset"]}).'
return content

View file

@ -849,8 +849,13 @@ async def get_builtin_tools(
)
# Skills tools - overview, targeted search, and bounded reads of relevant passages.
skill_ids = sorted(set(extra_params.get('__skill_ids__') or []))
workspace_skill_ids = [skill_id for skill_id in skill_ids if not skill_id.startswith('terminal:')]
if is_builtin_tool_enabled('skills'):
builtin_functions.extend([view_skill, read_skill_file, grep_skill_files, update_skill_files])
if skill_ids:
builtin_functions.extend([view_skill, read_skill_file, grep_skill_files])
if workspace_skill_ids:
builtin_functions.append(update_skill_files)
if user.get('role') == 'admin' or await has_permission(
user.get('id', ''), 'workspace.skills', await Config.get('user.permissions')
):
@ -907,6 +912,10 @@ async def get_builtin_tools(
)
spec = get_builtin_tool_spec(func)
if func.__name__ in {'view_skill', 'read_skill_file', 'grep_skill_files'}:
spec['parameters']['properties']['id']['enum'] = skill_ids
elif func.__name__ == 'update_skill_files':
spec['parameters']['properties']['id']['enum'] = workspace_skill_ids
if func.__name__ == 'delegate_task' and not config.get('subagents.background_enabled'):
parameters = spec.get('parameters', {})
parameters.get('properties', {}).pop('background', None)