Tool Calling Overview
Tool calling is how a Pedelec agent asks the web application for information or requests a controlled frontend action.
A tool is appropriate when the agent needs something that text alone cannot provide, such as:
- reading the current page URL, selected text, active document, or editor state;
- changing a counter, canvas, scene, form, or application setting;
- asking the user for confirmation or additional input;
- exporting a document through an application-owned API; or
- querying frontend-only data that is not available to the provider process.
The basic model
Section titled “The basic model”The application declares a set of capabilities when creating a session:
const session = await pedelec.createSession({ provider: "codex", skills: { guidance: "Use get_current_page when you need page identity.", tools: [getCurrentPageTool], },});skills has two parts:
type SkillsInput<TTools extends readonly ToolDefinition[]> = { guidance: string; tools: TTools;};guidance
Section titled “guidance”High-level instructions that explain when or how the agent should use the available tools. Keep it operational and specific.
Good guidance:
Use get_selected_text before editing the user's selection. Never guess the selection.Ask for confirmation before destructive changes.Weak guidance:
Use tools when useful.A list of serializable tool definitions. Each definition includes a name, description, argument schema, optional timeout, and optionally a browser-side inline handler.
End-to-end flow
Section titled “End-to-end flow”Web App declares skills and tools ↓Desktop Core validates the ToolRegistry and injects guidance plus a tool index into the first provider prompt ↓Agent decides to call a tool ↓Core emits tool_call and waits ↓SDK status becomes waiting_tool_result ↓Browser handler reads/changes frontend state ↓Handler returns a JSON-serializable result ↓SDK submits the result automatically ↓Agent continues the same turnThe application does not host instruction files. Core keeps the ToolRegistry as the runtime source of truth and injects skills.guidance plus tool names and descriptions into the first provider prompt. The initial prompt omits complete argument schemas; the agent uses pedelec-cli --thread-id <pedelec_thread_id> tool-spec <tool-name> when it needs one, then pedelec-cli --thread-id <pedelec_thread_id> tool-call <tool-name> '<json_args>' to execute. Core may still generate per-tool spec artifacts in the workspace, but the normal flow never requires reading tools.md.
Treat skills.guidance as product-facing operating guidance. Do not compose it from untrusted page content, user input, or tool results.
A complete small tool
Section titled “A complete small tool”import { defineTool } from "@kaoruisaac/pedelec";
const getCurrentPage = defineTool({ name: "get_current_page", description: "Read the title, URL, and selected text of the current browser page.", argsSchema: { type: "object", properties: {}, required: [], }, handler: (_args, ctx) => ({ title: document.title, url: location.href, selectedText: window.getSelection()?.toString() ?? "", observedForTurn: ctx.turnId, }),});
const session = await pedelec.createSession({ provider: "codex", skills: { guidance: "Call get_current_page instead of guessing page state.", tools: [getCurrentPage], },});The function in handler remains in the browser. Core receives the tool name, description, schema, and timeout—not executable frontend code.
Tool handlers run in page context
Section titled “Tool handlers run in page context”A handler can access normal page capabilities available to your application:
- DOM APIs;
- framework stores/signals/state;
- IndexedDB or local storage;
- canvas/editor APIs;
- authenticated application APIs; and
- modals and user interactions.
This power is why tools must be narrow. Do not expose a generic “execute JavaScript,” “fetch any URL,” or “change arbitrary state” tool when a small domain-specific tool will work.
Trust model
Section titled “Trust model”Agent-generated tool arguments are untrusted input. The schema helps the agent produce the expected shape, but the handler must still validate business rules and authorization.
session.onTool("delete_document", async (args) => { const { documentId } = args as { documentId?: unknown };
if (typeof documentId !== "string") { return { error: { code: "INVALID_DOCUMENT_ID", message: "documentId must be a string.", }, }; }
if (!canDeleteDocument(documentId)) { return { error: { code: "NOT_ALLOWED", message: "The current user cannot delete this document.", }, }; }
return deleteDocument(documentId);});A tool should never bypass the permissions your UI normally enforces.
Results
Section titled “Results”The handler return value is sent back to the agent automatically. Return plain JSON-compatible data:
return { ok: true, updatedCount: 4, affectedIds: ["a", "b"],};Avoid DOM nodes, functions, class instances with hidden state, BigInt, symbols, and cyclic objects.
Handler selection
Section titled “Handler selection”When a call arrives, the SDK uses this priority:
- a named handler registered with
session.onTool(name, handler); - the inline
handlerin the matchingdefineTool(); - the generic
session.onTool((tool, args, ctx) => ...)fallback; - an automatic
TOOL_HANDLER_NOT_FOUNDerror result.
This lets applications declare tools centrally but override behavior for a specific session or component.
Session status
Section titled “Session status”While Core waits for the result, session.getStatus() is waiting_tool_result. The session remains busy and cannot accept another sendText().
For an immediate tool this state may be brief. For an interactive tool it can remain visible while a modal waits for the user.
Tool calling is part of the same turn
Section titled “Tool calling is part of the same turn”A tool call does not create a new user turn. ctx.turnId remains associated with the original sendText() call. After the result, the provider continues reasoning and may call another tool before completing.
Do not assume one tool call per turn. Application code should support several sequential calls, while Core limits the currently pending request according to its runtime behavior.
Continue with Defining tools.