package murmur:tool@0.2.0;
interface run {
enum status {
passed,
failed,
error,
}
record tool-input {
data: option<string>,
log-path: option<string>,
}
/// An image a tool returns alongside its text: a file it wrote into the
/// work folder, named relative to that folder.
///
/// `media-type` is one of `image/png`, `image/jpeg`, `image/gif` or
/// `image/webp`. The host reads the file beneath the work folder, refusing
/// a link or a path outside it, checks that its leading bytes match
/// `media-type` and that it is no larger than the capsule's
/// `context.images.max_bytes`, and stores the bytes once by their sha256
/// beside the conversation. A refused attachment adds a note to the call's
/// result and never changes its `status`.
record attachment {
media-type: string,
path: string,
}
record tool-result {
status: status,
summary: option<string>,
data: option<string>,
data-path: option<string>,
truncated: bool,
/// Free-form key/value channel from a driver or tool back to the host.
///
/// Reserved key `"continuation_id"`: an inference driver that holds
/// conversation state provider-side (so the host need not resend the full
/// transcript on every Turn) opts in by returning this key with a
/// non-empty value on any Turn's response. The host persists it for the
/// current session loop and, on the next Turn under the same context,
/// transmits only the messages appended since the driver last acknowledged
/// state, plus this id (as the request-side `continuation_id` field),
/// instead of the full `messages` array. Returning the key with an
/// empty-string value, or omitting it, means "not continuing": the host
/// drops any held id and resends in full. If the list carries more than
/// one `continuation_id` tuple, the host uses the first in list order.
/// A driver that never sets this key (every driver shipped today) sees no
/// behavior change.
///
/// Reserved key `"state_effect"`: how this call affected the resource it
/// addressed, declared by the tool itself so host observability (e.g.
/// `mur trace` redundant-call detection) can reason about it without
/// knowing which tool this is. Recognized values: `"read"` (the call only
/// observed state — a repeat against the same unchanged resource is
/// redundant) and `"mutate"` (the call changed state — it invalidates any
/// earlier read of that resource). Any other value, or omitting the key,
/// means "unknown": the host treats the call conservatively (like a mutate
/// for invalidation purposes) and never credits it as a redundant read. A
/// tool that declares nothing is never misreported; it simply gets no
/// redundant-read detection of its own. The key is per-call, so one tool
/// with several operations declares the effect appropriate to each.
///
/// Reserved key `"resource_id"`: which resource this call addressed,
/// declared by the tool itself. The value is an opaque, tool-defined
/// string that the host records verbatim and never parses — two calls
/// address the same resource exactly when their `resource_id` values are
/// byte-equal. Declaring it is how a tool tells host observability (e.g.
/// `mur trace` redundant-call detection) *what* a call addressed, the
/// companion to `state_effect`'s *how it was affected*; the two are only
/// useful together. Omitting the key, or returning it with an
/// empty-string value, means "undeclared": the host falls back to
/// guessing the resource from a path-like field in the call's input,
/// which is all it could ever do before this key existed. A tool whose
/// resource is not a filesystem path (a symbol, a URI, a query) should
/// declare this key — it is the only way such a tool gets correct
/// detection, since no input-guessing heuristic can recognize its
/// addressing scheme. Scoping the value (e.g. `"sym:Foo::bar"`) keeps it
/// from colliding with an unrelated resource that happens to share a
/// name. If the list carries more than one `resource_id` tuple, the host
/// uses the first in list order. A tool that never sets this key (every
/// tool shipped today) sees no behavior change.
///
/// Reserved key `"transient"`: whether an inference driver's failed call
/// may succeed if the host makes it again. `"true"` marks the failure
/// transient: a stream that broke after the provider answered `200`, or a
/// provider error event mid-stream. The host then sends the same payload
/// again after a backoff, within the capsule's `inference.retry` bounds.
/// `"false"` vetoes a retry of a status the host would otherwise retry,
/// such as a `429` the provider reports as a permanent quota failure.
/// Any other value, or omitting the key, leaves the decision to what the
/// host's credential gateway saw: a `408`, `429` or retryable `5xx`, or a
/// connection that broke before a response arrived. Read only on a
/// failed inference call; if the list carries more than one `transient`
/// tuple, the host uses the first in list order.
metadata: list<tuple<string, string>>,
/// Images the call returns, in the order the model sees them. Empty for
/// a call that returns none, and for every inference driver's result.
attachments: list<attachment>,
}
run: func(input: tool-input) -> tool-result;
}