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.

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

On this page