From d17c79228eab662bf3ca922c2e601e8459c21004 Mon Sep 17 00:00:00 2001 From: Rohit Ghumare Date: Wed, 6 May 2026 14:12:15 +0100 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20drop=20"tool"=20terminology=20?= =?UTF-8?q?=E2=80=94=20they=20are=20register=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agents don't have "tools" — they have functions. The "tool" word is an agent-framework convention that doesn't belong inside an iii-primitive runtime. Scrub it across the codebase. Function id renames (per-worker namespacing) tool::browser_* → browser::* (workers/browser) tool::lsp_* → lsp::* (workers/lsp-tools) tool::hashline_* → hashline::* (workers/hashline) agent::list_tools → agent::list_functions Generic agent-helper namespace tool::* → fn::* (placeholder ids in approval-tiers, security policy patterns, agent-core test fixtures) Struct + field renames ToolCall → FunctionCall (agent-core/types.rs) Capabilities.tools → .functions (agent-core) HandTools → HandFunctions (hand-runner) Hand.tools → .functions (hand-runner) A2aCapabilities.tools → .functions (a2a-cards) Config field renames [hand.tools] → [hand.functions] (7 HAND.toml files) [agent.tools] → [agent.functions] (45 agent.toml files) [agent.capabilities] tools = → functions = (45 agent.toml files) Worker manifests + descriptions agentos-core: "tool dispatch" → "function dispatch" approval-tiers: "tool capability" → "function capability" approval: "tool requires approval" → "function requires approval" hooks: "tool-call interception" → "function-call interception" loop-guard: "repeated-tool-call" → "repeated-call" mcp-client: rewrote to clarify MCP wire-format vs iii functions security: "tool policy" → "function-call policy" Wire-protocol boundary preserved llm-router still emits "tools": [...] over the wire to upstream LLM providers (Anthropic / OpenAI require it). Internal interface from agent-core to llm-router is unchanged shape; only naming inside our workers shifted. wasm-sandbox cleanup Dropped tool::web_fetch, tool::file_read, tool::file_list from WASM_ALLOWED_FUNCTIONS — those function ids are unregistered (legacy TS-era refs). When fs::* / net::* workers ship they go in the list. Website Workers carousel desc, UseCases title, Footer manifesto, Agents copy no longer say "tool"; replaced with "function" or domain-appropriate word ("category-shaped pieces" not "category-shaped tools"). Test fixtures All agent-core/a2a-cards/hand-runner test JSON updated to use the new field names. wasm-sandbox allow-list expected count updated 7 → 4. All 1281 release tests pass. --- ARCHITECTURE.md | 2 +- agents/ai-engineer/agent.toml | 2 +- agents/analyst/agent.toml | 2 +- agents/app-store-optimizer/agent.toml | 2 +- agents/architect/agent.toml | 2 +- agents/assistant/agent.toml | 2 +- agents/brand-guardian/agent.toml | 2 +- agents/code-reviewer/agent.toml | 2 +- agents/coder/agent.toml | 2 +- agents/content-creator/agent.toml | 2 +- agents/customer-support/agent.toml | 2 +- agents/data-scientist/agent.toml | 2 +- agents/debugger/agent.toml | 2 +- agents/devops-lead/agent.toml | 2 +- agents/doc-writer/agent.toml | 2 +- agents/email-assistant/agent.toml | 2 +- agents/evidence-collector/agent.toml | 2 +- agents/feedback-synthesizer/agent.toml | 2 +- agents/growth-hacker/agent.toml | 2 +- agents/health-tracker/agent.toml | 2 +- agents/hello-world/agent.toml | 2 +- agents/home-automation/agent.toml | 2 +- agents/image-prompt-engineer/agent.toml | 2 +- agents/legal-assistant/agent.toml | 2 +- agents/meeting-assistant/agent.toml | 2 +- agents/mobile-builder/agent.toml | 2 +- agents/ops/agent.toml | 2 +- agents/orchestrator/agent.toml | 2 +- agents/performance-benchmarker/agent.toml | 2 +- agents/personal-finance/agent.toml | 2 +- agents/planner/agent.toml | 2 +- agents/rapid-prototyper/agent.toml | 2 +- agents/reality-checker/agent.toml | 2 +- agents/recruiter/agent.toml | 2 +- agents/researcher/agent.toml | 2 +- agents/sales-assistant/agent.toml | 2 +- agents/security-auditor/agent.toml | 2 +- agents/social-media/agent.toml | 2 +- agents/sprint-prioritizer/agent.toml | 2 +- agents/test-engineer/agent.toml | 2 +- agents/translator/agent.toml | 2 +- agents/travel-planner/agent.toml | 2 +- agents/trend-researcher/agent.toml | 2 +- agents/tutor/agent.toml | 2 +- agents/ux-architect/agent.toml | 2 +- agents/writer/agent.toml | 2 +- hands/browser/HAND.toml | 4 +- hands/clip/HAND.toml | 4 +- hands/collector/HAND.toml | 4 +- hands/lead/HAND.toml | 4 +- hands/predictor/HAND.toml | 4 +- hands/researcher/HAND.toml | 4 +- hands/twitter/HAND.toml | 4 +- website/components/Agents.tsx | 7 +- website/components/Footer.tsx | 2 +- website/components/UseCases.tsx | 4 +- website/components/Workers.tsx | 4 +- workers/a2a-cards/src/main.rs | 10 +-- workers/a2a-cards/src/types.rs | 6 +- workers/agent-core/iii.worker.yaml | 2 +- workers/agent-core/src/main.rs | 86 +++++++++---------- workers/agent-core/src/types.rs | 76 ++++++++-------- workers/approval-tiers/iii.worker.yaml | 2 +- workers/approval-tiers/src/main.rs | 78 ++++++++--------- workers/browser/src/main.rs | 24 +++--- workers/context-cache/src/types.rs | 4 +- workers/context-monitor/src/main.rs | 6 +- workers/eval/src/main.rs | 2 +- workers/evolve/src/main.rs | 4 +- workers/feedback/src/main.rs | 2 +- workers/hand-runner/src/main.rs | 20 ++--- workers/hand-runner/src/types.rs | 10 +-- .../hand-runner/tests/fixtures/browser.toml | 4 +- workers/hashline/src/main.rs | 18 ++-- workers/hooks/iii.worker.yaml | 2 +- workers/hooks/src/main.rs | 8 +- workers/loop-guard/iii.worker.yaml | 2 +- workers/loop-guard/src/main.rs | 42 ++++----- workers/lsp-tools/src/main.rs | 20 ++--- workers/mcp-client/iii.worker.yaml | 2 +- workers/security/iii.worker.yaml | 2 +- workers/security/src/tool_policy.rs | 10 +-- workers/session-lifecycle/src/main.rs | 2 +- workers/task-decomposer/src/main.rs | 2 +- workers/wasm-sandbox/src/main.rs | 36 ++------ 85 files changed, 299 insertions(+), 320 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a85f1fe..be5bf61 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -109,7 +109,7 @@ iii v0.11.4 exposes `state::update` / `stream::update` with `UpdateOp::set`, `Up These are **declarative config**, not workers: -- `hands//HAND.toml` — agent persona (system prompt, allowed tools, schedule), consumed by the `hand-runner` worker. +- `hands//HAND.toml` — agent persona (system prompt, allowed function ids, schedule), consumed by the `hand-runner` worker. - `integrations/.toml` — MCP server connection details (transport, command, OAuth scopes), consumed by the `mcp-client` worker. - `agents//...` — markdown templates for spawning agent personas. - `workflows/.yaml` — pre-defined workflow definitions for the `workflow` worker. diff --git a/agents/ai-engineer/agent.toml b/agents/ai-engineer/agent.toml index 6594ac5..fdeb246 100644 --- a/agents/ai-engineer/agent.toml +++ b/agents/ai-engineer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/analyst/agent.toml b/agents/analyst/agent.toml index 328b8d2..520717a 100644 --- a/agents/analyst/agent.toml +++ b/agents/analyst/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/app-store-optimizer/agent.toml b/agents/app-store-optimizer/agent.toml index 4c7f591..4c23c31 100644 --- a/agents/app-store-optimizer/agent.toml +++ b/agents/app-store-optimizer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/architect/agent.toml b/agents/architect/agent.toml index 9898441..fbe1c2a 100644 --- a/agents/architect/agent.toml +++ b/agents/architect/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/assistant/agent.toml b/agents/assistant/agent.toml index cf29c2d..800cb9d 100644 --- a/agents/assistant/agent.toml +++ b/agents/assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/brand-guardian/agent.toml b/agents/brand-guardian/agent.toml index a7e3d41..6e124da 100644 --- a/agents/brand-guardian/agent.toml +++ b/agents/brand-guardian/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/code-reviewer/agent.toml b/agents/code-reviewer/agent.toml index 4c4efc7..03ae3fa 100644 --- a/agents/code-reviewer/agent.toml +++ b/agents/code-reviewer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/coder/agent.toml b/agents/coder/agent.toml index 42b1dbc..067d0d5 100644 --- a/agents/coder/agent.toml +++ b/agents/coder/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/content-creator/agent.toml b/agents/content-creator/agent.toml index d7da559..1028558 100644 --- a/agents/content-creator/agent.toml +++ b/agents/content-creator/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/customer-support/agent.toml b/agents/customer-support/agent.toml index 68d7daf..64fbacf 100644 --- a/agents/customer-support/agent.toml +++ b/agents/customer-support/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/data-scientist/agent.toml b/agents/data-scientist/agent.toml index 4af15ed..b2a5b4d 100644 --- a/agents/data-scientist/agent.toml +++ b/agents/data-scientist/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/debugger/agent.toml b/agents/debugger/agent.toml index 4527ca2..be6a8e8 100644 --- a/agents/debugger/agent.toml +++ b/agents/debugger/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/devops-lead/agent.toml b/agents/devops-lead/agent.toml index a6a389a..e423964 100644 --- a/agents/devops-lead/agent.toml +++ b/agents/devops-lead/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/doc-writer/agent.toml b/agents/doc-writer/agent.toml index 4c9cdeb..233e882 100644 --- a/agents/doc-writer/agent.toml +++ b/agents/doc-writer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/email-assistant/agent.toml b/agents/email-assistant/agent.toml index be48152..435c10d 100644 --- a/agents/email-assistant/agent.toml +++ b/agents/email-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/evidence-collector/agent.toml b/agents/evidence-collector/agent.toml index 48dce55..a72ff8d 100644 --- a/agents/evidence-collector/agent.toml +++ b/agents/evidence-collector/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/feedback-synthesizer/agent.toml b/agents/feedback-synthesizer/agent.toml index 06666ce..8d76607 100644 --- a/agents/feedback-synthesizer/agent.toml +++ b/agents/feedback-synthesizer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/growth-hacker/agent.toml b/agents/growth-hacker/agent.toml index 6d3c43b..f7dfc37 100644 --- a/agents/growth-hacker/agent.toml +++ b/agents/growth-hacker/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/health-tracker/agent.toml b/agents/health-tracker/agent.toml index e7f7015..f45b48c 100644 --- a/agents/health-tracker/agent.toml +++ b/agents/health-tracker/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/hello-world/agent.toml b/agents/hello-world/agent.toml index 3966403..6735ba6 100644 --- a/agents/hello-world/agent.toml +++ b/agents/hello-world/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/home-automation/agent.toml b/agents/home-automation/agent.toml index 50f8b25..c6de1d6 100644 --- a/agents/home-automation/agent.toml +++ b/agents/home-automation/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/image-prompt-engineer/agent.toml b/agents/image-prompt-engineer/agent.toml index 5f7c2bb..0e0193a 100644 --- a/agents/image-prompt-engineer/agent.toml +++ b/agents/image-prompt-engineer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/legal-assistant/agent.toml b/agents/legal-assistant/agent.toml index 982ec56..7e39dce 100644 --- a/agents/legal-assistant/agent.toml +++ b/agents/legal-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/meeting-assistant/agent.toml b/agents/meeting-assistant/agent.toml index 4be6dff..3c5d1ae 100644 --- a/agents/meeting-assistant/agent.toml +++ b/agents/meeting-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/mobile-builder/agent.toml b/agents/mobile-builder/agent.toml index 637cca7..aa1984d 100644 --- a/agents/mobile-builder/agent.toml +++ b/agents/mobile-builder/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/ops/agent.toml b/agents/ops/agent.toml index 3b29e1c..18bcafc 100644 --- a/agents/ops/agent.toml +++ b/agents/ops/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/orchestrator/agent.toml b/agents/orchestrator/agent.toml index 33b7596..cb745b4 100644 --- a/agents/orchestrator/agent.toml +++ b/agents/orchestrator/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/performance-benchmarker/agent.toml b/agents/performance-benchmarker/agent.toml index 2166a27..80bc1f5 100644 --- a/agents/performance-benchmarker/agent.toml +++ b/agents/performance-benchmarker/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::shell_exec", "tool::file_read", "tool::file_write", "tool::web_fetch", "tool::calculate", "tool::code_interpret"] +functions = ["fn::shell_exec", "fn::file_read", "fn::file_write", "fn::web_fetch", "fn::calculate", "fn::code_interpret"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["localhost", "*.staging.*", "*.perf.*", "*.test.*"] diff --git a/agents/personal-finance/agent.toml b/agents/personal-finance/agent.toml index 2e8ef71..958e4d6 100644 --- a/agents/personal-finance/agent.toml +++ b/agents/personal-finance/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/planner/agent.toml b/agents/planner/agent.toml index b9f59a0..8d990fc 100644 --- a/agents/planner/agent.toml +++ b/agents/planner/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/rapid-prototyper/agent.toml b/agents/rapid-prototyper/agent.toml index 9bc75be..da1d020 100644 --- a/agents/rapid-prototyper/agent.toml +++ b/agents/rapid-prototyper/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/reality-checker/agent.toml b/agents/reality-checker/agent.toml index 59d95db..a71544e 100644 --- a/agents/reality-checker/agent.toml +++ b/agents/reality-checker/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/recruiter/agent.toml b/agents/recruiter/agent.toml index 944e510..039649d 100644 --- a/agents/recruiter/agent.toml +++ b/agents/recruiter/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/researcher/agent.toml b/agents/researcher/agent.toml index f534bbb..307f23c 100644 --- a/agents/researcher/agent.toml +++ b/agents/researcher/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/sales-assistant/agent.toml b/agents/sales-assistant/agent.toml index 69a5314..0fe603f 100644 --- a/agents/sales-assistant/agent.toml +++ b/agents/sales-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/security-auditor/agent.toml b/agents/security-auditor/agent.toml index 77257c3..23283cb 100644 --- a/agents/security-auditor/agent.toml +++ b/agents/security-auditor/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/social-media/agent.toml b/agents/social-media/agent.toml index 8083561..f8d3d58 100644 --- a/agents/social-media/agent.toml +++ b/agents/social-media/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/sprint-prioritizer/agent.toml b/agents/sprint-prioritizer/agent.toml index cb7b2ab..1288a14 100644 --- a/agents/sprint-prioritizer/agent.toml +++ b/agents/sprint-prioritizer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/test-engineer/agent.toml b/agents/test-engineer/agent.toml index ff5cd33..5082332 100644 --- a/agents/test-engineer/agent.toml +++ b/agents/test-engineer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/translator/agent.toml b/agents/translator/agent.toml index 4632e66..7064f05 100644 --- a/agents/translator/agent.toml +++ b/agents/translator/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/travel-planner/agent.toml b/agents/travel-planner/agent.toml index 037d8c7..f131dc0 100644 --- a/agents/travel-planner/agent.toml +++ b/agents/travel-planner/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/trend-researcher/agent.toml b/agents/trend-researcher/agent.toml index d267ffd..0fc4d9e 100644 --- a/agents/trend-researcher/agent.toml +++ b/agents/trend-researcher/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/tutor/agent.toml b/agents/tutor/agent.toml index 7bf397b..dde57b5 100644 --- a/agents/tutor/agent.toml +++ b/agents/tutor/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/ux-architect/agent.toml b/agents/ux-architect/agent.toml index 2f9d7fb..c4e7fb3 100644 --- a/agents/ux-architect/agent.toml +++ b/agents/ux-architect/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/writer/agent.toml b/agents/writer/agent.toml index e346382..a7dbebb 100644 --- a/agents/writer/agent.toml +++ b/agents/writer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -tools = ["tool::*"] +functions = ["fn::*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/hands/browser/HAND.toml b/hands/browser/HAND.toml index 54ccf69..a6643f8 100644 --- a/hands/browser/HAND.toml +++ b/hands/browser/HAND.toml @@ -5,8 +5,8 @@ description = "Autonomously browses the web to complete multi-step tasks like fo enabled = true schedule = "0 */2 * * *" -[hand.tools] -allowed = ["tool::browser_navigate", "tool::browser_click", "tool::browser_type", "tool::browser_screenshot", "tool::web_fetch", "memory::store", "memory::query"] +[hand.functions] +allowed = ["browser::navigate", "browser::click", "browser::type", "browser::screenshot", "fn::web_fetch", "memory::store", "memory::query"] [[hand.settings]] key = "headless_mode" diff --git a/hands/clip/HAND.toml b/hands/clip/HAND.toml index f0d4b77..72e57b9 100644 --- a/hands/clip/HAND.toml +++ b/hands/clip/HAND.toml @@ -5,8 +5,8 @@ description = "Monitors clipboard for content, automatically categorizes, enrich enabled = true schedule = "* * * * *" -[hand.tools] -allowed = ["tool::clipboard_read", "tool::clipboard_write", "tool::web_search", "memory::store", "memory::query", "tool::file_write"] +[hand.functions] +allowed = ["fn::clipboard_read", "fn::clipboard_write", "fn::web_search", "memory::store", "memory::query", "fn::file_write"] [[hand.settings]] key = "auto_categorize" diff --git a/hands/collector/HAND.toml b/hands/collector/HAND.toml index 8c58bea..9f93d90 100644 --- a/hands/collector/HAND.toml +++ b/hands/collector/HAND.toml @@ -5,8 +5,8 @@ description = "Autonomously collects, normalizes, and stores data from configure enabled = true schedule = "*/30 * * * *" -[hand.tools] -allowed = ["tool::web_fetch", "tool::http_request", "tool::file_write", "tool::file_read", "memory::store"] +[hand.functions] +allowed = ["fn::web_fetch", "fn::http_request", "fn::file_write", "fn::file_read", "memory::store"] [[hand.settings]] key = "collection_mode" diff --git a/hands/lead/HAND.toml b/hands/lead/HAND.toml index bbc99d9..2aeaca6 100644 --- a/hands/lead/HAND.toml +++ b/hands/lead/HAND.toml @@ -5,8 +5,8 @@ description = "Autonomously discovers, qualifies, and enriches sales leads from enabled = true schedule = "0 8 * * 1-5" -[hand.tools] -allowed = ["tool::web_search", "tool::web_fetch", "tool::http_request", "memory::store", "memory::query", "tool::file_write", "tool::email_draft"] +[hand.functions] +allowed = ["fn::web_search", "fn::web_fetch", "fn::http_request", "memory::store", "memory::query", "fn::file_write", "fn::email_draft"] [[hand.settings]] key = "target_industry" diff --git a/hands/predictor/HAND.toml b/hands/predictor/HAND.toml index 0ff67e7..a38bece 100644 --- a/hands/predictor/HAND.toml +++ b/hands/predictor/HAND.toml @@ -5,8 +5,8 @@ description = "Analyzes collected data to identify patterns, forecast trends, an enabled = true schedule = "0 0 * * *" -[hand.tools] -allowed = ["memory::query", "memory::store", "tool::file_write", "tool::calculate"] +[hand.functions] +allowed = ["memory::query", "memory::store", "fn::file_write", "fn::calculate"] [[hand.settings]] key = "prediction_horizon" diff --git a/hands/researcher/HAND.toml b/hands/researcher/HAND.toml index b420f0d..f3e27c7 100644 --- a/hands/researcher/HAND.toml +++ b/hands/researcher/HAND.toml @@ -5,8 +5,8 @@ description = "Autonomously researches topics across the web, synthesizes findin enabled = true schedule = "0 */6 * * *" -[hand.tools] -allowed = ["tool::web_search", "tool::web_fetch", "tool::file_write", "memory::store", "memory::query"] +[hand.functions] +allowed = ["fn::web_search", "fn::web_fetch", "fn::file_write", "memory::store", "memory::query"] [[hand.settings]] key = "research_depth" diff --git a/hands/twitter/HAND.toml b/hands/twitter/HAND.toml index 267b91e..72adc4b 100644 --- a/hands/twitter/HAND.toml +++ b/hands/twitter/HAND.toml @@ -5,8 +5,8 @@ description = "Monitors Twitter/X for mentions, trends, and conversations, then enabled = true schedule = "*/15 * * * *" -[hand.tools] -allowed = ["tool::web_search", "tool::web_fetch", "tool::http_request", "memory::store", "memory::query", "tool::file_write"] +[hand.functions] +allowed = ["fn::web_search", "fn::web_fetch", "fn::http_request", "memory::store", "memory::query", "fn::file_write"] [[hand.settings]] key = "track_keywords" diff --git a/website/components/Agents.tsx b/website/components/Agents.tsx index b3721dc..d4af1d9 100644 --- a/website/components/Agents.tsx +++ b/website/components/Agents.tsx @@ -12,9 +12,10 @@ export default function Agents() { A small surface is the feature.

- Agent frameworks teach the model their abstractions — chains, tools, - graphs, prompts. AgentOS teaches it three nouns. Every capability — - the agent's own and its environment's — is a Function call. + Agent frameworks teach the model their abstractions — chains, + graphs, prompt templates. AgentOS teaches it three nouns. Every + capability — the agent's own and its environment's — is a Function + call.

Workers are discoverable at runtime. Functions self-describe. Triggers diff --git a/website/components/Footer.tsx b/website/components/Footer.tsx index b1b714b..7553013 100644 --- a/website/components/Footer.tsx +++ b/website/components/Footer.tsx @@ -34,7 +34,7 @@ export default function Footer() {

Manifesto

AgentOS isn't competing with one agent framework. It's competing with{" "} - the need to assemble a runtime from category-shaped tools — + the need to assemble a runtime from category-shaped pieces — queues, sandboxes, schedulers, observers — every time you ship an agent.

diff --git a/website/components/UseCases.tsx b/website/components/UseCases.tsx index 64f4078..a804d47 100644 --- a/website/components/UseCases.tsx +++ b/website/components/UseCases.tsx @@ -7,9 +7,9 @@ const cases = [ body: "A Slack message becomes a Trigger, the agent worker reads memory, llm-router routes the call, the response goes back through the same channel worker.", }, { - title: "Sandboxed tool execution", + title: "Sandboxed function dispatch", flow: ["agent-core", "approval", "wasm-sandbox", "hashline"], - body: "Tool calls pass through approval tiers, run in wasmtime with a fuel cap, and append to a hash-chained ledger.", + body: "Function calls pass through approval tiers, run in wasmtime with a fuel cap, and append to a hash-chained ledger.", }, { title: "Multi-tenant org with budgets", diff --git a/website/components/Workers.tsx b/website/components/Workers.tsx index 4abe63f..fafa08d 100644 --- a/website/components/Workers.tsx +++ b/website/components/Workers.tsx @@ -13,7 +13,7 @@ const workers: Worker[] = [ { name: "agent-core", ns: "agent::*", - desc: "ReAct loop. Routes LLM calls, tools, memory recall.", + desc: "ReAct loop. Dispatches LLM calls, function calls, memory recall.", badge: "FIRST-PARTY", snippet: `iii.trigger("agent::chat", { message: "review PR #42" })`, @@ -67,7 +67,7 @@ const workers: Worker[] = [ badge: "DOCKER", snippet: `iii.trigger("mcp::call", { server: "github", - tool: "list_issues" })`, + name: "list_issues" })`, }, { name: "hierarchy", diff --git a/workers/a2a-cards/src/main.rs b/workers/a2a-cards/src/main.rs index 6dc552d..45b5c01 100644 --- a/workers/a2a-cards/src/main.rs +++ b/workers/a2a-cards/src/main.rs @@ -48,10 +48,10 @@ async fn state_list(iii: &III, scope: &str) -> Result, IIIError> { Ok(v.as_array().cloned().unwrap_or_default()) } -async fn list_agent_tools(iii: &III, agent_id: &str) -> Result, IIIError> { +async fn list_agent_functions(iii: &III, agent_id: &str) -> Result, IIIError> { let res = iii .trigger(TriggerRequest { - function_id: "agent::list_tools".to_string(), + function_id: "agent::list_functions".to_string(), payload: json!({ "agentId": agent_id }), action: None, timeout_ms: None, @@ -106,7 +106,7 @@ async fn generate_card(iii: &III, req: GenerateCardRequest) -> Result Result Result { description: "AI agent operating system with multi-agent orchestration".into(), url: format!("{}/api/a2a/agents/orchestrator", api_url()), capabilities: A2aCapabilities { - tools: vec![], + functions: vec![], streaming: true, push_notifications: false, }, diff --git a/workers/a2a-cards/src/types.rs b/workers/a2a-cards/src/types.rs index 5f9cfad..74014c0 100644 --- a/workers/a2a-cards/src/types.rs +++ b/workers/a2a-cards/src/types.rs @@ -9,7 +9,7 @@ pub struct AgentSkillRef { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct A2aCapabilities { - pub tools: Vec, + pub functions: Vec, pub streaming: bool, #[serde(rename = "pushNotifications")] pub push_notifications: bool, @@ -52,7 +52,7 @@ mod tests { description: "test agent".into(), url: "http://localhost:3111/api/a2a/agents/x".into(), capabilities: A2aCapabilities { - tools: vec!["tool::a".into()], + functions: vec!["fn::a".into()], streaming: true, push_notifications: false, }, @@ -73,7 +73,7 @@ mod tests { assert_eq!(val["defaultOutputModes"], json!(["text"])); let back: A2aAgentCard = serde_json::from_value(val).unwrap(); assert_eq!(back.name, "agentos"); - assert_eq!(back.capabilities.tools, vec!["tool::a"]); + assert_eq!(back.capabilities.functions, vec!["fn::a"]); } #[test] diff --git a/workers/agent-core/iii.worker.yaml b/workers/agent-core/iii.worker.yaml index 209fca1..3ef9930 100644 --- a/workers/agent-core/iii.worker.yaml +++ b/workers/agent-core/iii.worker.yaml @@ -4,4 +4,4 @@ language: rust deploy: binary manifest: Cargo.toml bin: agentos-core -description: Agent loop — chat, tool dispatch, multi-iteration ReAct over iii primitives +description: Agent loop — chat, function dispatch, multi-iteration ReAct over iii primitives diff --git a/workers/agent-core/src/main.rs b/workers/agent-core/src/main.rs index 08db705..98ee226 100644 --- a/workers/agent-core/src/main.rs +++ b/workers/agent-core/src/main.rs @@ -5,7 +5,7 @@ use std::time::Instant; mod types; -use types::{AgentConfig, ChatRequest, ToolCall}; +use types::{AgentConfig, ChatRequest, FunctionCall}; @@ -34,14 +34,14 @@ async fn main() -> Result<(), Box> { let iii_clone = iii.clone(); iii.register_function( - RegisterFunction::new_async("agent::list_tools", move |input: Value| { + RegisterFunction::new_async("agent::list_functions", move |input: Value| { let iii = iii_clone.clone(); async move { let agent_id = input["agentId"].as_str().unwrap_or("default"); - list_tools(&iii, agent_id).await + list_functions(&iii, agent_id).await } }) - .description("List tools available to an agent"), + .description("List functions available to an agent"), ); let iii_clone = iii.clone(); @@ -160,9 +160,9 @@ async fn agent_chat(iii: &III, req: ChatRequest) -> Result { .await .unwrap_or(json!([])); - let tools: Value = iii + let functions: Value = iii .trigger(TriggerRequest { - function_id: "agent::list_tools".to_string(), + function_id: "agent::list_functions".to_string(), payload: json!({ "agentId": &req.agent_id }), action: None, timeout_ms: None, @@ -179,7 +179,7 @@ async fn agent_chat(iii: &III, req: ChatRequest) -> Result { function_id: "llm::route".to_string(), payload: json!({ "message": &req.message, - "toolCount": tools.as_array().map(|a| a.len()).unwrap_or(0), + "functionCount": functions.as_array().map(|a| a.len()).unwrap_or(0), "config": config.as_ref().and_then(|c| c.model.as_ref()), }), action: None, @@ -218,7 +218,7 @@ async fn agent_chat(iii: &III, req: ChatRequest) -> Result { "model": model, "systemPrompt": system_prompt, "messages": messages, - "tools": tools, + "functions": functions, }), action: None, timeout_ms: None, @@ -234,7 +234,7 @@ async fn agent_chat(iii: &III, req: ChatRequest) -> Result { } iterations += 1; - let calls: Vec = tool_calls + let calls: Vec = tool_calls .iter() .filter_map(|tc| serde_json::from_value(tc.clone()).ok()) .collect(); @@ -293,7 +293,7 @@ async fn agent_chat(iii: &III, req: ChatRequest) -> Result { "model": model, "systemPrompt": system_prompt, "messages": messages, - "tools": tools, + "functions": functions, }), action: None, timeout_ms: None, @@ -370,7 +370,7 @@ async fn agent_chat(iii: &III, req: ChatRequest) -> Result { })) } -async fn list_tools(iii: &III, agent_id: &str) -> Result { +async fn list_functions(iii: &III, agent_id: &str) -> Result { let config: Option = iii .trigger(TriggerRequest { function_id: "state::get".to_string(), @@ -385,7 +385,7 @@ async fn list_tools(iii: &III, agent_id: &str) -> Result { let allowed = config .as_ref() .and_then(|c| c.capabilities.as_ref()) - .map(|c| c.tools.clone()) + .map(|c| c.functions.clone()) .unwrap_or_else(|| vec!["*".into()]); let allowed: Vec = allowed @@ -473,7 +473,7 @@ mod tests { "id": "memory::store", "arguments": {"content": "test data", "agentId": "agent-1"}, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert_eq!(tc.call_id, "tc-1"); assert_eq!(tc.id, "memory::store"); assert_eq!(tc.arguments["content"], "test data"); @@ -481,7 +481,7 @@ mod tests { #[test] fn test_tool_call_id_split_for_capability() { - let tc = ToolCall { + let tc = FunctionCall { call_id: "c-1".to_string(), id: "security::check_capability".to_string(), arguments: json!({}), @@ -492,7 +492,7 @@ mod tests { #[test] fn test_tool_call_id_split_no_separator() { - let tc = ToolCall { + let tc = FunctionCall { call_id: "c-2".to_string(), id: "simple_tool".to_string(), arguments: json!({}), @@ -514,7 +514,7 @@ mod tests { }), system_prompt: Some("Be helpful".to_string()), capabilities: Some(Capabilities { - tools: vec!["*".to_string()], + functions: vec!["*".to_string()], memory_scopes: None, network_hosts: None, }), @@ -524,7 +524,7 @@ mod tests { tags: Some(vec!["test".to_string()]), }; assert_eq!(config.name, "Test Agent"); - assert!(config.capabilities.unwrap().tools.contains(&"*".to_string())); + assert!(config.capabilities.unwrap().functions.contains(&"*".to_string())); } #[test] @@ -738,7 +738,7 @@ mod tests { }), system_prompt: Some("You are an expert".to_string()), capabilities: Some(Capabilities { - tools: vec!["file::*".to_string(), "memory::*".to_string(), "network::*".to_string()], + functions: vec!["file::*".to_string(), "memory::*".to_string(), "network::*".to_string()], memory_scopes: Some(vec!["personal".to_string(), "shared".to_string()]), network_hosts: Some(vec!["api.anthropic.com".to_string()]), }), @@ -749,7 +749,7 @@ mod tests { }; assert_eq!(config.id, Some("full-agent".to_string())); assert_eq!(config.model.as_ref().unwrap().max_tokens, Some(16384)); - assert_eq!(config.capabilities.as_ref().unwrap().tools.len(), 3); + assert_eq!(config.capabilities.as_ref().unwrap().functions.len(), 3); assert_eq!(config.tags.as_ref().unwrap().len(), 3); } @@ -822,7 +822,7 @@ mod tests { fn test_tool_call_parsing_nested_arguments() { let json_val = json!({ "callId": "tc-nested", - "id": "tool::complex", + "id": "fn::complex", "arguments": { "config": { "nested": { @@ -833,7 +833,7 @@ mod tests { "items": [1, 2, 3], }, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments["config"]["nested"]["deep"].as_bool().unwrap()); assert_eq!(tc.arguments["config"]["nested"]["level"], 3); assert_eq!(tc.arguments["items"].as_array().unwrap().len(), 3); @@ -843,10 +843,10 @@ mod tests { fn test_tool_call_parsing_array_arguments() { let json_val = json!({ "callId": "tc-arr", - "id": "tool::batch", + "id": "fn::batch", "arguments": [1, "two", false, null], }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments.is_array()); assert_eq!(tc.arguments.as_array().unwrap().len(), 4); } @@ -855,10 +855,10 @@ mod tests { fn test_tool_call_parsing_empty_arguments() { let json_val = json!({ "callId": "tc-empty", - "id": "tool::noop", + "id": "fn::noop", "arguments": {}, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments.as_object().unwrap().is_empty()); } @@ -866,10 +866,10 @@ mod tests { fn test_tool_call_parsing_null_argument_value() { let json_val = json!({ "callId": "tc-null", - "id": "tool::nullarg", + "id": "fn::nullarg", "arguments": {"key": null}, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments["key"].is_null()); } @@ -956,10 +956,10 @@ mod tests { #[test] fn test_tool_filter_multiple_prefixes() { - let allowed = vec!["file::".to_string(), "memory::".to_string(), "tool::".to_string()]; + let allowed = vec!["file::".to_string(), "memory::".to_string(), "fn::".to_string()]; assert!(allowed.iter().any(|a| "file::read".starts_with(a.as_str()))); assert!(allowed.iter().any(|a| "memory::store".starts_with(a.as_str()))); - assert!(allowed.iter().any(|a| "tool::web_fetch".starts_with(a.as_str()))); + assert!(allowed.iter().any(|a| "fn::web_fetch".starts_with(a.as_str()))); assert!(!allowed.iter().any(|a| "network::send".starts_with(a.as_str()))); assert!(!allowed.iter().any(|a| "security::scan".starts_with(a.as_str()))); } @@ -990,7 +990,7 @@ mod tests { #[test] fn test_tool_call_id_split_multiple_separators() { - let tc = ToolCall { + let tc = FunctionCall { call_id: "c-3".to_string(), id: "security::check::deep".to_string(), arguments: json!({}), @@ -1001,7 +1001,7 @@ mod tests { #[test] fn test_tool_call_id_split_empty_string() { - let tc = ToolCall { + let tc = FunctionCall { call_id: "c-4".to_string(), id: "".to_string(), arguments: json!({}), @@ -1059,23 +1059,23 @@ mod tests { } #[test] - fn test_tool_count_from_empty_tools() { - let tools = json!([]); - let count = tools.as_array().map(|a| a.len()).unwrap_or(0); + fn test_tool_count_from_empty_functions() { + let functions = json!([]); + let count = functions.as_array().map(|a| a.len()).unwrap_or(0); assert_eq!(count, 0); } #[test] - fn test_tool_count_from_tools_array() { - let tools = json!([{"id": "a"}, {"id": "b"}, {"id": "c"}]); - let count = tools.as_array().map(|a| a.len()).unwrap_or(0); + fn test_tool_count_from_functions_array() { + let functions = json!([{"id": "a"}, {"id": "b"}, {"id": "c"}]); + let count = functions.as_array().map(|a| a.len()).unwrap_or(0); assert_eq!(count, 3); } #[test] fn test_tool_count_from_non_array() { - let tools = json!("not an array"); - let count = tools.as_array().map(|a| a.len()).unwrap_or(0); + let functions = json!("not an array"); + let count = functions.as_array().map(|a| a.len()).unwrap_or(0); assert_eq!(count, 0); } @@ -1092,7 +1092,7 @@ mod tests { }), system_prompt: Some("Be helpful".to_string()), capabilities: Some(Capabilities { - tools: vec!["*".to_string()], + functions: vec!["*".to_string()], memory_scopes: None, network_hosts: None, }), @@ -1132,7 +1132,7 @@ mod tests { json!({"missing": "fields"}), json!({"callId": "3", "id": "another::tool", "arguments": {"k": "v"}}), ]; - let calls: Vec = tool_calls + let calls: Vec = tool_calls .iter() .filter_map(|tc| serde_json::from_value(tc.clone()).ok()) .collect(); @@ -1148,7 +1148,7 @@ mod tests { json!(42), json!(null), ]; - let calls: Vec = tool_calls + let calls: Vec = tool_calls .iter() .filter_map(|tc| serde_json::from_value(tc.clone()).ok()) .collect(); @@ -1285,7 +1285,7 @@ mod tests { #[test] fn test_tool_call_id_split_only_separator() { - let tc = ToolCall { + let tc = FunctionCall { call_id: "c".to_string(), id: "::".to_string(), arguments: json!({}), diff --git a/workers/agent-core/src/types.rs b/workers/agent-core/src/types.rs index 69a96a3..47b6aea 100644 --- a/workers/agent-core/src/types.rs +++ b/workers/agent-core/src/types.rs @@ -13,7 +13,7 @@ pub struct ChatRequest { } #[derive(Debug, Serialize, Deserialize)] -pub struct ToolCall { +pub struct FunctionCall { #[serde(rename = "callId")] pub call_id: String, pub id: String, @@ -42,8 +42,8 @@ pub struct ModelConfig { } #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Capabilities { - pub tools: Vec, +pub struct Capabilities { + pub functions: Vec, #[serde(rename = "memoryScopes")] pub memory_scopes: Option>, #[serde(rename = "networkHosts")] @@ -109,7 +109,7 @@ mod tests { "id": "memory::recall", "arguments": {"query": "test"}, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert_eq!(tc.call_id, "call-1"); assert_eq!(tc.id, "memory::recall"); assert_eq!(tc.arguments["query"], "test"); @@ -117,7 +117,7 @@ mod tests { #[test] fn test_tool_call_serialization() { - let tc = ToolCall { + let tc = FunctionCall { call_id: "c-1".to_string(), id: "file::read".to_string(), arguments: json!({"path": "/tmp/file.txt"}), @@ -156,7 +156,7 @@ mod tests { }, "systemPrompt": "Be helpful", "capabilities": { - "tools": ["file::*", "memory::*"], + "functions": ["file::*", "memory::*"], "memoryScopes": ["default"], "networkHosts": ["api.example.com"], }, @@ -172,7 +172,7 @@ mod tests { assert_eq!(model.provider, Some("anthropic".to_string())); assert_eq!(model.max_tokens, Some(4096)); let caps = config.capabilities.unwrap(); - assert_eq!(caps.tools, vec!["file::*", "memory::*"]); + assert_eq!(caps.functions, vec!["file::*", "memory::*"]); assert_eq!(caps.memory_scopes, Some(vec!["default".to_string()])); let resources = config.resources.unwrap(); assert_eq!(resources.max_tokens_per_hour, Some(100000)); @@ -204,10 +204,10 @@ mod tests { #[test] fn test_capabilities_deserialization() { let json_val = json!({ - "tools": ["*"], + "functions": ["*"], }); let caps: Capabilities = serde_json::from_value(json_val).unwrap(); - assert_eq!(caps.tools, vec!["*"]); + assert_eq!(caps.functions, vec!["*"]); assert!(caps.memory_scopes.is_none()); assert!(caps.network_hosts.is_none()); } @@ -215,12 +215,12 @@ mod tests { #[test] fn test_capabilities_with_all_fields() { let json_val = json!({ - "tools": ["file::read", "memory::recall"], + "functions": ["file::read", "memory::recall"], "memoryScopes": ["personal", "shared"], "networkHosts": ["api.anthropic.com"], }); let caps: Capabilities = serde_json::from_value(json_val).unwrap(); - assert_eq!(caps.tools.len(), 2); + assert_eq!(caps.functions.len(), 2); assert_eq!(caps.memory_scopes.as_ref().unwrap().len(), 2); assert_eq!(caps.network_hosts.as_ref().unwrap().len(), 1); } @@ -279,19 +279,19 @@ mod tests { "id": "system::status", "arguments": {}, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments.is_object()); assert!(tc.arguments.as_object().unwrap().is_empty()); } #[test] - fn test_agent_config_empty_tools() { + fn test_agent_config_empty_functions() { let json_val = json!({ "name": "NoTools", - "capabilities": { "tools": [] }, + "capabilities": { "functions": [] }, }); let config: AgentConfig = serde_json::from_value(json_val).unwrap(); - assert!(config.capabilities.unwrap().tools.is_empty()); + assert!(config.capabilities.unwrap().functions.is_empty()); } #[test] @@ -326,7 +326,7 @@ mod tests { }), system_prompt: Some("Be precise".to_string()), capabilities: Some(Capabilities { - tools: vec!["file::*".to_string()], + functions: vec!["file::*".to_string()], memory_scopes: Some(vec!["self".to_string()]), network_hosts: Some(vec!["api.openai.com".to_string()]), }), @@ -353,25 +353,25 @@ mod tests { fn test_tool_call_nested_arguments() { let json_val = json!({ "callId": "c-nested", - "id": "tool::complex", + "id": "fn::complex", "arguments": { "config": {"nested": true, "depth": 3}, "items": [1, 2, 3], }, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments["config"]["nested"].as_bool().unwrap()); } #[test] fn test_tool_call_roundtrip() { - let tc = ToolCall { + let tc = FunctionCall { call_id: "rt-call".to_string(), id: "memory::store".to_string(), arguments: json!({"agentId": "a1", "content": "data"}), }; let json_str = serde_json::to_string(&tc).unwrap(); - let rt: ToolCall = serde_json::from_str(&json_str).unwrap(); + let rt: FunctionCall = serde_json::from_str(&json_str).unwrap(); assert_eq!(rt.call_id, "rt-call"); assert_eq!(rt.id, "memory::store"); } @@ -390,19 +390,19 @@ mod tests { } #[test] - fn test_capabilities_wildcard_tools() { + fn test_capabilities_wildcard_functions() { let caps = Capabilities { - tools: vec!["*".to_string()], + functions: vec!["*".to_string()], memory_scopes: None, network_hosts: None, }; - assert!(caps.tools.contains(&"*".to_string())); + assert!(caps.functions.contains(&"*".to_string())); } #[test] fn test_capabilities_multiple_network_hosts() { let json_val = json!({ - "tools": ["*"], + "functions": ["*"], "networkHosts": ["api.anthropic.com", "api.openai.com", "*.example.com"], }); let caps: Capabilities = serde_json::from_value(json_val).unwrap(); @@ -492,10 +492,10 @@ mod tests { fn test_tool_call_null_arguments() { let json_val = json!({ "callId": "c-null", - "id": "tool::test", + "id": "fn::test", "arguments": null, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments.is_null()); } @@ -503,10 +503,10 @@ mod tests { fn test_tool_call_array_arguments() { let json_val = json!({ "callId": "c-arr", - "id": "tool::batch", + "id": "fn::batch", "arguments": [1, "two", false, null, [3, 4]], }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert!(tc.arguments.is_array()); assert_eq!(tc.arguments.as_array().unwrap().len(), 5); } @@ -515,12 +515,12 @@ mod tests { fn test_tool_call_deeply_nested_arguments() { let json_val = json!({ "callId": "c-deep", - "id": "tool::deep", + "id": "fn::deep", "arguments": { "a": {"b": {"c": {"d": {"e": "bottom"}}}} }, }); - let tc: ToolCall = serde_json::from_value(json_val).unwrap(); + let tc: FunctionCall = serde_json::from_value(json_val).unwrap(); assert_eq!(tc.arguments["a"]["b"]["c"]["d"]["e"], "bottom"); } @@ -555,7 +555,7 @@ mod tests { }), system_prompt: Some("sp".to_string()), capabilities: Some(Capabilities { - tools: vec!["t".to_string()], + functions: vec!["t".to_string()], memory_scopes: Some(vec!["s".to_string()]), network_hosts: Some(vec!["h".to_string()]), }), @@ -620,24 +620,24 @@ mod tests { #[test] fn test_capabilities_wildcard_and_specific() { let caps = Capabilities { - tools: vec!["*".to_string(), "file::read".to_string()], + functions: vec!["*".to_string(), "file::read".to_string()], memory_scopes: None, network_hosts: None, }; - assert!(caps.tools.contains(&"*".to_string())); - assert!(caps.tools.contains(&"file::read".to_string())); - assert_eq!(caps.tools.len(), 2); + assert!(caps.functions.contains(&"*".to_string())); + assert!(caps.functions.contains(&"file::read".to_string())); + assert_eq!(caps.functions.len(), 2); } #[test] - fn test_capabilities_empty_tools_serialization() { + fn test_capabilities_empty_functions_serialization() { let caps = Capabilities { - tools: vec![], + functions: vec![], memory_scopes: Some(vec![]), network_hosts: Some(vec![]), }; let val = serde_json::to_value(&caps).unwrap(); - assert!(val["tools"].as_array().unwrap().is_empty()); + assert!(val["functions"].as_array().unwrap().is_empty()); assert!(val["memoryScopes"].as_array().unwrap().is_empty()); assert!(val["networkHosts"].as_array().unwrap().is_empty()); } diff --git a/workers/approval-tiers/iii.worker.yaml b/workers/approval-tiers/iii.worker.yaml index 758fa0f..dd9f024 100644 --- a/workers/approval-tiers/iii.worker.yaml +++ b/workers/approval-tiers/iii.worker.yaml @@ -4,4 +4,4 @@ language: rust deploy: binary manifest: Cargo.toml bin: agentos-approval-tiers -description: "Tiered approval routing — auto/async/sync classification by tool capability" +description: "Tiered approval routing — auto/async/sync classification by function capability" diff --git a/workers/approval-tiers/src/main.rs b/workers/approval-tiers/src/main.rs index 4096209..2002a48 100644 --- a/workers/approval-tiers/src/main.rs +++ b/workers/approval-tiers/src/main.rs @@ -14,25 +14,25 @@ fn auto_tools() -> &'static HashSet<&'static str> { static AUTO: OnceLock> = OnceLock::new(); AUTO.get_or_init(|| { [ - "tool::file_read", - "tool::file_list", - "tool::web_search", - "tool::web_fetch", + "fn::file_read", + "fn::file_list", + "fn::web_search", + "fn::web_fetch", "memory::recall", "memory::search", - "tool::code_analyze", - "tool::code_explain", - "tool::uuid_generate", - "tool::hash_compute", - "tool::json_parse", - "tool::json_stringify", - "tool::json_query", - "tool::csv_parse", - "tool::csv_stringify", - "tool::yaml_parse", - "tool::yaml_stringify", - "tool::regex_match", - "tool::regex_replace", + "fn::code_analyze", + "fn::code_explain", + "fn::uuid_generate", + "fn::hash_compute", + "fn::json_parse", + "fn::json_stringify", + "fn::json_query", + "fn::csv_parse", + "fn::csv_stringify", + "fn::yaml_parse", + "fn::yaml_stringify", + "fn::regex_match", + "fn::regex_replace", "skill::list", "skill::get", "skill::search", @@ -48,16 +48,16 @@ fn async_tools() -> &'static HashSet<&'static str> { static ASYNC: OnceLock> = OnceLock::new(); ASYNC.get_or_init(|| { [ - "tool::file_write", - "tool::apply_patch", - "tool::code_format", - "tool::code_lint", - "tool::todo_create", - "tool::todo_update", - "tool::todo_list", - "tool::cron_create", - "tool::cron_list", - "tool::cron_delete", + "fn::file_write", + "fn::apply_patch", + "fn::code_format", + "fn::code_lint", + "fn::todo_create", + "fn::todo_update", + "fn::todo_list", + "fn::cron_create", + "fn::cron_list", + "fn::cron_delete", "memory::store", "memory::forget", "skill::install", @@ -72,14 +72,14 @@ fn sync_tools() -> &'static HashSet<&'static str> { static SYNC: OnceLock> = OnceLock::new(); SYNC.get_or_init(|| { [ - "tool::shell_exec", - "tool::agent_spawn", - "tool::agent_send", - "tool::agent_delegate", - "tool::media_download", - "tool::network_check", - "tool::code_test", - "tool::env_get", + "fn::shell_exec", + "fn::agent_spawn", + "fn::agent_send", + "fn::agent_delegate", + "fn::media_download", + "fn::network_check", + "fn::code_test", + "fn::env_get", "agent::create", "agent::delete", "swarm::create", @@ -191,7 +191,7 @@ async fn classify(iii: &III, input: Value) -> Result { } } - if tool_id == "tool::shell_exec" + if tool_id == "fn::shell_exec" && tier == ApprovalTier::Sync && let Some(command) = input .get("args") @@ -572,19 +572,19 @@ mod tests { #[test] fn classify_known_auto() { - assert_eq!(classify_tool("tool::file_read"), ApprovalTier::Auto); + assert_eq!(classify_tool("fn::file_read"), ApprovalTier::Auto); assert_eq!(classify_tool("memory::recall"), ApprovalTier::Auto); } #[test] fn classify_known_async() { - assert_eq!(classify_tool("tool::file_write"), ApprovalTier::Async); + assert_eq!(classify_tool("fn::file_write"), ApprovalTier::Async); assert_eq!(classify_tool("skill::install"), ApprovalTier::Async); } #[test] fn classify_known_sync() { - assert_eq!(classify_tool("tool::shell_exec"), ApprovalTier::Sync); + assert_eq!(classify_tool("fn::shell_exec"), ApprovalTier::Sync); assert_eq!(classify_tool("agent::create"), ApprovalTier::Sync); } diff --git a/workers/browser/src/main.rs b/workers/browser/src/main.rs index ac08346..058ffa1 100644 --- a/workers/browser/src/main.rs +++ b/workers/browser/src/main.rs @@ -489,7 +489,7 @@ async fn main() -> Result<(), Box> { let iii_clone = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::browser_navigate", move |input: Value| { + RegisterFunction::new_async("browser::navigate", move |input: Value| { let iii = iii_clone.clone(); async move { let body = input.get("body").cloned().unwrap_or(input.clone()); @@ -525,7 +525,7 @@ async fn main() -> Result<(), Box> { let iii_clone = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::browser_click", move |input: Value| { + RegisterFunction::new_async("browser::click", move |input: Value| { let iii = iii_clone.clone(); async move { let body = input.get("body").cloned().unwrap_or(input.clone()); @@ -566,7 +566,7 @@ async fn main() -> Result<(), Box> { let iii_clone = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::browser_type", move |input: Value| { + RegisterFunction::new_async("browser::type", move |input: Value| { let iii = iii_clone.clone(); async move { let body = input.get("body").cloned().unwrap_or(input.clone()); @@ -601,7 +601,7 @@ async fn main() -> Result<(), Box> { let iii_clone = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::browser_screenshot", move |input: Value| { + RegisterFunction::new_async("browser::screenshot", move |input: Value| { let iii = iii_clone.clone(); async move { let body = input.get("body").cloned().unwrap_or(input.clone()); @@ -642,7 +642,7 @@ async fn main() -> Result<(), Box> { let iii_clone = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::browser_read_page", move |input: Value| { + RegisterFunction::new_async("browser::read_page", move |input: Value| { let iii = iii_clone.clone(); async move { let body = input.get("body").cloned().unwrap_or(input.clone()); @@ -677,7 +677,7 @@ async fn main() -> Result<(), Box> { let iii_clone = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::browser_close", move |input: Value| { + RegisterFunction::new_async("browser::close", move |input: Value| { let iii = iii_clone.clone(); async move { let body = input.get("body").cloned().unwrap_or(input.clone()); @@ -721,12 +721,12 @@ async fn main() -> Result<(), Box> { let triggers = [ ("browser::create_session", "POST", "api/browser/session"), ("browser::list_sessions", "GET", "api/browser/sessions"), - ("tool::browser_navigate", "POST", "api/browser/navigate"), - ("tool::browser_click", "POST", "api/browser/click"), - ("tool::browser_type", "POST", "api/browser/type"), - ("tool::browser_screenshot", "POST", "api/browser/screenshot"), - ("tool::browser_read_page", "POST", "api/browser/read"), - ("tool::browser_close", "POST", "api/browser/close"), + ("browser::navigate", "POST", "api/browser/navigate"), + ("browser::click", "POST", "api/browser/click"), + ("browser::type", "POST", "api/browser/type"), + ("browser::screenshot", "POST", "api/browser/screenshot"), + ("browser::read_page", "POST", "api/browser/read"), + ("browser::close", "POST", "api/browser/close"), ]; for (fid, method, path) in triggers { iii.register_trigger(RegisterTriggerInput { diff --git a/workers/context-cache/src/types.rs b/workers/context-cache/src/types.rs index ce01df4..c0d9e25 100644 --- a/workers/context-cache/src/types.rs +++ b/workers/context-cache/src/types.rs @@ -30,7 +30,7 @@ pub fn sanitize_id(id: &str) -> String { pub fn cacheable(fn_id: &str) -> bool { matches!( fn_id, - "memory::recall" | "memory::user_profile::get" | "agent::list_tools" + "memory::recall" | "memory::user_profile::get" | "agent::list_functions" ) } @@ -42,7 +42,7 @@ mod tests { fn cacheable_set_matches_ts() { assert!(cacheable("memory::recall")); assert!(cacheable("memory::user_profile::get")); - assert!(cacheable("agent::list_tools")); + assert!(cacheable("agent::list_functions")); assert!(!cacheable("memory::store")); } diff --git a/workers/context-monitor/src/main.rs b/workers/context-monitor/src/main.rs index 407332b..4921057 100644 --- a/workers/context-monitor/src/main.rs +++ b/workers/context-monitor/src/main.rs @@ -809,7 +809,7 @@ mod tests { Message { role: "assistant".into(), content: "".into(), - tool_calls: Some(json!([{ "callId": "tc1", "id": "tool::read" }])), + tool_calls: Some(json!([{ "callId": "tc1", "id": "fn::read" }])), tool_results: None, tool_call_id: None, timestamp: None, @@ -848,8 +848,8 @@ mod tests { role: "assistant".into(), content: "".into(), tool_calls: Some(json!([ - { "callId": "tc1", "id": "tool::read" }, - { "callId": "tc2", "id": "tool::write" } + { "callId": "tc1", "id": "fn::read" }, + { "callId": "tc2", "id": "fn::write" } ])), tool_results: None, tool_call_id: None, diff --git a/workers/eval/src/main.rs b/workers/eval/src/main.rs index 291302a..327b33c 100644 --- a/workers/eval/src/main.rs +++ b/workers/eval/src/main.rs @@ -6,7 +6,7 @@ mod types; use types::{EvalResult, EvalScores, EvalSuite, EvalTestCase}; -const ALLOWED_SCORER_PREFIXES: &[&str] = &["evolved::", "eval::", "tool::"]; +const ALLOWED_SCORER_PREFIXES: &[&str] = &["evolved::", "eval::", "fn::"]; const W_CORRECTNESS: f64 = 0.5; const W_LATENCY: f64 = 0.15; diff --git a/workers/evolve/src/main.rs b/workers/evolve/src/main.rs index 2cd4a7b..94e9742 100644 --- a/workers/evolve/src/main.rs +++ b/workers/evolve/src/main.rs @@ -172,7 +172,7 @@ async fn evolve_generate(iii: &III, input: Value) -> Result { let function_id = format!("evolved::{safe_name}_v{next_version}"); let prompt = format!( - "Write a JavaScript function that accomplishes the following goal. Return ONLY the function body as an arrow function expression. Do not include markdown, explanations, or code fences.\n\nGoal: {goal}\n{}\n\nThe function receives a single `input` parameter (any type) and must return a result.\nIt has access to: JSON, Math, Date, Array, Object, String, Number, Boolean, Map, Set, Promise, parseInt, parseFloat.\nIt can call `await trigger({{ function_id: fnId, payload: data }})` to invoke other functions (only evolved::, tool::, llm:: prefixes).\nIt CANNOT use: fetch, fs, process, require, setTimeout, eval, Function constructor.\n\nExample: async (input) => {{ return {{ result: input.value * 2 }}; }}", + "Write a JavaScript function that accomplishes the following goal. Return ONLY the function body as an arrow function expression. Do not include markdown, explanations, or code fences.\n\nGoal: {goal}\n{}\n\nThe function receives a single `input` parameter (any type) and must return a result.\nIt has access to: JSON, Math, Date, Array, Object, String, Number, Boolean, Map, Set, Promise, parseInt, parseFloat.\nIt can call `await trigger({{ function_id: fnId, payload: data }})` to invoke other functions (only evolved::, fn::, llm:: prefixes).\nIt CANNOT use: fetch, fs, process, require, setTimeout, eval, Function constructor.\n\nExample: async (input) => {{ return {{ result: input.value * 2 }}; }}", if spec.is_empty() { String::new() } else { @@ -439,7 +439,7 @@ async fn evolve_fork(iii: &III, input: Value) -> Result { let function_id = format!("evolved::{safe_name}_v{next_version}"); let prompt = format!( - "Improve the following JavaScript function based on the goal below. Return ONLY the function body as an arrow function expression. Do not include markdown, explanations, or code fences.\n\nCurrent code:\n{}\n\nCurrent description: {}\n\nImprovement goal: {goal}\n\nThe function receives a single `input` parameter and must return a result.\nIt has access to: JSON, Math, Date, Array, Object, String, Number, Boolean, Map, Set, Promise.\nIt can call `await trigger({{ function_id: fnId, payload: data }})` for evolved::, tool::, llm:: prefixes.", + "Improve the following JavaScript function based on the goal below. Return ONLY the function body as an arrow function expression. Do not include markdown, explanations, or code fences.\n\nCurrent code:\n{}\n\nCurrent description: {}\n\nImprovement goal: {goal}\n\nThe function receives a single `input` parameter and must return a result.\nIt has access to: JSON, Math, Date, Array, Object, String, Number, Boolean, Map, Set, Promise.\nIt can call `await trigger({{ function_id: fnId, payload: data }})` for evolved::, fn::, llm:: prefixes.", source.code, source.description, ); diff --git a/workers/feedback/src/main.rs b/workers/feedback/src/main.rs index 3c09602..037a67b 100644 --- a/workers/feedback/src/main.rs +++ b/workers/feedback/src/main.rs @@ -888,7 +888,7 @@ async fn feedback_inject_signal(iii: &III, input: Value) -> Result anyhow::Result> { fn build_kickoff_prompt(hand: &Hand) -> String { let now = chrono::Utc::now().to_rfc3339(); - let tools = hand.tools.allowed.join(", "); + let functions = hand.functions.allowed.join(", "); let settings_lines = hand .settings .iter() @@ -82,8 +82,8 @@ fn build_kickoff_prompt(hand: &Hand) -> String { .collect::>() .join("\n"); format!( - "[{} — Autonomous Run — {}]\n\nReview your current task queue and execute pending work.\nAvailable tools: {}\n\n{}", - hand.name, now, tools, settings_lines, + "[{} — Autonomous Run — {}]\n\nReview your current task queue and execute pending work.\nAvailable functions: {}\n\n{}", + hand.name, now, functions, settings_lines, ) } @@ -99,7 +99,7 @@ async fn run_hand(iii: &III, hand: Hand) -> Result { payload: json!({ "agentId": hand.id, "message": kickoff, - "tools": hand.tools.allowed, + "functions": hand.functions.allowed, "systemPrompt": hand.agent.system_prompt, "temperature": hand.agent.temperature, "maxIterations": hand.agent.max_iterations, @@ -390,8 +390,8 @@ name = "Alpha" enabled = true schedule = "* * * * *" -[hand.tools] -allowed = ["tool::a"] +[hand.functions] +allowed = ["fn::a"] [hand.agent] max_iterations = 5 @@ -440,15 +440,15 @@ name = "Real" } #[test] - fn kickoff_prompt_contains_name_and_tools() { + fn kickoff_prompt_contains_name_and_functions() { let hand = Hand { id: "x".into(), name: "Watcher".into(), description: String::new(), enabled: true, schedule: "* * * * *".into(), - tools: types::HandTools { - allowed: vec!["tool::shell_exec".into(), "tool::web_fetch".into()], + functions: types::HandFunctions { + allowed: vec!["fn::shell_exec".into(), "fn::web_fetch".into()], }, settings: vec![types::HandSetting { key: "target".into(), @@ -461,7 +461,7 @@ name = "Real" }; let prompt = build_kickoff_prompt(&hand); assert!(prompt.contains("Watcher")); - assert!(prompt.contains("tool::shell_exec")); + assert!(prompt.contains("fn::shell_exec")); assert!(prompt.contains("target")); } diff --git a/workers/hand-runner/src/types.rs b/workers/hand-runner/src/types.rs index 9cea0e0..0bd8067 100644 --- a/workers/hand-runner/src/types.rs +++ b/workers/hand-runner/src/types.rs @@ -18,7 +18,7 @@ pub struct Hand { #[serde(default)] pub schedule: String, #[serde(default)] - pub tools: HandTools, + pub functions: HandFunctions, #[serde(default)] pub settings: Vec, #[serde(default)] @@ -33,7 +33,7 @@ fn default_enabled() -> bool { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(deny_unknown_fields)] -pub struct HandTools { +pub struct HandFunctions { #[serde(default)] pub allowed: Vec, } @@ -91,7 +91,7 @@ mod tests { assert_eq!(parsed.hand.name, "Web Browser Agent"); assert!(parsed.hand.enabled); assert_eq!(parsed.hand.schedule, "0 */2 * * *"); - assert!(parsed.hand.tools.allowed.contains(&"tool::browser_navigate".to_string())); + assert!(parsed.hand.functions.allowed.contains(&"browser::navigate".to_string())); assert_eq!(parsed.hand.agent.max_iterations, Some(80)); assert_eq!(parsed.hand.agent.temperature, Some(0.2)); assert!(parsed.hand.agent.system_prompt.contains("Phase 1")); @@ -107,7 +107,7 @@ mod tests { let parsed: HandFile = toml::from_str(FIXTURE_MINIMAL).expect("parse minimal fixture"); assert_eq!(parsed.hand.id, "tiny"); assert!(parsed.hand.enabled); - assert!(parsed.hand.tools.allowed.is_empty()); + assert!(parsed.hand.functions.allowed.is_empty()); assert!(parsed.hand.settings.is_empty()); } @@ -128,6 +128,6 @@ name = "X" let json = serde_json::to_value(&parsed.hand).unwrap(); let back: Hand = serde_json::from_value(json).unwrap(); assert_eq!(back.id, parsed.hand.id); - assert_eq!(back.tools.allowed, parsed.hand.tools.allowed); + assert_eq!(back.functions.allowed, parsed.hand.functions.allowed); } } diff --git a/workers/hand-runner/tests/fixtures/browser.toml b/workers/hand-runner/tests/fixtures/browser.toml index 54ccf69..a6643f8 100644 --- a/workers/hand-runner/tests/fixtures/browser.toml +++ b/workers/hand-runner/tests/fixtures/browser.toml @@ -5,8 +5,8 @@ description = "Autonomously browses the web to complete multi-step tasks like fo enabled = true schedule = "0 */2 * * *" -[hand.tools] -allowed = ["tool::browser_navigate", "tool::browser_click", "tool::browser_type", "tool::browser_screenshot", "tool::web_fetch", "memory::store", "memory::query"] +[hand.functions] +allowed = ["browser::navigate", "browser::click", "browser::type", "browser::screenshot", "fn::web_fetch", "memory::store", "memory::query"] [[hand.settings]] key = "headless_mode" diff --git a/workers/hashline/src/main.rs b/workers/hashline/src/main.rs index 1cb8684..935e1aa 100644 --- a/workers/hashline/src/main.rs +++ b/workers/hashline/src/main.rs @@ -273,7 +273,7 @@ async fn main() -> Result<(), Box> { let iii_ref = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::hashline_read", move |input: Value| { + RegisterFunction::new_async("hashline::read", move |input: Value| { let _iii = iii_ref.clone(); async move { let body = extract_body(&input); @@ -306,7 +306,7 @@ async fn main() -> Result<(), Box> { payload: json!({ "name": "tool_execution_total", "value": 1, - "labels": { "toolId": "tool::hashline_read", "status": "success" } + "labels": { "toolId": "hashline::read", "status": "success" } }), action: None, timeout_ms: None, @@ -327,7 +327,7 @@ async fn main() -> Result<(), Box> { let iii_ref = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::hashline_edit", move |input: Value| { + RegisterFunction::new_async("hashline::edit", move |input: Value| { let _iii = iii_ref.clone(); async move { let body = extract_body(&input); @@ -385,7 +385,7 @@ async fn main() -> Result<(), Box> { payload: json!({ "name": "tool_execution_total", "value": 1, - "labels": { "toolId": "tool::hashline_edit", "status": "success" } + "labels": { "toolId": "hashline::edit", "status": "success" } }), action: None, timeout_ms: None, @@ -405,7 +405,7 @@ async fn main() -> Result<(), Box> { let iii_ref = iii.clone(); iii.register_function( - RegisterFunction::new_async("tool::hashline_diff", move |input: Value| { + RegisterFunction::new_async("hashline::diff", move |input: Value| { let _iii = iii_ref.clone(); async move { let body = extract_body(&input); @@ -455,7 +455,7 @@ async fn main() -> Result<(), Box> { payload: json!({ "name": "tool_execution_total", "value": 1, - "labels": { "toolId": "tool::hashline_diff", "status": "success" } + "labels": { "toolId": "hashline::diff", "status": "success" } }), action: None, timeout_ms: None, @@ -475,19 +475,19 @@ async fn main() -> Result<(), Box> { iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::hashline_read".to_string(), + function_id: "hashline::read".to_string(), config: json!({ "api_path": "api/hashline/read", "http_method": "POST" }), metadata: None, })?; iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::hashline_edit".to_string(), + function_id: "hashline::edit".to_string(), config: json!({ "api_path": "api/hashline/edit", "http_method": "POST" }), metadata: None, })?; iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::hashline_diff".to_string(), + function_id: "hashline::diff".to_string(), config: json!({ "api_path": "api/hashline/diff", "http_method": "POST" }), metadata: None, })?; diff --git a/workers/hooks/iii.worker.yaml b/workers/hooks/iii.worker.yaml index fcecfac..07ed177 100644 --- a/workers/hooks/iii.worker.yaml +++ b/workers/hooks/iii.worker.yaml @@ -4,4 +4,4 @@ language: rust deploy: binary manifest: Cargo.toml bin: agentos-hooks -description: Hook registration and firing — agent lifecycle, tool-call interception, prompt mutation +description: Hook registration and firing — agent lifecycle, function-call interception, prompt mutation diff --git a/workers/hooks/src/main.rs b/workers/hooks/src/main.rs index af1e278..2905c1c 100644 --- a/workers/hooks/src/main.rs +++ b/workers/hooks/src/main.rs @@ -587,7 +587,7 @@ mod tests { function_id: "fn::x".into(), enabled: true, agent_id: Some("agent-1".into()), - filter: Some(json!({ "toolIds": ["tool::a"] })), + filter: Some(json!({ "toolIds": ["fn::a"] })), created_at: 12345, }; let v = serde_json::to_value(&h).unwrap(); @@ -689,12 +689,12 @@ mod tests { function_id: "fn".into(), enabled: true, agent_id: None, - filter: Some(json!({ "toolIds": ["tool::a", "tool::b"] })), + filter: Some(json!({ "toolIds": ["fn::a", "fn::b"] })), created_at: 0, }; let allowed = match &hook.filter { Some(f) => match f.get("toolIds").and_then(|v| v.as_array()) { - Some(ids) => ids.iter().any(|x| x.as_str() == Some("tool::a")), + Some(ids) => ids.iter().any(|x| x.as_str() == Some("fn::a")), None => true, }, None => true, @@ -703,7 +703,7 @@ mod tests { let not_allowed = match &hook.filter { Some(f) => match f.get("toolIds").and_then(|v| v.as_array()) { - Some(ids) => ids.iter().any(|x| x.as_str() == Some("tool::z")), + Some(ids) => ids.iter().any(|x| x.as_str() == Some("fn::z")), None => true, }, None => true, diff --git a/workers/loop-guard/iii.worker.yaml b/workers/loop-guard/iii.worker.yaml index f83cdd1..f0db118 100644 --- a/workers/loop-guard/iii.worker.yaml +++ b/workers/loop-guard/iii.worker.yaml @@ -4,4 +4,4 @@ language: rust deploy: binary manifest: Cargo.toml bin: agentos-loop-guard -description: Agent loop guard — iteration cap, ping-pong detection, repeated-tool-call backoff +description: Agent loop guard — iteration cap, ping-pong detection, repeated-call backoff diff --git a/workers/loop-guard/src/main.rs b/workers/loop-guard/src/main.rs index e780413..bbd3769 100644 --- a/workers/loop-guard/src/main.rs +++ b/workers/loop-guard/src/main.rs @@ -14,7 +14,7 @@ const BACKOFF_SCHEDULE: &[i64] = &[5000, 10000, 30000, 60000]; const AGENT_TTL_MS: i64 = 3_600_000; fn is_poll_tool(tool: &str) -> bool { - matches!(tool, "tool::shell_exec" | "tool::web_fetch") + matches!(tool, "fn::shell_exec" | "fn::web_fetch") } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -449,51 +449,51 @@ mod tests { #[test] fn hash_call_returns_16_char_hex() { - let h = hash_call("tool::test", &json!({ "a": 1 })); + let h = hash_call("fn::test", &json!({ "a": 1 })); assert_eq!(h.len(), 16); assert!(h.chars().all(|c| c.is_ascii_hexdigit())); } #[test] fn hash_call_is_deterministic() { - let h1 = hash_call("tool::a", &json!({ "x": 1 })); - let h2 = hash_call("tool::a", &json!({ "x": 1 })); + let h1 = hash_call("fn::a", &json!({ "x": 1 })); + let h2 = hash_call("fn::a", &json!({ "x": 1 })); assert_eq!(h1, h2); } #[test] fn hash_call_differs_by_tool() { - let h1 = hash_call("tool::a", &json!({ "x": 1 })); - let h2 = hash_call("tool::b", &json!({ "x": 1 })); + let h1 = hash_call("fn::a", &json!({ "x": 1 })); + let h2 = hash_call("fn::b", &json!({ "x": 1 })); assert_ne!(h1, h2); } #[test] fn hash_call_differs_by_params() { - let h1 = hash_call("tool::a", &json!({ "x": 1 })); - let h2 = hash_call("tool::a", &json!({ "x": 2 })); + let h1 = hash_call("fn::a", &json!({ "x": 1 })); + let h2 = hash_call("fn::a", &json!({ "x": 2 })); assert_ne!(h1, h2); } #[test] fn hash_call_handles_empty_params() { - let h = hash_call("tool::a", &json!({})); + let h = hash_call("fn::a", &json!({})); assert_eq!(h.len(), 16); } #[test] fn hash_call_handles_nested_params() { - let h = hash_call("tool::a", &json!({ "nested": { "deep": true } })); + let h = hash_call("fn::a", &json!({ "nested": { "deep": true } })); assert_eq!(h.len(), 16); } #[test] fn ping_pong_under_six_no_detect() { let h: Vec = vec![ - rec("a", "tool::a", 1), - rec("b", "tool::b", 2), - rec("a", "tool::a", 3), - rec("b", "tool::b", 4), + rec("a", "fn::a", 1), + rec("b", "fn::b", 2), + rec("a", "fn::a", 3), + rec("b", "fn::b", 4), ]; assert!(!detect_ping_pong(&h).0); } @@ -504,7 +504,7 @@ mod tests { for i in 0..8 { h.push(rec( if i % 2 == 0 { "aaa" } else { "bbb" }, - if i % 2 == 0 { "tool::a" } else { "tool::b" }, + if i % 2 == 0 { "fn::a" } else { "fn::b" }, i, )); } @@ -517,7 +517,7 @@ mod tests { let mut h = Vec::new(); for i in 0..9 { let hh = hashes[i % 3]; - h.push(rec(hh, &format!("tool::{hh}"), i as i64)); + h.push(rec(hh, &format!("fn::{hh}"), i as i64)); } assert!(detect_ping_pong(&h).0); } @@ -527,7 +527,7 @@ mod tests { let mut h = Vec::new(); for i in 0..10 { let hh = format!("unique-{i}"); - h.push(rec(&hh, &format!("tool::{hh}"), i)); + h.push(rec(&hh, &format!("fn::{hh}"), i)); } assert!(!detect_ping_pong(&h).0); } @@ -538,7 +538,7 @@ mod tests { for i in 0..8 { h.push(rec( if i % 2 == 0 { "pp1" } else { "pp2" }, - if i % 2 == 0 { "tool::read" } else { "tool::write" }, + if i % 2 == 0 { "fn::read" } else { "fn::write" }, i, )); } @@ -549,9 +549,9 @@ mod tests { #[test] fn poll_tools_recognised() { - assert!(is_poll_tool("tool::shell_exec")); - assert!(is_poll_tool("tool::web_fetch")); - assert!(!is_poll_tool("tool::other")); + assert!(is_poll_tool("fn::shell_exec")); + assert!(is_poll_tool("fn::web_fetch")); + assert!(!is_poll_tool("fn::other")); } #[test] diff --git a/workers/lsp-tools/src/main.rs b/workers/lsp-tools/src/main.rs index c09dd9c..f9bfa51 100644 --- a/workers/lsp-tools/src/main.rs +++ b/workers/lsp-tools/src/main.rs @@ -211,7 +211,7 @@ async fn main() -> Result<(), Box> { let iii = register_worker(&ws_url, InitOptions::default()); iii.register_function( - RegisterFunction::new_async("tool::lsp_diagnostics", move |input: Value| async move { + RegisterFunction::new_async("lsp::diagnostics", move |input: Value| async move { let body = body_or_self(&input); let path = body .get("path") @@ -282,7 +282,7 @@ async fn main() -> Result<(), Box> { ); iii.register_function( - RegisterFunction::new_async("tool::lsp_symbols", move |input: Value| async move { + RegisterFunction::new_async("lsp::symbols", move |input: Value| async move { let body = body_or_self(&input); let path = body .get("path") @@ -308,7 +308,7 @@ async fn main() -> Result<(), Box> { ); iii.register_function( - RegisterFunction::new_async("tool::lsp_references", move |input: Value| async move { + RegisterFunction::new_async("lsp::references", move |input: Value| async move { let body = body_or_self(&input); let symbol = body .get("symbol") @@ -370,7 +370,7 @@ async fn main() -> Result<(), Box> { ); iii.register_function( - RegisterFunction::new_async("tool::lsp_rename", move |input: Value| async move { + RegisterFunction::new_async("lsp::rename", move |input: Value| async move { let body = body_or_self(&input); let old_name = body .get("oldName") @@ -487,7 +487,7 @@ async fn main() -> Result<(), Box> { ); iii.register_function( - RegisterFunction::new_async("tool::lsp_goto_definition", move |input: Value| async move { + RegisterFunction::new_async("lsp::goto_definition", move |input: Value| async move { let body = body_or_self(&input); let symbol = body .get("symbol") @@ -544,31 +544,31 @@ async fn main() -> Result<(), Box> { iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::lsp_diagnostics".to_string(), + function_id: "lsp::diagnostics".to_string(), config: json!({ "api_path": "api/lsp/diagnostics", "http_method": "POST" }), metadata: None, })?; iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::lsp_symbols".to_string(), + function_id: "lsp::symbols".to_string(), config: json!({ "api_path": "api/lsp/symbols", "http_method": "POST" }), metadata: None, })?; iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::lsp_references".to_string(), + function_id: "lsp::references".to_string(), config: json!({ "api_path": "api/lsp/references", "http_method": "POST" }), metadata: None, })?; iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::lsp_rename".to_string(), + function_id: "lsp::rename".to_string(), config: json!({ "api_path": "api/lsp/rename", "http_method": "POST" }), metadata: None, })?; iii.register_trigger(RegisterTriggerInput { trigger_type: "http".to_string(), - function_id: "tool::lsp_goto_definition".to_string(), + function_id: "lsp::goto_definition".to_string(), config: json!({ "api_path": "api/lsp/goto-definition", "http_method": "POST" }), metadata: None, })?; diff --git a/workers/mcp-client/iii.worker.yaml b/workers/mcp-client/iii.worker.yaml index 2e7c1cc..7cc08b9 100644 --- a/workers/mcp-client/iii.worker.yaml +++ b/workers/mcp-client/iii.worker.yaml @@ -4,4 +4,4 @@ language: rust deploy: binary manifest: Cargo.toml bin: agentos-mcp-client -description: MCP client + server bridge — connect/disconnect MCP servers via stdio, list/call tools, and serve agentos functions as MCP tools +description: MCP client + server bridge — connects to remote MCP servers, surfaces their capabilities as iii functions, and re-exports agentos functions over the MCP wire protocol diff --git a/workers/security/iii.worker.yaml b/workers/security/iii.worker.yaml index 0ed6cc3..5c1f812 100644 --- a/workers/security/iii.worker.yaml +++ b/workers/security/iii.worker.yaml @@ -4,4 +4,4 @@ language: rust deploy: binary manifest: Cargo.toml bin: agentos-security -description: Combined guardrails + audit — HMAC-chained log, prompt-injection scan, capability checks, tool policy, signing, taint +description: Combined guardrails + audit — HMAC-chained log, prompt-injection scan, capability checks, function-call policy, signing, taint diff --git a/workers/security/src/tool_policy.rs b/workers/security/src/tool_policy.rs index 606344f..9e530ef 100644 --- a/workers/security/src/tool_policy.rs +++ b/workers/security/src/tool_policy.rs @@ -285,10 +285,10 @@ mod tests { #[test] fn test_policy_rule_serialization_roundtrip() { - let rule = allow_rule("tool::*"); + let rule = allow_rule("fn::*"); let serialized = serde_json::to_string(&rule).unwrap(); let deserialized: PolicyRule = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized.pattern, "tool::*"); + assert_eq!(deserialized.pattern, "fn::*"); } #[test] @@ -439,8 +439,8 @@ mod tests { #[test] fn test_glob_matches_special_chars_colons() { - assert!(glob_matches("ns::tool::v2", "ns::tool::v2")); - assert!(!glob_matches("ns::tool::v2", "ns::tool::v3")); + assert!(glob_matches("ns::fn::v2", "ns::fn::v2")); + assert!(!glob_matches("ns::fn::v2", "ns::fn::v3")); } #[test] @@ -491,7 +491,7 @@ mod tests { fn test_policy_config_large_rule_set() { let rules: Vec = (0..1000) .map(|i| PolicyRule { - pattern: format!("tool::category{}::*", i), + pattern: format!("fn::category{}::*", i), action: if i % 2 == 0 { PolicyAction::Allow } else { PolicyAction::Deny }, scope: PolicyScope::Agent, }) diff --git a/workers/session-lifecycle/src/main.rs b/workers/session-lifecycle/src/main.rs index 36a290d..0454b41 100644 --- a/workers/session-lifecycle/src/main.rs +++ b/workers/session-lifecycle/src/main.rs @@ -179,7 +179,7 @@ async fn transition(iii: &III, input: Value) -> Result { }); fire_void( iii, - "tool::agent_send", + "fn::agent_send", json!({ "targetAgentId": agent_id, "message": message }), ); } diff --git a/workers/task-decomposer/src/main.rs b/workers/task-decomposer/src/main.rs index 1ac54aa..f6966f2 100644 --- a/workers/task-decomposer/src/main.rs +++ b/workers/task-decomposer/src/main.rs @@ -445,7 +445,7 @@ async fn spawn_workers(iii: &III, input: Value) -> Result { tokio::spawn(async move { let _ = iii_clone .trigger(TriggerRequest { - function_id: "tool::agent_spawn".to_string(), + function_id: "fn::agent_spawn".to_string(), payload, action: None, timeout_ms: None, diff --git a/workers/wasm-sandbox/src/main.rs b/workers/wasm-sandbox/src/main.rs index a9a1fa5..e2dd6d5 100644 --- a/workers/wasm-sandbox/src/main.rs +++ b/workers/wasm-sandbox/src/main.rs @@ -281,9 +281,6 @@ async fn execute_wasm(iii: &III, cache: &ModuleCache, input: Value) -> Result Date: Wed, 6 May 2026 16:31:10 +0100 Subject: [PATCH 2/2] fix: address review findings on chore/scrub-tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verified each finding against current code. Applied four; documented why the others were skipped. Applied agents/*/agent.toml — 44 generalist configs went from functions = ["fn::*"] to functions = ["*"] The starts_with("fn::") prefix filter (agent-core/main.rs:421) silently blocked these agents from calling memory::*, browser::*, lsp::*, etc. The original pre-rename intent was "any function" (was tool::*); the wildcard "*" matches the engine::functions::list short-circuit on line 407 and restores that. workers/evolve/main.rs:197 — dropped the unused mut binding and no-op `let _ = &mut metadata;`. metadata isn't mutated anywhere in evolve_generate; the comment about system-managed-field protection belonged on EvolvedFunction (where it already lives). workers/security/tool_policy.rs:705 — description "Check if a tool invocation is allowed by policy" was stale; renamed to "Check if a function call is allowed by policy" to match the scrub. workers/wasm-sandbox/main.rs — hoisted WASM_ALLOWED_FUNCTIONS to module scope. The const was redeclared inline 8 times (host_call closure + 7 tests), so tests validated copies instead of the production constant. Tests now reference the single definition via `use super::*`. Skipped (with reason) agent-core types serde alias for legacy "tools" key — repo policy is pre-1.0, no backwards-compatibility shims (memory rule: "Don't use feature flags or backwards-compatibility shims when you can just change the code"). hand-runner relax deny_unknown_fields — same; legacy [hand.tools] files in the repo are already migrated in this PR, no external callers exist. approval-tiers fall back to legacy tool::* override keys — same. hooks worker keep "toolIds"/"toolId" JSON keys — these are part of a cross-worker schema (hashline emits, hooks filters, approval-tiers consumes, session-replay records, TUI reads). Coordinated rename is a follow-up RFC, not a one-PR change. No new comment added; the keys' rename is tracked outside this scrub. hashline metric label "toolId" — same shared schema; left as-is. approval-tiers fn:: classification nitpick — current default Async is correct for unknown agent helpers (least surprise; sync would block any unknown call). browser about:blank SSRF rejection — pre-existing bug, predates this PR; addressing it requires a session.navigated flag + handler refactor across click/type/screenshot/read_page. Tracked separately. performance-benchmarker / hands/lead/HAND.toml unimplemented fn ids — these are agent allow-lists (capability checks), not registrations. Allow-list entries that match no registered function id are harmless: agent::list_functions returns the intersection, so unimplemented ids never appear in dispatch. Forward-looking permissions, not runtime errors. All 1281 tests pass. --- agents/ai-engineer/agent.toml | 2 +- agents/analyst/agent.toml | 2 +- agents/app-store-optimizer/agent.toml | 2 +- agents/architect/agent.toml | 2 +- agents/assistant/agent.toml | 2 +- agents/brand-guardian/agent.toml | 2 +- agents/code-reviewer/agent.toml | 2 +- agents/coder/agent.toml | 2 +- agents/content-creator/agent.toml | 2 +- agents/customer-support/agent.toml | 2 +- agents/data-scientist/agent.toml | 2 +- agents/debugger/agent.toml | 2 +- agents/devops-lead/agent.toml | 2 +- agents/doc-writer/agent.toml | 2 +- agents/email-assistant/agent.toml | 2 +- agents/evidence-collector/agent.toml | 2 +- agents/feedback-synthesizer/agent.toml | 2 +- agents/growth-hacker/agent.toml | 2 +- agents/health-tracker/agent.toml | 2 +- agents/hello-world/agent.toml | 2 +- agents/home-automation/agent.toml | 2 +- agents/image-prompt-engineer/agent.toml | 2 +- agents/legal-assistant/agent.toml | 2 +- agents/meeting-assistant/agent.toml | 2 +- agents/mobile-builder/agent.toml | 2 +- agents/ops/agent.toml | 2 +- agents/orchestrator/agent.toml | 2 +- agents/personal-finance/agent.toml | 2 +- agents/planner/agent.toml | 2 +- agents/rapid-prototyper/agent.toml | 2 +- agents/reality-checker/agent.toml | 2 +- agents/recruiter/agent.toml | 2 +- agents/researcher/agent.toml | 2 +- agents/sales-assistant/agent.toml | 2 +- agents/security-auditor/agent.toml | 2 +- agents/social-media/agent.toml | 2 +- agents/sprint-prioritizer/agent.toml | 2 +- agents/test-engineer/agent.toml | 2 +- agents/translator/agent.toml | 2 +- agents/travel-planner/agent.toml | 2 +- agents/trend-researcher/agent.toml | 2 +- agents/tutor/agent.toml | 2 +- agents/ux-architect/agent.toml | 2 +- agents/writer/agent.toml | 2 +- workers/evolve/src/main.rs | 4 +- workers/security/src/tool_policy.rs | 2 +- workers/wasm-sandbox/src/main.rs | 50 ++++--------------------- 47 files changed, 53 insertions(+), 91 deletions(-) diff --git a/agents/ai-engineer/agent.toml b/agents/ai-engineer/agent.toml index fdeb246..1ac090b 100644 --- a/agents/ai-engineer/agent.toml +++ b/agents/ai-engineer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/analyst/agent.toml b/agents/analyst/agent.toml index 520717a..9861e76 100644 --- a/agents/analyst/agent.toml +++ b/agents/analyst/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/app-store-optimizer/agent.toml b/agents/app-store-optimizer/agent.toml index 4c23c31..5874bd4 100644 --- a/agents/app-store-optimizer/agent.toml +++ b/agents/app-store-optimizer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/architect/agent.toml b/agents/architect/agent.toml index fbe1c2a..02fcad3 100644 --- a/agents/architect/agent.toml +++ b/agents/architect/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/assistant/agent.toml b/agents/assistant/agent.toml index 800cb9d..17ede56 100644 --- a/agents/assistant/agent.toml +++ b/agents/assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/brand-guardian/agent.toml b/agents/brand-guardian/agent.toml index 6e124da..36ce3c3 100644 --- a/agents/brand-guardian/agent.toml +++ b/agents/brand-guardian/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/code-reviewer/agent.toml b/agents/code-reviewer/agent.toml index 03ae3fa..3a91a7f 100644 --- a/agents/code-reviewer/agent.toml +++ b/agents/code-reviewer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/coder/agent.toml b/agents/coder/agent.toml index 067d0d5..b7a1c80 100644 --- a/agents/coder/agent.toml +++ b/agents/coder/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/content-creator/agent.toml b/agents/content-creator/agent.toml index 1028558..3342918 100644 --- a/agents/content-creator/agent.toml +++ b/agents/content-creator/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/customer-support/agent.toml b/agents/customer-support/agent.toml index 64fbacf..0666ec5 100644 --- a/agents/customer-support/agent.toml +++ b/agents/customer-support/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/data-scientist/agent.toml b/agents/data-scientist/agent.toml index b2a5b4d..b441bcc 100644 --- a/agents/data-scientist/agent.toml +++ b/agents/data-scientist/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/debugger/agent.toml b/agents/debugger/agent.toml index be6a8e8..37ea84b 100644 --- a/agents/debugger/agent.toml +++ b/agents/debugger/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/devops-lead/agent.toml b/agents/devops-lead/agent.toml index e423964..3b2ec60 100644 --- a/agents/devops-lead/agent.toml +++ b/agents/devops-lead/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/doc-writer/agent.toml b/agents/doc-writer/agent.toml index 233e882..c3df051 100644 --- a/agents/doc-writer/agent.toml +++ b/agents/doc-writer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/email-assistant/agent.toml b/agents/email-assistant/agent.toml index 435c10d..b809e74 100644 --- a/agents/email-assistant/agent.toml +++ b/agents/email-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/evidence-collector/agent.toml b/agents/evidence-collector/agent.toml index a72ff8d..a4a963d 100644 --- a/agents/evidence-collector/agent.toml +++ b/agents/evidence-collector/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/feedback-synthesizer/agent.toml b/agents/feedback-synthesizer/agent.toml index 8d76607..d0dfc25 100644 --- a/agents/feedback-synthesizer/agent.toml +++ b/agents/feedback-synthesizer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/growth-hacker/agent.toml b/agents/growth-hacker/agent.toml index f7dfc37..32a199b 100644 --- a/agents/growth-hacker/agent.toml +++ b/agents/growth-hacker/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/health-tracker/agent.toml b/agents/health-tracker/agent.toml index f45b48c..2dc25b6 100644 --- a/agents/health-tracker/agent.toml +++ b/agents/health-tracker/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/hello-world/agent.toml b/agents/hello-world/agent.toml index 6735ba6..27d15f6 100644 --- a/agents/hello-world/agent.toml +++ b/agents/hello-world/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/home-automation/agent.toml b/agents/home-automation/agent.toml index c6de1d6..6c4e8ff 100644 --- a/agents/home-automation/agent.toml +++ b/agents/home-automation/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/image-prompt-engineer/agent.toml b/agents/image-prompt-engineer/agent.toml index 0e0193a..67bff9f 100644 --- a/agents/image-prompt-engineer/agent.toml +++ b/agents/image-prompt-engineer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/legal-assistant/agent.toml b/agents/legal-assistant/agent.toml index 7e39dce..46e84f2 100644 --- a/agents/legal-assistant/agent.toml +++ b/agents/legal-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/meeting-assistant/agent.toml b/agents/meeting-assistant/agent.toml index 3c5d1ae..6f687a4 100644 --- a/agents/meeting-assistant/agent.toml +++ b/agents/meeting-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/mobile-builder/agent.toml b/agents/mobile-builder/agent.toml index aa1984d..511a64c 100644 --- a/agents/mobile-builder/agent.toml +++ b/agents/mobile-builder/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/ops/agent.toml b/agents/ops/agent.toml index 18bcafc..6f81c6c 100644 --- a/agents/ops/agent.toml +++ b/agents/ops/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/orchestrator/agent.toml b/agents/orchestrator/agent.toml index cb745b4..cadd434 100644 --- a/agents/orchestrator/agent.toml +++ b/agents/orchestrator/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/personal-finance/agent.toml b/agents/personal-finance/agent.toml index 958e4d6..a06dd69 100644 --- a/agents/personal-finance/agent.toml +++ b/agents/personal-finance/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/planner/agent.toml b/agents/planner/agent.toml index 8d990fc..ab1a4c2 100644 --- a/agents/planner/agent.toml +++ b/agents/planner/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/rapid-prototyper/agent.toml b/agents/rapid-prototyper/agent.toml index da1d020..34a7fc6 100644 --- a/agents/rapid-prototyper/agent.toml +++ b/agents/rapid-prototyper/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/reality-checker/agent.toml b/agents/reality-checker/agent.toml index a71544e..75ecb13 100644 --- a/agents/reality-checker/agent.toml +++ b/agents/reality-checker/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/recruiter/agent.toml b/agents/recruiter/agent.toml index 039649d..3eba921 100644 --- a/agents/recruiter/agent.toml +++ b/agents/recruiter/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/researcher/agent.toml b/agents/researcher/agent.toml index 307f23c..bcc1ade 100644 --- a/agents/researcher/agent.toml +++ b/agents/researcher/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/sales-assistant/agent.toml b/agents/sales-assistant/agent.toml index 0fe603f..3e0570a 100644 --- a/agents/sales-assistant/agent.toml +++ b/agents/sales-assistant/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/security-auditor/agent.toml b/agents/security-auditor/agent.toml index 23283cb..d1fc0a6 100644 --- a/agents/security-auditor/agent.toml +++ b/agents/security-auditor/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/social-media/agent.toml b/agents/social-media/agent.toml index f8d3d58..e4ed1c8 100644 --- a/agents/social-media/agent.toml +++ b/agents/social-media/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/sprint-prioritizer/agent.toml b/agents/sprint-prioritizer/agent.toml index 1288a14..3a3073b 100644 --- a/agents/sprint-prioritizer/agent.toml +++ b/agents/sprint-prioritizer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/test-engineer/agent.toml b/agents/test-engineer/agent.toml index 5082332..4fcd86f 100644 --- a/agents/test-engineer/agent.toml +++ b/agents/test-engineer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/translator/agent.toml b/agents/translator/agent.toml index 7064f05..b3fdb29 100644 --- a/agents/translator/agent.toml +++ b/agents/translator/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/travel-planner/agent.toml b/agents/travel-planner/agent.toml index f131dc0..2b38b37 100644 --- a/agents/travel-planner/agent.toml +++ b/agents/travel-planner/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/trend-researcher/agent.toml b/agents/trend-researcher/agent.toml index 0fc4d9e..825f231 100644 --- a/agents/trend-researcher/agent.toml +++ b/agents/trend-researcher/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/tutor/agent.toml b/agents/tutor/agent.toml index dde57b5..9135e44 100644 --- a/agents/tutor/agent.toml +++ b/agents/tutor/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/ux-architect/agent.toml b/agents/ux-architect/agent.toml index c4e7fb3..dcb8a13 100644 --- a/agents/ux-architect/agent.toml +++ b/agents/ux-architect/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/agents/writer/agent.toml b/agents/writer/agent.toml index a7dbebb..331b303 100644 --- a/agents/writer/agent.toml +++ b/agents/writer/agent.toml @@ -9,7 +9,7 @@ model = "claude-sonnet-4-6" max_tokens = 4096 [agent.capabilities] -functions = ["fn::*"] +functions = ["*"] memory_scopes = ["self.*", "shared.*"] network_hosts = ["*"] diff --git a/workers/evolve/src/main.rs b/workers/evolve/src/main.rs index 94e9742..8847250 100644 --- a/workers/evolve/src/main.rs +++ b/workers/evolve/src/main.rs @@ -193,12 +193,10 @@ async fn evolve_generate(iii: &III, input: Value) -> Result { None }; - // CR fix: spread extra_meta first so the system-managed fields cannot be overridden. - let mut metadata = match extra_meta { + let metadata = match extra_meta { Value::Object(map) => Value::Object(map), _ => json!({}), }; - let _ = &mut metadata; let now = now_ms(); let record = EvolvedFunction { diff --git a/workers/security/src/tool_policy.rs b/workers/security/src/tool_policy.rs index 9e530ef..0006f81 100644 --- a/workers/security/src/tool_policy.rs +++ b/workers/security/src/tool_policy.rs @@ -702,7 +702,7 @@ pub fn register(iii: &III) { })) } }) - .description("Check if a tool invocation is allowed by policy"), + .description("Check if a function call is allowed by policy"), ); let iii_ref = iii.clone(); diff --git a/workers/wasm-sandbox/src/main.rs b/workers/wasm-sandbox/src/main.rs index e2dd6d5..9556f7c 100644 --- a/workers/wasm-sandbox/src/main.rs +++ b/workers/wasm-sandbox/src/main.rs @@ -14,6 +14,13 @@ const DEFAULT_FUEL: u64 = 1_000_000; const DEFAULT_TIMEOUT_SECS: u64 = 30; const DEFAULT_MEMORY_PAGES: u64 = 256; +const WASM_ALLOWED_FUNCTIONS: &[&str] = &[ + "memory::recall", + "memory::store", + "security::scan_injection", + "embedding::generate", +]; + #[derive(Debug)] enum ExecutionError { Timeout, @@ -278,13 +285,6 @@ async fn execute_wasm(iii: &III, cache: &ModuleCache, input: Value) -> Result