summaryrefslogtreecommitdiff
path: root/libpanto/src/tool.zig
diff options
context:
space:
mode:
authorT <t@tjp.lol>2026-05-25 22:54:43 -0600
committerT <t@tjp.lol>2026-05-26 10:10:42 -0600
commitc2f727e188c1558bcc6b34569af2feab3b0366c0 (patch)
tree24573b32e92cb45f998c24c304d2b060c6737b1f /libpanto/src/tool.zig
parentf026bb81ae68f516910d0eb4f23c9344dd36b62b (diff)
phase 3 part 1
Diffstat (limited to 'libpanto/src/tool.zig')
-rw-r--r--libpanto/src/tool.zig61
1 files changed, 61 insertions, 0 deletions
diff --git a/libpanto/src/tool.zig b/libpanto/src/tool.zig
new file mode 100644
index 0000000..1d8d113
--- /dev/null
+++ b/libpanto/src/tool.zig
@@ -0,0 +1,61 @@
+//! Native tool extension API.
+//!
+//! A `Tool` is the boundary between the agent loop and any extension runtime
+//! — native Zig code, a Lua bridge, a future Python or Go bridge. libpanto
+//! itself does not parse tool inputs or outputs; it just dispatches.
+
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+
+pub const Tool = struct {
+ /// Tool name. Borrowed — lifetime is owned by whoever constructs the
+ /// `Tool`. Typically the same owner that backs `ctx` (e.g. a LuaTool
+ /// adapter, or a static const in a native tool).
+ name: []const u8,
+
+ /// Human-readable purpose of the tool. Emitted to the LLM alongside the
+ /// schema. Borrowed; same lifetime contract as `name`.
+ description: []const u8,
+
+ /// JSON Schema for the tool's input, as raw JSON bytes. Emitted verbatim
+ /// into provider request bodies. Borrowed; same lifetime contract.
+ schema_json: []const u8,
+
+ /// Opaque context pointer passed back to every vtable call.
+ ctx: *anyopaque,
+
+ vtable: *const VTable,
+
+ pub const VTable = struct {
+ /// Invoke the tool. MUST be thread-safe — the agent may call
+ /// `invoke` concurrently from multiple threads when the LLM emits
+ /// multiple ToolUse blocks in a single response.
+ ///
+ /// `input` is the raw JSON bytes the provider sent. The tool is
+ /// responsible for parsing them if it cares about their structure.
+ ///
+ /// Returns owned bytes allocated with `allocator`. These bytes
+ /// become the `content` of the ToolResult block sent back to the
+ /// LLM. The agent takes ownership and frees them.
+ ///
+ /// Returning an error aborts the current turn. The agent surfaces
+ /// the error to the user. Native tool implementations are
+ /// responsible for catching their own panics — a panic in `invoke`
+ /// will crash the process. Adapters that bridge to safer languages
+ /// (Lua, Python, Go) should convert panics/exceptions into errors.
+ invoke: *const fn (
+ ctx: *anyopaque,
+ input: []const u8,
+ allocator: Allocator,
+ ) anyerror![]u8,
+
+ /// Called when the tool is unregistered or the registry is torn
+ /// down. Frees any resources owned by `ctx`, including `ctx`
+ /// itself if it was heap-allocated.
+ ///
+ /// `name`, `description`, and `schema_json` are also typically
+ /// owned by the same allocation as `ctx` — the tool's deinit
+ /// hook is responsible for freeing them.
+ deinit: *const fn (ctx: *anyopaque, allocator: Allocator) void,
+ };
+};