// Build a new builtin tool package under `packages/builtin-tool-<name>/`. Use when adding a new agent-callable toolset, designing its API surface (manifest / ApiName / Params / State), implementing the Executor + ExecutionRuntime, building the Inspector / Render / Placeholder / Streaming / Intervention / Portal UI, or wiring a tool into the central registries (`packages/builtin-tools/src/{index,identifiers,inspectors,renders,placeholders,streamings,interventions,portals}.ts` and `src/store/tool/slices/builtin/executors/index.ts`). Triggers on "new builtin tool", "add a tool", "tool inspector", "tool render", "tool placeholder", "tool streaming", "tool intervention", "BuiltinToolManifest", "BaseExecutor", "ExecutionRuntime".
Build a new builtin tool package under `packages/builtin-tool-<name>/`. Use when adding a new agent-callable toolset, designing its API surface (manifest / ApiName / Params / State), implementing the Executor + ExecutionRuntime, building the Inspector / Render / Placeholder / Streaming / Intervention / Portal UI, or wiring a tool into the central registries (`packages/builtin-tools/src/{index,identifiers,inspectors,renders,placeholders,streamings,interventions,portals}.ts` and `src/store/tool/slices/builtin/executors/index.ts`). Triggers on "new builtin tool", "add a tool", "tool inspector", "tool render", "tool placeholder", "tool streaming", "tool intervention", "BuiltinToolManifest", "BaseExecutor", "ExecutionRuntime".
Builtin Tool Authoring Guide
A builtin tool is a package the agent runtime can call. It ships five faces:
Creating a new packages/builtin-tool-<name>/ package
Adding a new API method to an existing builtin tool
Building or restyling any of the 6 client surfaces for a tool
Wiring a tool into the central registries
Debugging "tool not found / API not found / render not showing / placeholder stuck" errors
Top-Level Design Principles
lobe-<domain> identifier is permanent. It's stored in message history. Renames need @deprecated aliases (see packages/builtin-tools/src/inspectors.ts:88-89). Get it right the first time.
ApiName is an as const object, not a TS enum. It doubles as the runtime list BaseExecutor iterates over.
Three result fields, three audiences:
content: string → the LLM reads it
state: Record<…> → the UI's pluginState; result-domain only, never echo all params back
error: { type, message, body? } → both LLM and UI; type is a stable code
Split execution from frontend wiring.
src/ExecutionRuntime/ — pure runtime, no React, no Zustand, accepts services via constructor. The default place for new logic.
src/client/executor/ — BaseExecutor subclass that calls ExecutionRuntime (or stores/services directly when frontend-only).
UI defaults to "do nothing". Inspector is required (the header strip). Render/Placeholder/Streaming/Intervention/Portal are added only when there's something specific to show — empty registries are fine.
Style with createStaticStyles + cssVar.* (zero-runtime). Fall back to createStyles + token only when you genuinely need runtime values. Use @lobehub/ui components, not raw antd.
i18n keys live in src/locales/default/plugin.ts. Inspector titles must come from t('builtins.<identifier>.apiName.<api>') so something renders while args stream.
Package Layout (preferred, post-2026 convention)
packages/builtin-tool-<name>/
├── package.json
└── src/
├── index.ts # exports manifest + types + systemRole + Identifier (no React, no stores)
├── manifest.ts # BuiltinToolManifest with JSON Schema for every API
├── types.ts # ApiName const + Params/State interfaces per API
├── systemRole.ts # System prompt teaching the model when/how to use the APIs
├── ExecutionRuntime/ # ✅ Default home for runtime logic (server- or anywhere-callable)
│ └── index.ts
└── client/
├── index.ts # Re-exports for the registries
├── executor/ # ✅ Frontend executor — extends BaseExecutor, often delegates to ExecutionRuntime
│ └── index.ts
├── Inspector/ # required — header chip per API
├── Render/ # optional — rich result card
├── Placeholder/ # optional — skeleton during streaming/execution
├── Streaming/ # optional — live output renderer (e.g. RunCommand, WriteFile)
├── Intervention/ # optional — approval / edit-before-run UI
├── Portal/ # optional — full-screen detail view
└── components/ # shared subcomponents used by the surfaces above
Older packages (builtin-tool-task, builtin-tool-calculator, etc.) still have src/executor/ as a sibling of src/client/. That's grandfathered; don't relocate without a deliberate refactor. New packages and new APIs added to existing packages should follow the layout above.