Comma Agents
@comma-agents/coreTools

defineTool

Create custom tools with schema validation and lifecycle hooks.

defineTool

defineTool creates a typed tool definition with full parameter inference from a Zod schema. The execute function receives validated arguments and a ToolContext, and must return a ToolResult.

defineTool

Creates a ToolDefinition with full type inference from the Zod schema.

import { z } from "zod";
import { defineTool, okResult, errorResult } from "@comma-agents/core";

const weatherTool = defineTool({
  description: "Get the current weather for a location",
  parameters: z.object({
    location: z.string().describe("City name"),
    unit: z.enum(["celsius", "fahrenheit"]).optional(),
  }),
  execute: async (validatedArguments, toolContext) => {
    const { location, unit } = validatedArguments;

    // Access agent context
    console.log(`Tool called by agent: ${toolContext.agentName}`);

    // Access the per-tool guard for authorization
    const abs = await toolContext.guard.authorize(
      { type: "fs.read", resource: "data/cache.json" },
      { agentName: toolContext.agentName, toolName: "weather", signal: toolContext.abort },
    );

    try {
      const weather = await fetchWeather(location, unit ?? "celsius");
      return okResult(weather.summary, {
        data: { temp: weather.temp, humidity: weather.humidity },
      });
    } catch (err) {
      return errorResult({
        kind: "unknown",
        message: `Weather fetch failed: ${err instanceof Error ? err.message : String(err)}`,
        recoverable: true,
        suggestedNextAction: "Retry with a different location name or check connectivity.",
      });
    }
  },
});

Guard System

Every tool receives a per-tool Guard on toolContext.guard. The guard is the runtime enforcement layer that authorizes every file-system access, command execution, and custom policy check. It is provided by the agent's sandbox — there is always one, defaulting to permissive if none is explicitly configured.

What the guard does

ResponsibilityDescription
Workspace jailRejects paths escaping the workspace root when jail: true.
Path policiesEach path access passes through ordered read/write deny/allow lists.
Command approvalGate dangerous commands behind deny/approve lists or interactive prompts.
Forbidden globsAlways-deny patterns for secrets, keys, and sensitive paths.

Authorizing access

Call guard.authorize() inside execute for any operation that touches the file system or shell:

const abs = await toolContext.guard.authorize(
  { type: "fs.read", resource: "data/cache.json" },
  {
    agentName: toolContext.agentName,
    toolName: "weather",
    signal: toolContext.abort,
  },
);

The guard returns the resolved absolute path. If access is denied, it throws a SandboxViolationError — the tool framework catches this and converts it to a structured ToolError with kind permission_denied.

Tool-level policies

Custom tools can declare their own policies via the policies option. These are appended to the guard's policy chain at tool construction time:

import { defineTool, denyCommandsPolicy } from "@comma-agents/core";

const tool = defineTool({
  description: "Run a user-supplied shell command",
  parameters: z.object({ command: z.string() }),
  policies: [
    denyCommandsPolicy(["rm -rf /", "sudo shutdown"]),
  ],
  execute: async ({ command }, ctx) => {
    await ctx.guard.authorize(
      { type: "command.execute", resource: command },
      { agentName: ctx.agentName, toolName: "my_tool", signal: ctx.abort },
    );
    // ...
  },
});

Built-in policy factories include denyCommandsPolicy, approveCommandsPolicy, forbiddenGlobsPolicy, and pathPolicy. You can also define custom Policy functions with the full "allow" / "deny" / "ask" / "pass" decision model.

For the full guard API — jail mode, path policies, the "ask" dispatch flow, session decisions, and interactive permission prompts — see the Sandbox documentation.

Guard System

Every tool receives a per-tool Guard on toolContext.guard. The guard is the runtime enforcement layer that authorizes every file-system access, command execution, and custom policy check. It is provided by the agent's sandbox — there is always one, defaulting to permissive if none is explicitly configured.

What the guard does

ResponsibilityDescription
Workspace jailRejects paths escaping the workspace root when jail: true.
Path policiesEach path access passes through ordered read/write deny/allow lists.
Command approvalGate dangerous commands behind deny/approve lists or interactive prompts.
Forbidden globsAlways-deny patterns for secrets, keys, and sensitive paths.

Authorizing access

Call guard.authorize() inside execute for any operation that touches the file system or shell:

const abs = await toolContext.guard.authorize(
  { type: "fs.read", resource: "data/cache.json" },
  {
    agentName: toolContext.agentName,
    toolName: "weather",
    signal: toolContext.abort,
  },
);

The guard returns the resolved absolute path. If access is denied, it throws a SandboxViolationError — the tool framework catches this and converts it to a structured ToolError with kind permission_denied.

Tool-level policies

Custom tools can declare their own policies via the policies option. These are appended to the guard's policy chain at tool construction time:

import { defineTool, denyCommandsPolicy } from "@comma-agents/core";

const tool = defineTool({
  description: "Run a user-supplied shell command",
  parameters: z.object({ command: z.string() }),
  policies: [
    denyCommandsPolicy(["rm -rf /", "sudo shutdown"]),
  ],
  execute: async ({ command }, ctx) => {
    await ctx.guard.authorize(
      { type: "command.execute", resource: command },
      { agentName: ctx.agentName, toolName: "my_tool", signal: ctx.abort },
    );
    // ...
  },
});

Built-in policy factories include denyCommandsPolicy, approveCommandsPolicy, forbiddenGlobsPolicy, and pathPolicy. You can also define custom Policy functions with the full "allow" / "deny" / "ask" / "pass" decision model.

For the full guard API — jail mode, path policies, the "ask" dispatch flow, session decisions, and interactive permission prompts — see the Sandbox documentation.

Result Helpers

okResult

Builds a successful ToolResult. The output field is the text returned to the model.

import { okResult } from "@comma-agents/core";

// Basic success
return okResult("Wrote 12 bytes");

// With structured data
return okResult("File saved successfully", {
  data: { sha256: "abc123", sizeBytes: 4096 },
  metadata: { durationMs: 15 },
});

errorResult

Builds a failure ToolResult from a structured ToolError. If no output override is provided, it defaults to error.message.

import { errorResult } from "@comma-agents/core";

return errorResult({
  kind: "stale_file",
  message: "File changed since last read",
  path: "/workspace/src/index.ts",
  recoverable: true,
  suggestedNextAction: "Re-read the file to get the latest content before editing.",
});

toolError

Utility for constructing a ToolError without repeating the kind as a literal.

import { toolError } from "@comma-agents/core";

const err = toolError("not_found", "No such file", {
  path: "/workspace/missing.txt",
  recoverable: true,
  suggestedNextAction: "Check the path and try again.",
});

On this page