653 lines
18 KiB
TOML
653 lines
18 KiB
TOML
# =====================================================================
|
|
# The VM sub-agent's tools: what it is offered, and how each is described
|
|
# to it.
|
|
#
|
|
# This is the second model, not the assistant. The assistant sees exactly
|
|
# one tool - vm_agent - and never any of these; configs/tools.toml is the
|
|
# file for that one. These are the verbs the sub-agent uses to drive a
|
|
# real machine, and every description here is prompt text it pays for on
|
|
# every turn of a session.
|
|
#
|
|
# WHAT YOU CAN CHANGE, AND WHAT HAPPENS IF YOU GET IT WRONG
|
|
#
|
|
# enabled = false removes the tool. The agent is never told it exists.
|
|
# description is read by the model, and is the whole of what it
|
|
# knows about the tool. Say what it does, when to
|
|
# reach for it, and the one rule that must not break.
|
|
# Leave the reasoning to configs/agent/prompt.md.
|
|
# parameters must keep matching what vmd actually accepts. This
|
|
# file cannot add a tool: dispatch lives in code, and
|
|
# a name here with nothing behind it is refused at
|
|
# startup rather than offered and then failing.
|
|
#
|
|
# {shell}, {project} and {example} are filled in per machine from
|
|
# configs/vms.toml - bash and /mnt/project on Ubuntu, PowerShell and Z:\
|
|
# on Windows. That substitution is why one file describes both machines.
|
|
#
|
|
# Order matters and is the order below: planning verbs, then the command
|
|
# line, then the lookup suite, then the screen. While the machine is
|
|
# still booting ONLY the task group is offered, because there is nothing
|
|
# to run yet and offering a tool that would be refused teaches the model
|
|
# to retry rather than to wait.
|
|
#
|
|
# After editing: docker compose restart vmagent
|
|
# =====================================================================
|
|
|
|
|
|
# --- planning ---------------------------------------------------------
|
|
# The spine of a session. plan_tasks MUST be the agent's first call and
|
|
# nothing else runs until there is a list; finish is refused while any
|
|
# task is still open. These three are also the only tools offered while
|
|
# the machine is booting.
|
|
|
|
|
|
[[tool]]
|
|
name = "plan_tasks"
|
|
group = "task"
|
|
enabled = true
|
|
required = ["tasks"]
|
|
description = '''
|
|
Write down what you intend to do. This must be your FIRST call - nothing else runs until there is a list. Give 2-20 steps, each one something you could report on, not every command you expect to type. Include a step for checking the work.'''
|
|
|
|
[tool.parameters.tasks]
|
|
type = "array"
|
|
items = { type = "string" }
|
|
description = '''
|
|
The steps, in order.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "update_task"
|
|
group = "task"
|
|
enabled = true
|
|
required = ["id", "status"]
|
|
description = '''
|
|
Move a task to in_progress, done, blocked or dropped. Mark one in_progress when you start it and closed when you stop. A note is REQUIRED for blocked and dropped: that sentence is what the user reads when work comes back unfinished.'''
|
|
|
|
[tool.parameters.id]
|
|
type = "integer"
|
|
description = '''
|
|
The task number.'''
|
|
|
|
[tool.parameters.status]
|
|
type = "string"
|
|
enum = ["in_progress", "done", "blocked", "dropped"]
|
|
|
|
[tool.parameters.note]
|
|
type = "string"
|
|
description = '''
|
|
Why. Required unless status is done.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "add_task"
|
|
group = "task"
|
|
enabled = true
|
|
required = ["text"]
|
|
description = '''
|
|
Add a step you did not plan for. Use it when the work turns out to need something real, not to record every command. Added tasks are marked as added in the report.'''
|
|
|
|
[tool.parameters.text]
|
|
type = "string"
|
|
|
|
[tool.parameters.after_id]
|
|
type = "integer"
|
|
|
|
|
|
# --- the machine ------------------------------------------------------
|
|
# Running commands and moving files about, plus the session's own
|
|
# lifecycle verbs. `run` is the one it spends most of its turns in.
|
|
|
|
|
|
[[tool]]
|
|
name = "run"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["command"]
|
|
description = '''
|
|
Run a shell command in the machine and get back the exit code, stdout AND stderr. This is {shell}. Commands start in {project}. It is the main thing you do: read what actually happened rather than predicting it.'''
|
|
|
|
[tool.parameters.command]
|
|
type = "string"
|
|
description = '''
|
|
The command line to run.'''
|
|
|
|
[tool.parameters.timeout_s]
|
|
type = "number"
|
|
description = '''
|
|
Seconds to wait, default 120, maximum 900.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "write_file"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["path", "content"]
|
|
description = '''
|
|
Create a file in the project folder, {project}, or replace one whole. Paths are relative to that folder, which is the ONLY place whose contents survive the end of this session - everything else in the machine is destroyed. One write takes at most 16 KB: that is deliberate, and it is what stops you designing a whole program before writing any of it. Start with the skeleton, then append_file the sections and edit_file the parts you got wrong.'''
|
|
|
|
[tool.parameters.path]
|
|
type = "string"
|
|
description = '''
|
|
Relative path, e.g. {example}.'''
|
|
|
|
[tool.parameters.content]
|
|
type = "string"
|
|
|
|
|
|
[[tool]]
|
|
name = "append_file"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["path", "content"]
|
|
description = '''
|
|
Add to the end of a file in the project folder, creating it if it is not there. This is how you build something too big for one write: put down a section, check it, then add the next. Prefer it over holding a whole file in your head.'''
|
|
|
|
[tool.parameters.path]
|
|
type = "string"
|
|
|
|
[tool.parameters.content]
|
|
type = "string"
|
|
|
|
|
|
[[tool]]
|
|
name = "edit_file"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["path", "old", "new"]
|
|
description = '''
|
|
Change part of a file by replacing exact text. `old` must appear EXACTLY once, whitespace and indentation included - read the file first rather than guessing at the wording. It refuses if the text is missing or ambiguous, which means a failed edit tells you your idea of the file is wrong before you build on it. Use this to revise; use append_file to extend; use write_file only to start.'''
|
|
|
|
[tool.parameters.path]
|
|
type = "string"
|
|
|
|
[tool.parameters.old]
|
|
type = "string"
|
|
description = '''
|
|
Exact text to replace, with enough surrounding lines to be unique.'''
|
|
|
|
[tool.parameters.new]
|
|
type = "string"
|
|
description = '''
|
|
What to put in its place.'''
|
|
|
|
[tool.parameters.replace_all]
|
|
type = "boolean"
|
|
description = '''
|
|
Replace every occurrence instead of requiring one.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "read_file"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["path"]
|
|
description = '''
|
|
Read part of a file from the project folder. Windowed, so a large log does not fill your context; ask for the next window with offset if you need more.'''
|
|
|
|
[tool.parameters.path]
|
|
type = "string"
|
|
|
|
[tool.parameters.offset]
|
|
type = "integer"
|
|
|
|
[tool.parameters.size]
|
|
type = "integer"
|
|
description = '''
|
|
Bytes to return, default 40000.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "web_search"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["query"]
|
|
description = '''
|
|
Search the web and get back ranked results with titles, URLs and snippets. Use it before guessing at a download URL, a package name, a flag or an error message. Snippets are thin: read_page the one that looks right, or open_page it if you need to see the page rather than read it.'''
|
|
|
|
[tool.parameters.query]
|
|
type = "string"
|
|
description = '''
|
|
Search terms as they would appear on a good result page - include version numbers and error strings verbatim.'''
|
|
|
|
[tool.parameters.max_results]
|
|
type = "integer"
|
|
description = '''
|
|
1 to 15, default 6.'''
|
|
|
|
[tool.parameters.site]
|
|
type = "string"
|
|
description = '''
|
|
Restrict to one domain, e.g. 'docs.python.org'.'''
|
|
|
|
[tool.parameters.time_range]
|
|
type = "string"
|
|
description = '''
|
|
'day', 'week', 'month' or 'year'.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "read_page"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["url"]
|
|
description = '''
|
|
Fetch a web page or PDF and return its text. This is how you read documentation: it costs one call, where reading the same page off the screen costs a screenshot per scroll. Long pages come back a window at a time - ask for the next with offset.'''
|
|
|
|
[tool.parameters.url]
|
|
type = "string"
|
|
description = '''
|
|
Full URL, exactly as web_search gave it.'''
|
|
|
|
[tool.parameters.max_chars]
|
|
type = "integer"
|
|
description = '''
|
|
500 to 20000, default 6000.'''
|
|
|
|
[tool.parameters.offset]
|
|
type = "integer"
|
|
description = '''
|
|
Continue from this character.'''
|
|
|
|
[tool.parameters.search]
|
|
type = "string"
|
|
description = '''
|
|
Jump to the first mention of this term.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "list_dir"
|
|
group = "cli"
|
|
enabled = true
|
|
required = []
|
|
description = '''
|
|
List a directory in the project folder, with sizes and modification times.'''
|
|
|
|
[tool.parameters.path]
|
|
type = "string"
|
|
description = '''
|
|
Relative path, default '.'.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "check_internet"
|
|
group = "cli"
|
|
enabled = true
|
|
required = []
|
|
description = '''
|
|
Check whether the machine can actually reach the internet. Use this rather than ping: ping can NEVER work here for a reason that has nothing to do with the network, and reading its failure as 'no internet' will waste the session.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "git_commit"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["message"]
|
|
description = '''
|
|
Commit the project folder's current state. Commit when something works, not at the end - a commit is how the user sees what you did, and the push happens for you when the session ends. Test scripts, probes, captured output and build products are NOT committed and never reach the release; the result names anything it held back.'''
|
|
|
|
[tool.parameters.message]
|
|
type = "string"
|
|
description = '''
|
|
What changed, in one line.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "reboot_vm"
|
|
group = "cli"
|
|
enabled = true
|
|
required = []
|
|
description = '''
|
|
Reboot the machine. The disk survives, so nothing you have written is lost. Use it when something needs a restart to take effect, or when you have broken the machine's networking or a service badly enough that starting again is quicker.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "ask_operator"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["question"]
|
|
description = '''
|
|
Ask the user a question and pause. Use it only when the answer cannot be found in the machine - a choice about what they want, a credential, a decision you should not make for them. Anything you could determine by running a command, run.'''
|
|
|
|
[tool.parameters.question]
|
|
type = "string"
|
|
|
|
|
|
[[tool]]
|
|
name = "request_unfreeze"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["reason"]
|
|
description = '''
|
|
Ask the operator to unfreeze the base image so software can be added permanently. You cannot do this and it will not happen during this session. Only ask when something is needed by EVERY future session; for this one, install it normally - that works fine, it just does not persist.'''
|
|
|
|
[tool.parameters.reason]
|
|
type = "string"
|
|
description = '''
|
|
What is needed and why it must be in the base image.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "wait"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["seconds"]
|
|
description = '''
|
|
Wait, without spending a turn on it. Use it while something long is running - a package install, a build, a first boot, a window that has not appeared yet. Far cheaper and more reliable than running the same command over and over to see if it has finished yet.'''
|
|
|
|
[tool.parameters.seconds]
|
|
type = "number"
|
|
description = '''
|
|
How many seconds to wait.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "finish"
|
|
group = "cli"
|
|
enabled = true
|
|
required = ["summary", "verification"]
|
|
description = '''
|
|
End the session. Refused while any task is still open, and refused if you have not run anything. The verification command is RE-RUN by the supervisor afterwards and its real exit code is what the user is shown - so choose a command that would fail if the work were wrong.'''
|
|
|
|
[tool.parameters.summary]
|
|
type = "string"
|
|
description = '''
|
|
What you did and what the user should know.'''
|
|
|
|
[tool.parameters.verification]
|
|
type = "object"
|
|
required = ["command"]
|
|
|
|
[tool.parameters.verification.properties.command]
|
|
type = "string"
|
|
description = '''
|
|
One command that demonstrates it works.'''
|
|
|
|
[tool.parameters.verification.properties.expected]
|
|
type = "string"
|
|
description = '''
|
|
What you expect it to print.'''
|
|
|
|
|
|
# --- the lookup suite -------------------------------------------------
|
|
# These are calls BACK into the tool server, dispatched as one
|
|
# `research` verb. A tool switched off in configs/tools.toml is not
|
|
# offered here either - it would 404 and read as a broken tool rather
|
|
# than an absent one.
|
|
|
|
|
|
[[tool]]
|
|
name = "search_stackexchange"
|
|
group = "research"
|
|
enabled = true
|
|
required = ["query"]
|
|
description = '''
|
|
Search Stack Exchange for a technical answer - error messages, API behaviour, 'why does this fail'. Returns question titles with the accepted answer.'''
|
|
|
|
[tool.parameters.query]
|
|
type = "string"
|
|
|
|
[tool.parameters.site]
|
|
type = "string"
|
|
description = '''
|
|
e.g. 'stackoverflow', 'unix', 'serverfault'.'''
|
|
|
|
[tool.parameters.max_results]
|
|
type = "integer"
|
|
|
|
|
|
[[tool]]
|
|
name = "read_man_page"
|
|
group = "research"
|
|
enabled = true
|
|
required = ["name"]
|
|
description = '''
|
|
Read a Linux manual page, including for software this machine does not have installed. Faster and more exact than guessing at flags.'''
|
|
|
|
[tool.parameters.name]
|
|
type = "string"
|
|
description = '''
|
|
e.g. 'rsync' or 'systemd.exec'.'''
|
|
|
|
[tool.parameters.section]
|
|
type = "integer"
|
|
|
|
[tool.parameters.search]
|
|
type = "string"
|
|
description = '''
|
|
Jump to the first mention of this.'''
|
|
|
|
[tool.parameters.offset]
|
|
type = "integer"
|
|
|
|
|
|
[[tool]]
|
|
name = "lookup_package"
|
|
group = "research"
|
|
enabled = true
|
|
required = ["name", "ecosystem"]
|
|
description = '''
|
|
Get the current published version of a package - pypi, npm, crates, apt and more. Use it instead of pinning a version from memory.'''
|
|
|
|
[tool.parameters.name]
|
|
type = "string"
|
|
|
|
[tool.parameters.ecosystem]
|
|
type = "string"
|
|
description = '''
|
|
pypi, npm, crates, apt, and others.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "wikipedia_lookup"
|
|
group = "research"
|
|
enabled = true
|
|
required = ["topic"]
|
|
description = '''
|
|
Read a Wikipedia article. For background and definitions, not for anything that changes often.'''
|
|
|
|
[tool.parameters.topic]
|
|
type = "string"
|
|
|
|
[tool.parameters.search]
|
|
type = "string"
|
|
|
|
[tool.parameters.full_article]
|
|
type = "boolean"
|
|
|
|
[tool.parameters.offset]
|
|
type = "integer"
|
|
|
|
|
|
[[tool]]
|
|
name = "lookup_rfc"
|
|
group = "research"
|
|
enabled = true
|
|
required = []
|
|
description = '''
|
|
Read or search IETF RFCs - the actual protocol text, when behaviour has to be right rather than plausible.'''
|
|
|
|
[tool.parameters.number]
|
|
type = "integer"
|
|
|
|
[tool.parameters.query]
|
|
type = "string"
|
|
|
|
[tool.parameters.search]
|
|
type = "string"
|
|
|
|
[tool.parameters.offset]
|
|
type = "integer"
|
|
|
|
|
|
[[tool]]
|
|
name = "lookup_cve"
|
|
group = "research"
|
|
enabled = true
|
|
required = []
|
|
description = '''
|
|
Look up a CVE by id, or search for vulnerabilities in a product or version.'''
|
|
|
|
[tool.parameters.cve_id]
|
|
type = "string"
|
|
description = '''
|
|
e.g. 'CVE-2024-3094'.'''
|
|
|
|
[tool.parameters.keyword]
|
|
type = "string"
|
|
description = '''
|
|
Product or version to search for.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "search_papers"
|
|
group = "research"
|
|
enabled = true
|
|
required = ["query"]
|
|
description = '''
|
|
Search academic papers. For methods and published results, not for software how-to.'''
|
|
|
|
[tool.parameters.query]
|
|
type = "string"
|
|
|
|
[tool.parameters.max_results]
|
|
type = "integer"
|
|
|
|
[tool.parameters.open_access_only]
|
|
type = "boolean"
|
|
|
|
|
|
[[tool]]
|
|
name = "find_datasheet"
|
|
group = "research"
|
|
enabled = true
|
|
required = ["part_number"]
|
|
description = '''
|
|
Find the datasheet for an electronic component by part number.'''
|
|
|
|
[tool.parameters.part_number]
|
|
type = "string"
|
|
|
|
|
|
# --- the screen -------------------------------------------------------
|
|
# Eyes and hands: QMP screendump and input-send-event, which are the
|
|
# virtual hardware, so they work at the boot and login screens with no
|
|
# guest software involved. Every session has these; a job that never
|
|
# looks at the screen simply never calls them.
|
|
|
|
|
|
[[tool]]
|
|
name = "open_page"
|
|
group = "gui"
|
|
enabled = true
|
|
required = ["url"]
|
|
description = '''
|
|
Open a URL in the machine's own browser, in one call - no clicking into the address bar and no typing. Use it when you need to SEE a page: a download, a rendered diagram, a form. If you only need to know what a page SAYS, read_page is cheaper and does not touch the screen. Take a screenshot afterwards to see it.'''
|
|
|
|
[tool.parameters.url]
|
|
type = "string"
|
|
description = '''
|
|
Full http(s) URL, exactly as web_search gave it.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "launch_app"
|
|
group = "gui"
|
|
enabled = true
|
|
required = ["path"]
|
|
description = '''
|
|
Start a program in the project folder on the machine's REAL desktop, where a screenshot can see it. Use this for anything with a window - starting it with `run` puts it on a desktop nobody is looking at, which does not fail, it just never appears. Returns once the program is confirmed still running a few seconds later; take a screenshot next to see the window.'''
|
|
|
|
[tool.parameters.path]
|
|
type = "string"
|
|
description = '''
|
|
Relative path in the project folder, e.g. 'Toolkit.ps1'.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "screenshot"
|
|
group = "gui"
|
|
enabled = true
|
|
required = []
|
|
description = '''
|
|
Look at the machine's screen. Returns the image and its size in pixels; click coordinates are in those pixels, measured from the top left.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "click"
|
|
group = "gui"
|
|
enabled = true
|
|
required = ["x", "y"]
|
|
description = '''
|
|
Click at a pixel position. Take a screenshot first - the pointer goes exactly where you say, so a stale idea of what is on screen puts it in the wrong place.'''
|
|
|
|
[tool.parameters.x]
|
|
type = "integer"
|
|
|
|
[tool.parameters.y]
|
|
type = "integer"
|
|
|
|
[tool.parameters.button]
|
|
type = "string"
|
|
enum = ["left", "right", "middle"]
|
|
|
|
|
|
[[tool]]
|
|
name = "move"
|
|
group = "gui"
|
|
enabled = true
|
|
required = ["x", "y"]
|
|
description = '''
|
|
Move the pointer without clicking, for hover menus and drags.'''
|
|
|
|
[tool.parameters.x]
|
|
type = "integer"
|
|
|
|
[tool.parameters.y]
|
|
type = "integer"
|
|
|
|
|
|
[[tool]]
|
|
name = "key"
|
|
group = "gui"
|
|
enabled = true
|
|
required = ["keys"]
|
|
description = '''
|
|
Press a key or a chord, e.g. 'ret', 'tab', 'esc', 'ctrl+alt+t', 'alt+f4'. Names are QEMU's, and an unknown one is refused rather than guessed at.'''
|
|
|
|
[tool.parameters.keys]
|
|
type = "string"
|
|
description = '''
|
|
One key, or a chord joined by '+'.'''
|
|
|
|
|
|
[[tool]]
|
|
name = "type"
|
|
group = "gui"
|
|
enabled = true
|
|
required = ["text"]
|
|
description = '''
|
|
Type literal text at the keyboard. ASCII only. For anything long, write a file and open it in the machine instead - typing 2,000 characters one key at a time is slow and every keystroke is a chance for the focus to move.'''
|
|
|
|
[tool.parameters.text]
|
|
type = "string"
|
|
|
|
|
|
[[tool]]
|
|
name = "scroll"
|
|
group = "gui"
|
|
enabled = true
|
|
required = ["amount"]
|
|
description = '''
|
|
Scroll the wheel. Negative scrolls down, positive up.'''
|
|
|
|
[tool.parameters.x]
|
|
type = "integer"
|
|
|
|
[tool.parameters.y]
|
|
type = "integer"
|
|
|
|
[tool.parameters.amount]
|
|
type = "integer"
|
|
|