agentskit.js
Concepts

Skill

A persona the model becomes — a system prompt plus optional examples, tools, and delegates. Pure declarative configuration.

A Skill is a packaged behavior. A system prompt that turns a generic LLM into a domain-specialized agent: a researcher, a critic, a code reviewer, a customer support triager. Optional examples make it few-shot. Optional tool and delegate references make it composable.

Skills are not tools. A tool is a function the model calls. A skill is a persona the model becomes. Confusing the two produces the LangChain "agents" mess where you can't tell what .invoke() versus .run() versus .stream() does.

#The interface

import type { SkillDefinition } from '@agentskit/core'

export interface SkillDefinition {
  name: string
  description: string
  systemPrompt: string
  examples?: Array<{ input: string; output: string }>
  tools?: string[]        // tool names, resolved at activation
  delegates?: string[]    // skill names, resolved at activation
  temperature?: number
  metadata?: Record<string, unknown>
  onActivate?: () => MaybePromise<{ tools?: ToolDefinition[] }>
}

Notice: no run() method. A skill is not invokable. The runtime activates it and proceeds with normal model calls plus the skill's prompt, tools, and temperature.

#Defining a skill

import type { SkillDefinition } from '@agentskit/core'

export const codeReviewer: SkillDefinition = {
  name: 'code_reviewer',
  description: 'Reviews TypeScript pull requests for bugs, missing tests, and style.',
  systemPrompt: `You are a meticulous senior TypeScript engineer reviewing a PR.

Always:
- Read the diff fully before commenting
- Flag missing tests as severity:high
- Flag any 'any' usage with a concrete suggestion
- Approve only after you've checked the test runner output
- End with a one-line summary verdict`,
  tools: ['filesystem_read', 'shell'],
  examples: [
    {
      input: 'Review the diff in PR #42',
      output: '...comment-by-comment review with a verdict at the end...',
    },
  ],
  temperature: 0.2,
}

The skill is just data. It can be serialized, versioned, traded in a marketplace, or generated by another agent.

#Using a built-in skill

import { researcher, critic, planner } from '@agentskit/skills'

const result = await runtime.run('What changed in JavaScript runtimes in the last year?', {
  skill: researcher,
})

#Tools by reference

Skills don't define tools inline. They reference tool names, and the runtime resolves them against the registry at activation:

const supportTriager: SkillDefinition = {
  name: 'support_triager',
  description: 'Routes tickets to the right team.',
  systemPrompt: '...',
  tools: ['lookup_customer', 'list_open_tickets', 'tag_ticket'],
}

createRuntime({
  adapter,
  tools: [lookupCustomer, listOpenTickets, tagTicket],
})

await runtime.run('Triage all unassigned tickets', { skill: supportTriager })

This is what enables tool reuse across skills, and what makes the marketplace possible.

#Delegation: skills calling skills

import { planner, researcher, coder } from '@agentskit/skills'

await runtime.run('Build a landing page about quantum computing', {
  skill: planner,
  delegates: {
    researcher: { skill: researcher, tools: [webSearch()], maxSteps: 3 },
    coder:      { skill: coder, tools: [...filesystem({ basePath: './src' })], maxSteps: 8 },
  },
})

The runtime materializes each delegate as a tool the planner can call (delegate_researcher, delegate_coder). To the model, delegation is just another tool call. To the runtime, it's a recursive run() with depth tracking.

#Dynamic per-user tools (onActivate)

The one and only escape hatch in an otherwise-pure-data contract:

const supportAgent: SkillDefinition = {
  name: 'support_agent',
  description: 'Acts on behalf of the current user.',
  systemPrompt: '...',
  async onActivate() {
    const userToken = await getCurrentUserToken()
    return {
      tools: [createGmailTool(userToken), createCalendarTool(userToken)],
    }
  },
}

Use this only for credential-bound tools that can't be constructed at the registry level. General initialization belongs elsewhere.

#When to write your own

Almost always, if the built-in skills don't cover your domain. A skill is a system prompt plus a few fields — it's the cheapest concept in AgentsKit to author. The hard part is the prompt itself.

Read existing skills (packages/skills/src/researcher.ts, critic.ts, coder.ts, summarizer.ts, planner.ts) to see the prompt patterns we use. They're not magic; they're disciplined.

#Common pitfalls

PitfallWhat to do instead
Putting tool implementations inside the skillReference by name; register the tool with the runtime
Doing I/O when defining the skill (top-level await)Move it into onActivate or out of the skill entirely
Trying to call skill.run()Skills aren't invokable — pass them to runtime.run({ skill })
Multi-turn examples encoded as one input/outputEncode the demonstration in systemPrompt prose for v1
Skill versioning via filenameUse metadata.version until the manifest spec lands

#Going deeper

The full list of invariants (twelve of them, S1–S12) is in ADR 0005 — Skill contract.

Explore nearby

✎ Edit this page on GitHub·Found a problem? Open an issue →·How to contribute →

On this page