The Tool behaviour (CONTEXT.md; the Kimojo-shaped contract minus risk_level).
A Tool declares itself via __tool__/0 and runs via execute/2. Per ADR 0005 every
Tool is also dry-runnable: use Pixir.Tool injects a default dry_run/2 that
reports the planned action without side effects; side-effecting Tools override it
with a richer (still effect-free) plan.
__tool__/0
%{
name: "read",
description: "Read a file from the workspace",
parameters: %{ # JSON Schema (string keys)
"type" => "object",
"properties" => %{"path" => %{"type" => "string"}},
"required" => ["path"]
}
}execute/2 and dry_run/2
Receive args (a string-keyed map, validated by the Executor) and a context
%{session_id, workspace, call_id, dry_run}. They return:
{:ok, result}—resultis a string-keyed map;"output"(a string) is the model-facing payload (token-bounded viatruncate/2, ADR 0005).{:error, structured}— the standard%{ok: false, error: %{kind, message, details}}envelope (useerror/3).
Summary
Types
The curated, stable error-kind vocabulary (ADR 0005, rule 3). Callers and the model
branch on these, so the set is documented here as the single source of truth — adding a
kind is a deliberate change, not an ad-hoc string. Grouped by origin
Types
@type kind() ::
:invalid_args
| :unknown_tool
| :outside_workspace
| :not_found
| :resource_missing
| :no_match
| :not_unique
| :read_failed
| :write_failed
| :command_failed
| :timeout
| :permission_denied
| :detached
| :iteration_cap
| :tool_result_record_failed
| :corrupt_log_line
| :ephemeral_not_loggable
| :log_encode_failed
| :log_read_failed
| :log_write_failed
| :provider_http_error
| :model_not_supported
| :usage_limit_reached
| :rate_limited
| :network
| :not_authenticated
| :token_request_failed
| :no_account_id
| :invalid_response
| :corrupt_auth
| :auth_read_failed
| :auth_write_failed
| :device_auth_failed
| :device_code_unsupported
| :session_start_failed
| :no_prompt
| :stdin_error
The curated, stable error-kind vocabulary (ADR 0005, rule 3). Callers and the model
branch on these, so the set is documented here as the single source of truth — adding a
kind is a deliberate change, not an ad-hoc string. Grouped by origin:
- tools / executor —
:invalid_args,:unknown_tool,:outside_workspace,:not_found,:resource_missing,:no_match,:not_unique,:read_failed,:write_failed,:command_failed,:timeout,:permission_denied,:detached - turn loop —
:iteration_cap,:tool_result_record_failed - log —
:corrupt_log_line,:ephemeral_not_loggable,:log_encode_failed,:log_read_failed,:log_write_failed - provider —
:provider_http_error,:model_not_supported,:usage_limit_reached,:rate_limited,:network - auth —
:not_authenticated,:token_request_failed,:no_account_id,:invalid_response,:corrupt_auth,:auth_read_failed,:auth_write_failed,:device_auth_failed,:device_code_unsupported,:session_start_failed - cli / stdin —
:no_prompt,:stdin_error
Note: a bash command that runs but exits nonzero is not an error — it returns a
successful result %{"output", "exit_code", "ok" => false} so the model can read the
output and decide (a no-match grep exiting 1 is normal). See ADR 0005.
Callbacks
Functions
Build the standard structured error envelope (ADR 0005). kind should be a member of
the documented kind/0 vocabulary so callers can branch on it.
@spec truncate(binary(), pos_integer()) :: binary()
Token-bound a model-facing string (ADR 0005). Truncates to max bytes with an
explicit marker so the model knows output was cut.