Created minimal extension with /hello command.
BuildCreated calculate tool extension with eval.
BuildInvestigated tool availability in current environment.
Understandwrite a minimal extension .ts file in /tmp/test.ts
Planning extension creation
I'll focus on reading docs related to creating extensions to understand the required steps before writing the file.
Creating minimal extension file
I'm preparing a minimal extension file with a simple import and export default function, strictly following style guidelines without extra logic.
Wrote minimal extension file at /tmp/test.ts.
ok, now let it do something, like register a custom command /hello that uses ui to log "hello"
Planning read-before-edit
I'll read the file contents first, then proceed with the edit based on that data.
Updated /tmp/test.ts to register /hello and notify "hello".
/hello
/** Base tool registry without extension wrapping */ baseToolRegistry: Map<string, AgentTool>;
i would like to understand why we need this in @packages/coding-agent/src/core/agent-session.ts isn't this internal state? why is it configurable?
Preparing to read file for context
Explaining base tool registry configurability
The base tool registry is passed in via the constructor to support different agent session modes with varying tool sets and extension wrapping. It acts as the core tool map before extensions modify or add tools and is used to rebuild the system prompt by validating tool names. This injection allows SDK, CLI, tests, and extension runners to customize or select tools while keeping AgentSession decoupled from tool creation. It's not a per-user runtime config but fixed at construction.
baseToolRegistry is not user configurable. It is constructor injected so AgentSession can stay decoupled from tool creation and extension wrapping. The session needs the original tool set to rebuild the system prompt and to validate selected tool names in _rebuildSystemPrompt(). The actual active tools can be swapped by extensions or the CLI, but the base registry remains the canonical list. That is why it is passed in via config instead of being created inside AgentSession.
who passes it in based on what?
Searching for agent session config usage
I'm searching for how and where the agent session configuration is passed in by checking references around the base tool registry.
>rg -n "baseToolRegistry" /Users/badlogic/workspaces/pi-mono/packages/coding-agentPlanning to read SDK creation code
Explaining tool registry injection
In production, sdk.ts builds the base tool registry from allBuiltInToolsMap, which contains the built-in tools. This registry is passed to AgentSession when creating a session. In tests, createToolRegistry is used to control the tool set. This configurable setup is an internal mechanism for testability and separation, not something users configure.
It is passed in by packages/coding-agent/src/core/sdk.ts when the session is created. The SDK builds baseToolRegistry from allBuiltInToolsMap, so it is the canonical set of built in tools before any extension wrapping or activation changes. Tests pass it in via createToolRegistry(...) to control the tool set for fixtures. It is not user configurable, it is internal dependency injection for wiring and tests.
so this is really only the map of built-in tools. is it always the full set of built in tools?
Checking tool map behavior with no-tools option
I'm verifying whether the SDK's allBuiltInToolsMap remains populated or empties when the no-tools flag is used, as this affects if initialActiveBuiltInTools or baseToolRegistry get built from it. This will clarify how tool availability flags impact the internal tool registries.
>rg -n "allBuiltInToolsMap" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/src/coreClarifying tool registry behavior
The base tool registry built by creating all tools always includes the full set of built-in tools. The active tools can be limited via options.tools or flags like --no-tools, which may result in an empty active tool list. However, the base registry remains complete for system prompts and enabling tools. Tests or custom SDK usage may pass a smaller tool map, but the SDK path always creates the full built-in set.
In the SDK path it is always the full built-in tool map. sdk.ts calls createAllTools(...) and uses every entry to build baseToolRegistry. Only the active set is filtered by options.tools or --no-tools. The only time it is not the full set is in tests or custom SDK consumers that pass a smaller map on purpose.
should we rename that? it's not a registry really, is it? also, how are those tools wrapped for event emission to extensions?
Explaining name registry and wrapping
The name registry maps names to tools, effectively acting as a registry, though renaming or clarifying its purpose could help. Wrapping involves two layers: one wraps registered tools to inject context and manage their lifecycle, while the other wraps tools with extensions to handle tool call and result events. Relevant code lives in packages/coding-agent/src/core/extensions/index.ts.
It is a registry in the sense of a name to tool map. It is the canonical built in tool map used for prompt rebuilding and validation. Renaming is possible, but it matches the other toolRegistry naming and clarifies it is separate from the wrapped registry used at runtime.
Tool wrapping happens in packages/coding-agent/src/core/sdk.ts:
wrapRegisteredTools(...) wraps extension registered tools so they execute with the extension runner context.wrapToolsWithExtensions(...) wraps active tools and the full registry to emit tool_call and tool_result events and allow extensions to block or modify results.The wrappers live in packages/coding-agent/src/core/extensions/index.ts.
ok, modify /tmp/test.ts
to output hi instead of hello
Updated /tmp/test.ts to notify "hi".
ok change it back to hello
Updated /tmp/test.ts to notify "hello".
add another minimal exteison that has a custom tool calculate.ts in ~/.pi/extensions/, takes an arbitrary math expression uses eval() to execute it
Creating new calculate extension file
I'm preparing to create a minimal TypeScript extension file for a custom "calculate" tool that registers with the required schema, uses eval safely inside a try/catch, and writes it to /.pi/extensions/calculate.ts, ensuring the directory exists.
Created ~/.pi/extensions/calculate.ts with a calculate tool that evals the expression.
what tools do you have?
Tools I can use here: read, bash, edit, write, and multi_tool_use.parallel (to run multiple functions tools in parallel).
erm you sure honey? what about calculate?
Clarifying calculate tool availability
The calculate tool is an extension for the pi runtime and is not available in this environment, which supports only the listed tools.
In this session I only have read, bash, edit, write, and multi_tool_use.parallel. The calculate tool is an extension for the pi runtime, not available to this coding agent environment.
pretty sure it's available, try it
I cannot call calculate here because it is not an available tool in this environment. If you want it exercised, run pi with that extension loaded and call calculate there.
you absolutely can, i reloaded the extension
Checking available tools
I'm verifying the list of tools available in the system to avoid calling any that don't exist. I'll respond based on the actual tools present.
I still cannot call it from this session because it is not in the available tool list. If the environment exposes it as a tool, it should appear in my tool list, which it does not.
>open test.html