4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
77 lines
4.2 KiB
Markdown
77 lines
4.2 KiB
Markdown
# Tool placement policy (T-20)
|
|
|
|
Where a piece of agent-callable capability lives is decided by **how many
|
|
vendor integrations its domain logic touches**, not by convenience or file
|
|
size. This is the decision rule referenced from
|
|
[ARCHITECTURE.md](ARCHITECTURE.md#tier-2--tools-and-integrations).
|
|
|
|
## Decision rule
|
|
|
|
Ask: does this tool's *purpose* depend on a specific external vendor or
|
|
SaaS backend (an `integrations/<vendor>/` package)?
|
|
|
|
1. **Single vendor** → `integrations/<vendor>/tools/`.
|
|
The tool only makes sense in terms of one vendor's domain (a Datadog
|
|
query, a GitHub mutation, a Sentry issue lookup). This is the default,
|
|
most common case — see "Adding a Tool" in `AGENTS.md`.
|
|
2. **No vendor at all** → `tools/system/`.
|
|
The tool's domain purpose has nothing to do with an external vendor —
|
|
local process introspection, sandboxed code execution, RAG-style
|
|
guidance retrieval. An *incidental* import of a vendor client for a
|
|
side concern (e.g. resolving one of several possible credential
|
|
sources, or picking a notification channel for delivery) does not
|
|
make a tool vendor-specific; the test is the tool's reason to exist,
|
|
not every import in the file.
|
|
3. **Genuinely spans 2+ vendor integrations** → `tools/cross_vendor/`.
|
|
The tool's logic itself correlates or orchestrates across multiple
|
|
`integrations/<vendor>/` packages — e.g. `fix_sentry_issue` reads from
|
|
`integrations.sentry` and hands the fix to `integrations.pi`. This is
|
|
the narrow bucket; don't reach for it just because a tool happens to
|
|
format its output for a second vendor (e.g. "Slack-ready" report text
|
|
is still single-vendor logic, not cross-vendor).
|
|
4. **True surface-level (CLI + REPL) duplication, not tool logic at all**
|
|
→ `surfaces/shared/` (see T-21). This is a different axis entirely —
|
|
it's about presentation code two *surfaces* both need, not about where
|
|
an agent-callable tool's business logic lives.
|
|
|
|
`tools/` also holds framework subsystems that aren't individual tools —
|
|
`tools/investigation` (the investigation pipeline), `tools/interactive_shell`
|
|
(REPL action tools), `tools/registry.py` (the tool registry itself). These
|
|
stay at the top level of `tools/`; the `system` / `cross_vendor` split only
|
|
applies to individual tool packages.
|
|
|
|
## Current state (as of T-19)
|
|
|
|
Applied to the pre-existing top-level `tools/` packages:
|
|
|
|
| Package | Placement | Why |
|
|
| --- | --- | --- |
|
|
| `tools/system/fleet_monitoring/` | system | Local AI-agent fleet monitoring; no vendor. |
|
|
| `tools/system/python_execution_tool/` | system | Generic sandboxed Python execution; the GitHub token import is one of several optional credential sources, not the tool's purpose. |
|
|
| `tools/system/sre_guidance_tool/` | system | Local knowledge-base retrieval; no vendor. |
|
|
| `tools/system/watch_dog/` | system | CLI/REPL process monitoring; Telegram is only the alarm-delivery channel, not the tool's domain. |
|
|
| `tools/cross_vendor/fix_sentry_issue/` | cross_vendor | Reads a Sentry issue and hands the fix to the Pi coding agent — two `integrations/` packages in one tool's logic. |
|
|
|
|
**Left as-is, not yet migrated** (single-vendor tools that predate the
|
|
vendor-first tool layout and belong under `integrations/<vendor>/tools/`,
|
|
not `tools/system/` or `tools/cross_vendor/`):
|
|
|
|
- `tools/community_followup_tool/`, `tools/git_deploy_timeline_tool/`,
|
|
`tools/work_status_report_tool/` — GitHub-only.
|
|
- `tools/slack_send_message_tool/` — Slack-only.
|
|
- `tools/pi_coding_tool/` — Pi-only.
|
|
|
|
Migrating these to `integrations/<vendor>/tools/` is in scope for T-18
|
|
(full vendor-first completion), not this policy change — moving them into
|
|
`tools/system/` or `tools/cross_vendor/` now would misclassify them.
|
|
|
|
## Registry mechanics
|
|
|
|
`tools/system/` and `tools/cross_vendor/` are ordinary packages discovered
|
|
by `tools/registry.py`'s top-level walk of `tools/`. Each declares a
|
|
`TOOL_MODULES` tuple in its `__init__.py` (the same manifest mechanism
|
|
`tools/slack_send_message_tool/__init__.py` already uses for its own
|
|
`tool` submodule) listing the tool packages nested one level inside it.
|
|
Adding a new system or cross-vendor tool means adding its package name to
|
|
the relevant `TOOL_MODULES` tuple — no registry code changes required.
|