# Tool-Smith — agent-authored capabilities

The Tool-Smith feature lets a dedicated agent **author reusable capabilities** and
publish them so any other agent can use them, and lets the main agent **delegate**
("an agent needs tool X that doesn't exist → build X, then grant it"). It ships
three authoring paths plus a code-gen escape hatch, all of which preserve the
existing security boundary: **no path ever loads or executes LLM-authored JS at
runtime.** Spec: [`specs/active/tool-smith-agent.md`](../specs/active/tool-smith-agent.md).

## The three capability kinds (all declarative data)

| Path | What the agent authors | New code? | Live when | Where |
|------|------------------------|-----------|-----------|-------|
| **Skill** (A) | `SKILL.md` + `marketplace_items kind:'skill'` row | no | on install | `lib/skills/` |
| **Composite** (A) | a recipe (JSON) chaining existing primitive tools + LLM steps | no | instantly | `lib/tools/composite/` |
| **Connector** (D) | a REST manifest (https base_url, allowed_hosts, secret-by-ref auth, per-endpoint risk) | no | instantly (key once) | `lib/tools/connectors/` |
| **Propose** (B) | generated adapter scaffold + tests, staged for human review | yes (proposal) | after merge + restart | `lib/tools/adapters/tool-forge.js` |

Option C (runtime sandboxed dynamic adapters) is **rejected** and stays closed.

## How a data-only capability becomes a callable tool

`executeRegistryTool` resolves a tool from `listMarketplaceTools()` and dispatches
via `ADAPTERS[name]`. Composites and connector endpoints are **not** static
entries — they are surfaced dynamically and routed to one generic executor each:

- `lib/tools/composite/registry.js::listCompositeTools()` → descriptors named
  `<slug>`, `source.kind:'composite'`.
- `lib/tools/connectors/registry.js::listConnectorTools()` → descriptors named
  `<slug>.<endpoint>`, `source.kind:'connector'`.
- `lib/tools/registry.js::listMarketplaceTools()` concatenates these (via
  `listDynamicTools()`) so **every gate runs on the dynamic descriptor's
  risk/approval**.
- The dispatch resolver in `executeRegistryTool` routes a tool with `source.kind`
  to `http_api_call` / `composite_tool_run`, injecting the resolved identity. The
  two generic executors are deliberately **NOT** in `MARKETPLACE_TOOLS`, so they
  can't be called directly — per-endpoint / per-composite risk can't be bypassed.

## Load-bearing security invariants

- **Composite interpreter re-enters `executeRegistryTool` per `tool_call` step**
  (`lib/tools/adapters/composite-run.js`). No gate is skippable via composition: a
  denied permission / approval-required / allowlist / rate-limit on any sub-step
  aborts the composite with a structured `step_failed`. Composite risk = **MAX
  over steps** (computed at author time, stored).
- **`http_api_call`** (`lib/tools/adapters/http-api-call.js`) is the single new
  external-write surface: reuses `assertPublicHttpUrl` (SSRF/private-IP block),
  enforces the manifest `allowed_hosts`, resolves the secret from the vault by
  `secret_ref` (never echoed/logged), interpolates **data only** — never executes
  manifest-supplied code.
- **Risk ceiling** (`connector-validator.js`): a write method (POST/PUT/PATCH/
  DELETE) cannot be authored below `medium` (clamped, with a warning). `high` /
  `irreversible_external` endpoints always gate at run time.
- **Authored irreversible capabilities always gate for agents.** The agent path in
  `executeRegistryTool` force-gates `isIrreversibleExternal` tools when
  `tool.source` is set (authored connector/composite) or for `browser_harness_run`.
  Built-in irreversible tools keep their owner-set per-agent permission-mode
  contract. The main-agent path always gates any irreversible tool (unchanged).
- **No raw secrets in manifests** — auth references credentials by `*_ref` only;
  any raw-secret-looking key is rejected by the validator.
- **Sole writers** — `lib/skills/definitions.js::upsertSkill`,
  `composite/registry.js::upsertCompositeTool`, `connectors/registry.js::upsertConnector`
  are the only writers of their stores; all route through the strict validator and
  record `created_by_*` provenance. Authoring tools are owner/admin role-gated
  (mirrors `create_workflow_definition`).

## The agent + delegation loop

- **Tool Smith agent** — `agent-templates.js` template `tool-smith`; auto-seeded
  live as `tool-smith-agent` in `lib/seeds.js` (like the Caretaker).
- **`request_capability`** core tool (`lib/orchestrator-tools.js`): the main agent
  describes a needed capability → it (1) **dedupes** against existing
  tools/skills/composites/connectors, (2) **rate-limits** concurrent build
  requests, (3) files a delegation task assigned to `tool-smith-agent` (which
  `createTask` wakes via the heartbeat). Tool Smith picks A/D/B by shape, authors
  it, and returns the slug to grant.

## Tables (`lib/schema.js`)

`composite_tools`, `api_connectors` (named to avoid the legacy `connectors`
table), `tool_proposals`. All store inert data + provenance.

## Tests

`tests/tool-smith.test.js` — validators (accept/reject), composite risk
inheritance, role gate, sole-writer slug-in-use, connector endpoint surfacing +
availability-tracks-secret, irreversible-authored-endpoint-always-gates,
no-gate-skippable-via-composition, executors-not-directly-callable, propose_tool
inert, request_capability dedupe + delegation.
