diff options
Diffstat (limited to 'agent/tools/write.lua')
| -rw-r--r-- | agent/tools/write.lua | 89 |
1 files changed, 12 insertions, 77 deletions
diff --git a/agent/tools/write.lua b/agent/tools/write.lua index 144a305..ee1b2ae 100644 --- a/agent/tools/write.lua +++ b/agent/tools/write.lua @@ -2,8 +2,7 @@ -- description terse because every byte of every tool description rides -- in the system prompt on every turn. -local panto = require("panto") -local uv = require("luv") +local std = require("_std") local tool = { name = "std.write", @@ -18,75 +17,6 @@ local tool = { }, } -panto.ext.on("tool", function(e) - if e.tool_name ~= tool.name then return end - e:set_component(e:get_component()) -end) - --- --------------------------------------------------------------------------- --- Coroutine-synchronous libuv wrappers. panto runs each tool handler in --- its own coroutine and drives a single `uv.run()` to completion; a --- handler may only block by yielding on a pending libuv op whose --- callback resumes it exactly once. `await` encapsulates that. --- --------------------------------------------------------------------------- -local function await(arm) - local co = assert(coroutine.running(), - "write: await must run inside a tool handler coroutine") - local res, fired = nil, false - arm(function(...) - assert(not fired, "write: libuv callback fired twice") - fired = true - res = table.pack(...) - local ok, err = coroutine.resume(co) - if not ok then error(err, 0) end - end) - coroutine.yield() - return table.unpack(res, 1, res.n) -end - --- Async `fs_mkdir`. luv's async callback is `(err, success)`, and on --- EEXIST it reports the error via a string code in `err`; we detect --- that textually since the async form doesn't surface the errno name --- separately the way the sync form's third return does. -local function fs_mkdir(path, mode) - return await(function(resolve) uv.fs_mkdir(path, mode, resolve) end) -end -local function fs_open(path, flags, mode) - return await(function(resolve) uv.fs_open(path, flags, mode, resolve) end) -end -local function fs_write(fd, data, offset) - return await(function(resolve) uv.fs_write(fd, data, offset, resolve) end) -end -local function fs_close(fd) - return await(function(resolve) uv.fs_close(fd, resolve) end) -end - --- mkdir -p equivalent: walk the path components and create each. --- Ignores "already exists" so the call is idempotent. -local function mkdir_p(path) - if path == "" or path == "." or path == "/" then return true end - local parts = {} - for piece in string.gmatch(path, "[^/]+") do - parts[#parts + 1] = piece - end - local cur = (path:sub(1, 1) == "/") and "" or "." - for _, piece in ipairs(parts) do - cur = cur .. "/" .. piece - local err = fs_mkdir(cur, tonumber("755", 8)) - if err and not tostring(err):find("EEXIST") then - return nil, err - end - end - return true -end - --- Return the dirname of `path`, or nil if there isn't one (basename only). -local function dirname(path) - local slash = path:find("/[^/]*$") - if not slash or slash == 1 then return nil end - return path:sub(1, slash - 1) -end - tool.handler = function(input) local path = input.path local content = input.content @@ -98,15 +28,15 @@ tool.handler = function(input) return "Error: `content` must be a string." end - local parent = dirname(path) + local parent = std.dirname(path) if parent then - local ok, err = mkdir_p(parent) + local ok, err = std.mkdir_p_async(parent) if not ok then return "Error: could not create parent directory " .. parent .. ": " .. tostring(err) end end - local open_err, fd = fs_open(path, "w", tonumber("644", 8)) + local open_err, fd = std.fs_open(path, "w", tonumber("644", 8)) if not fd then return "Error: " .. (open_err or ("could not open " .. path .. " for writing")) end @@ -114,15 +44,15 @@ tool.handler = function(input) -- A single `fs_write` may short-write; loop until all bytes land. local offset = 0 while offset < #content do - local werr, n = fs_write(fd, content:sub(offset + 1), offset) + local werr, n = std.fs_write(fd, content:sub(offset + 1), offset) if not n then - fs_close(fd) + std.fs_close(fd) return "Error: write failed: " .. tostring(werr) end offset = offset + n end - local close_err = fs_close(fd) + local close_err = std.fs_close(fd) if close_err then return "Error: close failed: " .. tostring(close_err) end @@ -130,4 +60,9 @@ tool.handler = function(input) return string.format("Wrote %d bytes to %s.", #content, path) end +std.install_renderer(tool.name, function(st) + local obj = std.decode(st.input) + return "write " .. tostring(obj and obj.path or "") +end) + return tool |
