Developer guide / Skills

Skills

A skill is an invokable capability you expose to the model — a tool. Declare its name, description, and a JSON-schema for its arguments; implement one Invoke method.

The interface

csharp
public interface ISkill
{
    SkillManifest Manifest { get; }
    Task<SkillResult> Invoke(SkillContext ctx, CancellationToken ct = default);
}

public sealed record SkillManifest
{
    public required string Name { get; init; }          // stable id, e.g. "read_file"
    public required string Description { get; init; }    // one line; enters model context
    public JsonElement? Parameters { get; init; }        // JSON Schema for the args object
    public IReadOnlyList<string> Keywords { get; init; } = [];   // for skill discovery/search
}
Only a skill's name and description occupy context until it's actually used — large tool sets stay cheap. Keep the description tight and action-oriented.

Input & output

SkillContext carries the SessionId, AgentName, the parsed Args (a JsonElement), the CallId, and the agent-settings snapshot taken at turn entry. Return a SkillResult:

csharp
SkillResult.Success("done — 3 files changed");        // ok, content goes back to the model
SkillResult.Fail("path not found: /etc/none");        // failure the model can react to
SkillResult.Success(text, attachments);               // include AIContent attachments

A complete skill

The built-in read_file, verbatim — note the schema helper and provider injection:

ReadFileSkill.cscsharp
using AgentParley.Abstractions.Providers;
using AgentParley.Abstractions.Skills;

public sealed class ReadFileSkill(IFilesProvider files) : ISkill
{
    public SkillManifest Manifest => new()
    {
        Name = "read_file",
        Description = "Read a file. Output is line-numbered. Optional line range for large files.",
        Parameters = Schema.Of(
            new Prop("path", "string", "file path (relative to workspace)", true),
            new Prop("startLine", "integer", "first line (1-based)"),
            new Prop("lineCount", "integer", "number of lines")),
    };

    public async Task<SkillResult> Invoke(SkillContext ctx, CancellationToken ct = default)
    {
        var path = Args.Str(ctx.Args, "path", "");
        var start = Args.Int(ctx.Args, "startLine") ?? 1;
        var count = Args.Int(ctx.Args, "lineCount") ?? 2000;
        var content = await files.Read(new FileRef(ctx.AgentName, path), new LineRange(start, count), ct);
        return SkillResult.Success(content.Text);
    }
}
Schema.Of / Prop / Args are convenience helpers in the base plugin. In your own plugin, either mirror that small helper or build the JSON Schema JsonElement directly — the only contract is that Parameters is valid JSON Schema for the arguments object.

Registering a skill

Skills usually depend on resolved providers, so register the type in Configure and add the resolved instance in Start:

csharp
public void Configure(IPluginContext ctx) =>
    ctx.Services.AddSingleton<DeploySkill>();

public Task Start(IPluginRuntime runtime, CancellationToken ct)
{
    runtime.Skills.Add(runtime.Services.GetRequiredService<DeploySkill>());
    return Task.CompletedTask;
}

Each agent's allow/deny policy decides which registered skills it can actually see — adding a skill makes it available, not automatically granted to everyone.

Parking: deferred skills

A skill that needs to wait — for a human answer, a peer reply, or an approval — returns a deferred result. The session parks; when the resolution arrives the skill is re-invoked with ctx.Resolution set, and produces its real result. One skill owns both halves; the runtime never special-cases it.

csharp
// first invoke: park until a human answers (the next message is the answer)
return SkillResult.Defer(ctx.CallId, DeferredAwaiting.AwaitingHuman, ResolutionMode.Positional);

// on resume, ctx.Resolution carries the answer:
if (ctx.Resolution is { } r)
    return SkillResult.Success($"got it: {r.Text}");
AwaitingResolves with
AwaitingHumanthe next human message (Positional) or a correlated answer
AwaitingPeera correlated reply from another agent
AwaitingApprovala correlated approve/deny decision