Extensions

This page documents Osprey source commit 01dabf2fedd1826362162ead9757232e89d9bfa5.

Extension layers

Osprey resolves extension files from nearest to farthest.

  1. The nearest project .osprey directory
  2. Farther project .osprey directories within the workspace boundary
  3. The user $OSPREY_HOME directory
  4. Artifacts bundled into the binary

The default $OSPREY_HOME value is ~/.osprey. Name collisions use the first matching artifact unless a type uses additive composition.

Artifact Location Resolution
Custom tools tools/*.toml The first valid and trusted manifest with a given tool name wins.
Lifecycle hooks hooks.toml The nearest file wins.
Replacement prompt SYSTEM.md An environment override wins, then the nearest file.
Prompt append APPEND_SYSTEM.md Every readable file appends in layer order.
Tool descriptions tool-descriptions.toml An environment override wins, then the nearest file.
Named toolsets toolsets.toml The nearest file wins.
Configuration config.toml Supported fields use the configuration merge rules.

Skills and JavaScript workflows also use layered discovery. Their dedicated pages describe their different paths and trust rules.

Run osprey extensions to inspect the complete artifact report. Add --json for machine-readable output.

osprey extensions
osprey extensions --json

The report includes layer, path, shadowed copies, trust, status, provenance, digest, and activation boundary. The report also includes a sanitized reason for each skipped artifact.

In the terminal user interface (TUI), enter /extensions to view loaded custom tools. Enter /reload to schedule extension discovery at the next turn boundary. The reload command fails when the session lacks a reload coordinator.

Compose system prompts

Set OSPREY_SYSTEM_PROMPT_FILE to replace the persona prompt with a readable file. Without that environment variable, the nearest SYSTEM.md replaces the persona prompt. After an environment file read failure, Osprey uses the layered file. After a nearest SYSTEM.md read failure, Osprey uses the persona base. Osprey does not continue to a farther SYSTEM.md after that read failure.

Osprey appends every readable APPEND_SYSTEM.md after the selected base. Project append files precede the user append file.

Osprey appends project instructions after all extension prompt fragments. The instruction scan starts at the workspace root and ends at the working directory. Each directory contributes AGENTS.md, or CLAUDE.md when AGENTS.md is absent. The combined project instruction budget defaults to 32 kibibytes (KiB). Osprey truncates the tail and adds [truncated] when the budget is exceeded.

Define a custom tool

Place one Tom's Obvious Minimal Language (TOML) manifest in a layer's tools directory. The manifest exposes a subprocess as a model tool.

name = "word_count"
description = "Counts whitespace-separated words."
command = ["./count.sh", "--json"]
timeout_secs = 30
sandbox = true
[parameters.text]
type = "string"
description = "Text to count."
required = true
[parameters.mode]
type = "string"
enum = ["words", "lines"]
required = false
Field Requirement or default
name Required. Use 1 through 64 lowercase letters, digits, or underscores.
description Required model-facing description.
command Required nonempty argument vector. The first item is the program.
timeout_secs Defaults to 300 seconds and has a 3,600-second maximum.
sandbox Optional parsed flag. The current dispatcher does not enforce the flag.
parameters Optional map of model-facing input fields.
Parameter type Required JavaScript Object Notation (JSON) schema type string.
Parameter description Optional field description.
Parameter enum Optional list of allowed string values.
Parameter required Defaults to false.

A relative program path starts at the manifest directory. Fixed command arguments follow the program. Custom names cannot shadow any built-in tool name.

Custom tool execution

Osprey writes one JSON object and a newline to the subprocess standard input. The JSON object contains the model tool input. On success, Osprey returns the subprocess standard output.

The subprocess runs with the program's parent directory as its working directory. The subprocess inherits the environment available to Osprey. The command can read and write files. It can start processes. It can also use available network access.

sandbox = true is stored in the execution specification but is not enforced at this commit. Do not treat the manifest flag as an isolation boundary. Apply operating-system controls outside Osprey when a custom program needs isolation.

Osprey invokes the program directly without a shell around model input. The fixed manifest arguments still control the invoked program.

Osprey keeps a 512 KiB head and a 512 KiB tail from each output stream. Longer output contains a dropped-byte marker between those sections. A nonzero exit returns captured output and an error.

Spawn, wait, timeout, and nonzero-exit failures are non-retryable tool failures. A timeout terminates the subprocess process group. Cancellation also terminates the process group.

Configure lifecycle hooks

Hooks run commands at five events:

  • PreToolUse
  • PostToolUse
  • Stop
  • SessionStart
  • SessionEnd
[[hooks.PreToolUse]]
matcher = "bash|write_file"
[[hooks.PreToolUse.hooks]]
command = "./guard.sh"
timeout_secs = 10
[[hooks.Stop]]
[[hooks.Stop.hooks]]
command = "./verify.sh"

A matcher is an anchored regular expression over the complete tool name. A group without a matcher accepts every tool name. Invalid regular expressions make the complete hooks file invalid.

Each hook command runs through sh -c in the agent working directory. The default hook timeout is 600 seconds. Hook commands have the same filesystem and network side effects as other subprocesses.

Osprey sends a JSON event object to the hook's standard input. The object includes session_id, cwd, and hook_event_name. Tool events also include tool_name and tool_input. A Stop event includes stop_count.

On exit zero, a hook can return a JSON verdict on standard output. The verdict supports decision, reason, systemMessage, and hookSpecificOutput. The nested object supports permissionDecision, permissionDecisionReason, and updatedInput.

A pre-tool hook can allow, deny, request approval, or replace tool input. A post-tool hook can replace the visible result with a message or error. A blocking stop hook continues the agent loop with the supplied reason. Session start and end verdicts are observation-only.

Exit zero with empty output allows execution. Exit two blocks only when standard error contains a reason. Malformed output produces a warning and allows the intercepted action. Other exits, spawn failures, and timeouts have the same behavior. A timed-out hook process group is terminated.

A malformed or untrusted nearest hooks file is skipped. Osprey does not continue to a farther hooks file after that skip.

Override tool descriptions

Set OSPREY_TOOL_DESCRIPTIONS_FILE to select an explicit TOML file. Without the environment variable, Osprey uses the nearest tool-descriptions.toml. After an environment file error, Osprey uses the layered file. After a layered file error, Osprey uses bundled descriptions.

Description overrides change model-facing text only. They do not add tools or change tool implementation.

Define toolsets

A toolset gives one name to a set of built-in and custom tool names. The headless lead can select one with --toolset or a persona toolset field. The command-line option has higher priority. The installed TUI lead and spawned children do not apply a persona toolset at this commit.

[toolsets.readonly]
tools = ["read_file", "grep", "find_files", "list_directory", "tree"]
[toolsets.review]
includes = ["readonly"]
tools = ["word_count"]
exclude = ["tree"]

Osprey recursively unions tools and includes. Osprey removes exclude entries after each union. Include cycles stop at the first repeated set.

An unknown requested toolset produces an error with available names. An unknown included set names its containing toolset in the error. A requested toolset fails when no toolsets.toml exists. A malformed nearest toolsets file is skipped without trying a farther copy.

Trust and provenance

Osprey records executable trust separately from artifact authorship. Trust protects custom tools and hooks because those artifacts execute commands.

Project executable artifacts require a matching hash pin in $OSPREY_HOME/trust.toml. The hash covers the project-relative manifest path and manifest bytes. The hash also covers referenced executable bytes inside the workspace boundary. An external executable contributes its path instead of its bytes. For hooks, only a path-bearing first command token contributes a referenced executable. A bare command resolved through PATH contributes no referenced executable identity.

A changed manifest or local executable changes the trust status to modified. Modified and untrusted project executable artifacts remain inactive. A malformed trust file causes project executable artifacts to fail closed.

Bundled executable artifacts are trusted. Existing user artifacts without managed provenance are also trusted. Agent-authored user executable artifacts require a matching trust pin. Malformed provenance makes a user executable artifact untrusted.

Managed artifacts receive a JSON provenance sidecar beside the artifact. The sidecar records origin, operation identifier, digest, and activation boundary. Provenance states are agent, legacy, and unknown. Lifecycle states include pending_reload, active, and inactive.

At this commit, the installed command has no action that creates an extension trust pin. osprey extensions only reports trust state. The TUI /extensions view also performs inspection only. The parsed [extensions].trust_agent_authored setting governs no behavior at this commit.

Manage user artifacts through the agent

The extension_manage model tool manages validated artifacts in the resolved user layer. The tool supports create, replace, delete, and list operations. List results expose metadata without artifact bodies.

Supported kinds are:

  • skill
  • system_prompt
  • append_system
  • tool
  • hooks

Create and replace require complete content. The maximum artifact size is 256 KiB. The manager rejects null bytes and detected secrets. The manager also applies the production parser for each artifact kind.

Use expected_digest on replace or delete for revision protection. A digest mismatch produces a stale_revision error without a write. The manager rejects symbolic-link targets and nonregular files.

Writes use a staged file and atomic replacement. The manager also writes a provenance file. After a partial failure, it restores the artifact and provenance files. Successful changes become effective at the next normal turn boundary.

The manager refuses project collisions for tools, replacement prompts, and hooks. An append prompt can coexist with append files from other layers. The manager never grants executable trust. Therefore, a managed tool or hook can remain inactive after its bytes are durable.

Management failures use stable categories.

  • invalid_name
  • invalid_content
  • name_collision
  • already_exists
  • not_found
  • stale_revision
  • unsupported_operation
  • storage_unavailable

Use osprey extensions --json to diagnose inactive, shadowed, modified, or pending artifacts. Correct malformed content before reloading. Restore the pinned bytes when a trusted project executable changed unexpectedly.

Back to Osprey