Skip to content

feat(skills): added skills to agent block#3149

Open
waleedlatif1 wants to merge 4 commits intostagingfrom
feat/skills
Open

feat(skills): added skills to agent block#3149
waleedlatif1 wants to merge 4 commits intostagingfrom
feat/skills

Conversation

@waleedlatif1
Copy link
Collaborator

@waleedlatif1 waleedlatif1 commented Feb 6, 2026

Summary

Adds Agent Skills as a first-class feature — reusable prompt/instruction packages that users can create, manage, and attach to Agent blocks. Follows the agentskills.io open specification.

Architecture

Skills use progressive disclosure to keep context lean:

  1. Metadata only (name + description) is injected into the system prompt as <available_skills> XML so the LLM knows what's available
  2. An auto-injected load_skill tool lets the LLM load full skill content on-demand when it decides a skill is relevant
  3. Full markdown instructions enter context as a tool response — not bloating the system prompt

This works across all providers (OpenAI, Anthropic, Gemini, etc.) using standard tool-calling — no provider-specific code needed.

What's included

  • DB schema: skill table with workspace-scoped unique name index
  • API: CRUD routes at /api/skills with workspace permission checks
  • Settings UI: Skills tab under Tools with create/edit/delete modal
  • Agent block: skill-input subblock (Combobox dropdown for skill selection)
  • Execution: skills-resolver.ts for metadata/content resolution, load_skill handler in tools/index.ts
  • Permissions: disableSkills wired end-to-end (permission groups API, access control UI, settings modal, executor validation)
  • Docs: Skills documentation page
  • Icon: agentskills.io hexagon favicon

Type of Change

  • New feature

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Feb 6, 2026 8:25am

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Greptile Overview

Greptile Summary

This PR introduces Agent Skills as workspace-scoped, reusable instruction packages that can be attached to Agent blocks. It adds a new skill DB table + migration, a /api/skills endpoint for listing/upserting/deleting skills, a Settings UI tab for managing skills, a new skill-input agent subblock, and executor support for progressive disclosure (injecting <available_skills> metadata into the system prompt and providing a load_skill tool to fetch full markdown content on demand).

Main must-fix items are around runtime authorization/containment for load_skill: the executor currently resolves skill content purely based on _context.workspaceId with no enforcement that the run is allowed to use skills and no restriction to the skills selected on the agent block. Tightening this is important so the new tool can’t be abused to read arbitrary workspace skill content and so permission-group disableSkills is reliably enforced end-to-end.

Confidence Score: 2/5

  • This PR needs fixes before merge due to authorization gaps in the new load_skill execution path.
  • Core feature wiring looks consistent (DB/API/UI/executor), but load_skill currently lacks permission checks and an allowlist tied to the agent’s configured skills, which can allow unintended skill content access within a workspace. There is also some uncertainty around consistent handling of permission-group denials during execution.
  • apps/sim/tools/index.ts, apps/sim/executor/handlers/agent/agent-handler.ts, apps/sim/executor/handlers/agent/skills-resolver.ts

Important Files Changed

Filename Overview
apps/sim/app/api/skills/route.ts Adds CRUD-like API for listing/upserting/deleting workspace skills with permission checks; prior review already noted redundant permission checks.
apps/sim/executor/handlers/agent/agent-handler.ts Injects skill metadata into system prompt and adds load_skill tool; needs authz/allowlist hardening and consistent error handling for permission denials.
apps/sim/executor/handlers/agent/skills-resolver.ts Implements skill metadata/content resolution and tool+prompt builders; ensure tool config shape aligns with ProviderToolConfig expectations.
apps/sim/hooks/queries/skills.ts Adds react-query hooks for skills CRUD via /api/skills; used by UI components.
apps/sim/tools/index.ts Adds load_skill tool execution path; currently lacks permission/allowlist checks for workspace skill content access.
packages/db/migrations/0152_parallel_frog_thor.sql Adds skill table migration with indexes and foreign keys.
packages/db/schema.ts Adds skill table definition and unique index (workspaceId,name).

Sequence Diagram

sequenceDiagram
  participant UI as Skills UI (Settings/Agent block)
  participant API as /api/skills
  participant DB as DB (skill table)
  participant Exec as AgentBlockHandler
  participant LLM as Provider LLM
  participant Tools as executeTool(load_skill)

  UI->>API: GET /api/skills?workspaceId
  API->>DB: SELECT skills WHERE workspaceId
  DB-->>API: rows
  API-->>UI: skills list

  UI->>API: POST /api/skills (create/update)
  API->>DB: INSERT/UPDATE skill rows
  DB-->>API: updated list
  API-->>UI: success

  UI->>Exec: Run workflow with agent.skills=[{skillId,...}]
  Exec->>DB: resolveSkillMetadata(skillIds, workspaceId)
  DB-->>Exec: [{name,description}]
  Exec->>LLM: system prompt + <available_skills>
  Exec->>LLM: tools include load_skill(enum=skillNames)

  LLM->>Tools: tool_call load_skill(skill_name)
  Tools->>DB: resolveSkillContent(skill_name, workspaceId)
  DB-->>Tools: content
  Tools-->>LLM: tool response {content}
  LLM-->>Exec: final assistant output
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@waleedlatif1
Copy link
Collaborator Author

@greptile

@waleedlatif1
Copy link
Collaborator Author

@cursor review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.


logger.info(`[${requestId}] Updated skill ${s.id}`)
continue
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skill rename to conflicting name returns raw 500 error

Medium Severity

The update path in upsertSkills issues a continue after updating, which skips the duplicate name check that only runs for new skill inserts. When a user renames a skill to a name that already exists in the workspace, the DB unique constraint (skill_workspace_name_unique) throws a raw PostgreSQL error (code 23505). The API route's catch block checks for "already exists" in the error message, but the DB error message says "duplicate key value violates unique constraint" — so it falls through to a generic 500 response instead of a user-friendly 409.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added a duplicate name check on the update path (excluding the skill being updated) so renames to conflicting names now return a friendly 409 instead of a raw DB constraint error.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 63 to +66

const providerId = getProviderFromModel(model)
const formattedTools = await this.formatTools(ctx, filteredInputs.tools || [])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unhandled permission error path

validateSkillsAllowed(ctx.userId, ctx) can throw SkillsNotAllowedError, but there’s no local handling here. Unless executeProviderRequest / the generic block runner catches this specific error and converts it to a user-friendly block error, the workflow run will fail with an uncaught exception when a permission group has disableSkills enabled and an agent block has skills configured.

Consider catching SkillsNotAllowedError in the agent handler (or in a central executor layer) and returning a structured block failure (similar to other access-control validations) so runs don’t crash unexpectedly.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/executor/handlers/agent/agent-handler.ts
Line: 63:66

Comment:
**Unhandled permission error path**

`validateSkillsAllowed(ctx.userId, ctx)` can throw `SkillsNotAllowedError`, but there’s no local handling here. Unless `executeProviderRequest` / the generic block runner catches this specific error and converts it to a user-friendly block error, the workflow run will fail with an uncaught exception when a permission group has `disableSkills` enabled and an agent block has skills configured.

Consider catching `SkillsNotAllowedError` in the agent handler (or in a central executor layer) and returning a structured block failure (similar to other access-control validations) so runs don’t crash unexpectedly.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change needed — this follows the same pattern as validateMcpToolsAllowed and validateCustomToolsAllowed (lines 169/173), which also throw and are caught by the executor's higher-level error handling.

Comment on lines +5 to +12
import type { SkillInput } from '@/executor/handlers/agent/types'

const logger = createLogger('SkillsResolver')

function escapeXml(str: string): string {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid tool schema field

buildLoadSkillTool returns both params: {} and parameters: {...}. Elsewhere in the codebase provider tool configs generally use one schema field; if downstream tooling expects params (or expects parameters) this mixed shape will break tool registration for some providers.

Make the returned object match the project’s ProviderToolConfig shape consistently (remove the unused field and ensure the remaining field name is what the executor/provider adapter actually reads).

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/executor/handlers/agent/skills-resolver.ts
Line: 5:12

Comment:
**Invalid tool schema field**

`buildLoadSkillTool` returns both `params: {}` and `parameters: {...}`. Elsewhere in the codebase provider tool configs generally use *one* schema field; if downstream tooling expects `params` (or expects `parameters`) this mixed shape will break tool registration for some providers.

Make the returned object match the project’s `ProviderToolConfig` shape consistently (remove the unused field and ensure the remaining field name is what the executor/provider adapter actually reads).

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change needed — both params and parameters are standard in this codebase. params holds user-provided parameter values (empty for load_skill), parameters holds the JSON schema for the LLM. Same convention at agent-handler.ts:300-301 and providers/utils.ts:520-521.

Comment on lines 63 to +66

const providerId = getProviderFromModel(model)
const formattedTools = await this.formatTools(ctx, filteredInputs.tools || [])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permission errors bubble up

validateSkillsAllowed(ctx.userId, ctx) throws SkillsNotAllowedError, and this handler doesn’t catch it (similar for validateMcpToolsAllowed / validateCustomToolsAllowed). If the executor doesn’t have a central error-mapper for these access-control errors, a user with disableSkills enabled will get a hard workflow failure rather than a structured “permission denied” block error.

If there isn’t already a global handler, add one (or wrap just the skills validation) so these errors are converted to a consistent block-level error response.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/executor/handlers/agent/agent-handler.ts
Line: 63:66

Comment:
**Permission errors bubble up**

`validateSkillsAllowed(ctx.userId, ctx)` throws `SkillsNotAllowedError`, and this handler doesn’t catch it (similar for `validateMcpToolsAllowed` / `validateCustomToolsAllowed`). If the executor doesn’t have a central error-mapper for these access-control errors, a user with `disableSkills` enabled will get a hard workflow failure rather than a structured “permission denied” block error.

If there isn’t already a global handler, add one (or wrap just the skills validation) so these errors are converted to a consistent block-level error response.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above — identical to how MCP/custom tool permission errors are handled. The executor catches these at a higher level.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Additional Comments (1)

apps/sim/tools/index.ts
Missing authz for skill loads

executeTool handles load_skill by trusting params._context?.workspaceId, but it never verifies that the running agent/workflow is actually allowed to read skills for that workspace (or that the requested skill is among the agent’s selected skills). Any prompt/tool-call that can set _context.workspaceId can fetch arbitrary skill content from that workspace, bypassing the permission-group disableSkills enforcement added in the agent handler.

At minimum, gate load_skill with the same permission check used for execution (e.g. validateSkillsAllowed(ctx.userId, ctx)), and ideally restrict loads to the skill IDs/names attached to the agent block for this run (so the model can’t enumerate/fetch other workspace skills).

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/index.ts
Line: 218:220

Comment:
**Missing authz for skill loads**

`executeTool` handles `load_skill` by trusting `params._context?.workspaceId`, but it never verifies that the running agent/workflow is actually allowed to read skills for that workspace (or that the requested skill is among the agent’s selected skills). Any prompt/tool-call that can set `_context.workspaceId` can fetch arbitrary skill content from that workspace, bypassing the permission-group `disableSkills` enforcement added in the agent handler.

At minimum, gate `load_skill` with the same permission check used for execution (e.g. `validateSkillsAllowed(ctx.userId, ctx)`), and ideally restrict loads to the skill IDs/names attached to the agent block for this run (so the model can’t enumerate/fetch other workspace skills).

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant