# Where Agent Capabilities Are Defined in Nodeterm: Inside `src/shared/agents/config.ts`

> Discover where agent capabilities are defined in Nodeterm. This article explores the src/shared/agents/config.ts file, detailing agent IDs, configurations, and helper functions.

- Repository: [eneskirca/nodeterm](https://github.com/eneskirca/nodeterm)
- Tags: internals
- Published: 2026-08-23

---

**TLDR: In the eneskirca/nodeterm repository, all agent capabilities for Nodeterm are defined centrally inside [`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts), which exports agent IDs, a static `AGENT_CONFIG`, capability membership arrays, and predicate helper functions such as `canResume()`, `hasHooks()`, and `canControlCanvas()` that the rest of the codebase imports to gate features.**

Nodeterm is an open-source terminal-based agent launcher that supports multiple AI coding agents like Claude, Codex, Gemini, and Grok. Because each agent has different abilities — hook support, resume capability, usage tracking, or canvas control — Nodeterm centralizes every agent capability definition in a single module. According to the Nodeterm source code, that authoritative module lives at [[`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts)](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts), and every UI feature, launch-line builder, and process manager pulls capability checks from it.

## Nodeterm: The Single Source of Truth for Agent Capabilities

The design principle behind Nodeterm's agent capability system is **centralization**: one file defines what each agent can do, and everywhere else simply consumes those definitions. This guarantees consistent behavior across the desktop, server, and mobile surfaces — and makes adding a new capability a one-file change.

In [`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts), you'll find:

- **Agent identifiers** — a generic `AgentId` type plus built-in IDs (`claude`, `codex`, `gemini`, `opencode`, `grok`, `copilot`)
- **Static configuration** — the `AGENT_CONFIG` map holding launch commands, color associations, and prompt-injection modes per agent
- **Capability membership lists** — a series of `const ... = [...] as const` arrays, each representing the set of agents that support a given capability (e.g., `USAGE_CAPABLE`, `CANVAS_CONTROL_CAPABLE`, `RESUMABLE_AGENTS`)
- **Predicate helpers** — small functions like `hasHooks(id)`, `canResume(id)`, and `canControlCanvas(id)` that resolve capability for built-in and custom agents alike
- **Inheritance utilities** — `setCustomAgentBaseResolver`, `baseAgentOf`, and `capabilityAgentId`, which let custom agents inherit capability sets from a built-in agent harness

## What the Central Config Module Defines

Let's unpack each layer of the config file to see exactly where agent capabilities are defined.

### Agent Identifiers in Nodeterm

The module exports a generic **`AgentId`** type that unions all supported agent identifiers with a fallback for custom agents. The built-in IDs defined in the source are:

- `claude`
- `codex`
- `gemini`
- `opencode`
- `grok`
- `copilot`

Because `AgentId` is exported from [`config.ts`](https://github.com/eneskirca/nodeterm/blob/main/config.ts), any other module — from the PTY manager in `src/main/` to the React renderer in `src/renderer/` — can reference agents without importing each agent's implementation.

### Static Configuration: `AGENT_CONFIG`

The constant **`AGENT_CONFIG`** is a per-agent object map that stores startup metadata:

- **`launchCmd`** — the command used to boot the agent
- **Color** associations for UI badges
- **Prompt-injection modes** and related settings

Any component that constructs a launch command, such as the code in [`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts) used across the app, reads from `AGENT_CONFIG` and falls back to something like `'node'` for unknown custom agents.

### Capability Lists (Membership Arrays)

The core of the capability system lives in a series of **`const` arrays with `as const` assertions**. Each array is a membership list: "these agent IDs support the following capability."

Capability lists defined in the file include:

- `AGENT_HOOK_TARGETS` — agents that support hook execution
- `RESUMABLE_AGENTS` — agents that can be resumed after a restart
- `USAGE_CAPABLE` — agents that report usage metrics
- `CANVAS_CONTROL_CAPABLE` — agents that support the canvas-control skill

Because these lists are the literal source of truth, adding a new capability to Nodeterm only requires adding (or removing) an agent ID in the appropriate array. No other file has to be updated.

### Predicate Helpers: Resolving Capabilities at Runtime

Rather than checking the arrays directly, the rest of the codebase calls exported predicate functions that wrap those arrays:

- `hasHooks(agentId)` — does the agent receive hook callback events?
- `canResume(agentId)` — can the agent be resumed after termination?
- `canRename(agentId)` — does the agent support renaming?
- `canChat(agentId)` — does the agent support chat interaction?
- `canControlCanvas(agentId)` — does the agent expose canvas control?

Each predicate is smart: for built-in agents it checks the corresponding list; for **custom agents** it first resolves the inherited harness via `baseAgentOf()` and then checks that agent's capability set. That is how a custom agent at launch is treated as "a Codex but with X"—by inheriting the capability list of a built-in base agent.

### Custom Agent Inheritance Utilities

Optional custom agents need a way to map their `custom@` identifier to a built-in harness. For this, [`config.ts`](https://github.com/eneskirca/nodeterm/blob/main/config.ts) exports:

- `setCustomAgentBaseResolver(resolverFn)` — registers a function that maps a custom agent ID to its base agent ID
- `baseAgentOf(customAgentId)` — resolves the base ID (defaulting to the agent itself)
- `capabilityAgentId(agentId)` — returns the effective ID used for capability checks (either the custom ID or its base)

These utilities guarantee that any feature gating (permission-mode UI, canvas-control skill, copy feedback) behaves consistently for both built-in and custom agents.

## How the Codebase Consumes Agent Capability Predicates

Every module that needs to know what an agent can do imports the capability predicates from [`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts), ensuring one consistent query path.

| File | Role |
|---|---|
| [[`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts)](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts) | Defines `AgentId`, static `AGENT_CONFIG`, all capability arrays (`USAGE_CAPABLE`, `CANVAS_CONTROL_CAPABLE`, etc.) and the predicate helpers |
| [[`src/shared/agents/approval-mode.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/approval-mode.ts)](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/approval-mode.ts) | Maps high-level permission-mode values to CLI flags for agents that support `PERMISSION_MODE_CAPABLE` |
| [[`src/renderer/state/agent-status.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/state/agent-status.ts)](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/state/agent-status.ts) | Imports the capability predicates to drive UI badges, restart actions, and permission-mode UI |
| [[`src/main/pty-manager.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/pty-manager.ts)](https://github.com/eneskirca/nodeterm/blob/main/src/main/pty-manager.ts) | Uses `hasHooks` and `canResume` when spawning a new terminal session |

## Practical Examples: Using Agent Capability Predicates

Here's a look at how Nodeterm's own code — and your extensions — would query agent capabilities.

```ts
import {
  hasHooks,
  canResume,
  canControlCanvas,
  canRename,
  canChat,
  AGENT_CONFIG,
} from '@/shared/agents/config'

// Example: decide whether to show the "Restart Agent" menu item
function shouldShowRestart(agentId: AgentId): boolean {
  return canResume(agentId) && hasHooks(agentId)
}

```

```ts
// Example: build a launch command with the correct permission-mode flag
import { permissionModeFlag, resolvePermissionMode } from '@/shared/agents/config'

function buildLaunch(agentId: AgentId, project: Project | undefined, settings: Settings): string {
  const base = AGENT_CONFIG[agentId as BuiltinAgentId]?.launchCmd ?? 'node' // fallback for custom agents
  const mode = resolvePermissionMode(project, settings)
  const flags = permissionModeFlag(mode)   // [] for 'manual', otherwise ["--permission-mode","auto"]
  return `${base} ${flags.join(' ')}`.trim()
}

```

Canvas-control is a UI-heavy capability, so the renderer checks it before exposing the corresponding skill:

```ts
// Example: checking canvas-control capability before exposing the skill
if (canControlCanvas(agentId)) {
  // register the "manage-nodeterm-canvas" skill for this agent
}

```

These examples mirror the actual code patterns found in [`src/renderer/state/agent-status.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/state/agent-status.ts) and [`src/shared/agents/approval-mode.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/approval-mode.ts).

## What Happens When You Add a New Agent Capability to Nodeterm

To add a new capability to Nodeterm's agent model, you normally only edit one file:

1. Open [`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts).
2. Define a new membership array or a new predicate helper that reads it.
3. Optionally, add the feature logic in the renderer or main process that guards by that predicate.

Because the rest of the codebase always imports from this module, the new capability will automatically propagate to the PTY manager, the approval-mode mapper, and the UI badge system. Nodeterm's architecture was designed so that the capability model at [`config.ts`](https://github.com/eneskirca/nodeterm/blob/main/config.ts) is the only place you need to touch for most agent support changes.

## Summary

- All Nodeterm agent capabilities are defined in a single file: **[`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts)**.
- The file exports **capability arrays** (`RESUMABLE_AGENTS`, `USAGE_CAPABLE`, `CANVAS_CONTROL_CAPABLE`, `AGENT_HOOK_TARGETS`) plus predicate helpers like `hasHooks()`, `canResume()`, `canControlCanvas()`.
- Custom agents can inherit a built-in agent's capabilities via `baseAgentOf()` and `setCustomAgentBaseResolver()`.
- UI indicators, launch-line construction, and PTY spawning all import these predicates, guaranteeing one source of truth.

## Frequently Asked Questions

### What is the agent capability definition file in Nodeterm?

The agent capability definitions in Nodeterm live in [`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts). This file exports the `AgentId` type, the `AGENT_CONFIG` map, capability membership arrays, and predicate helpers like `canResume()` and `canControlCanvas()`.

### How do I add a new capability to an agent in Nodeterm?

You add the capability in the single configuration module: update [`src/shared/agents/config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/agents/config.ts) to include a new capability list and predicate. Since every other module imports capability checks from this file, your new capability automatically flows to the PTY manager, approval mode, and renderer.

### Can custom agents in Nodeterm inherit capabilities from built-in agents?

Yes. The custom agent mechanism lets you map an agent ID to a built-in base agent with `setCustomAgentBaseResolver` and `baseAgentOf`. Custom agents then reuse the capability queries of the base harness — for example, a custom agent treated as a Codex automatically gains `canResume`, `hasHooks`, and canvas-control until overridden.

### Which features in Nodeterm depend on agent capability predicates?

Several: the `Restart Agent` menu (uses `canResume` and `hasHooks`), permission-mode UI (uses `permissionModeFlag` and `resolvePermissionMode`), copy-feedback, the canvas-control skill and agent status badges in [`src/renderer/state/agent-status.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/state/agent-status.ts)— plus their spawning loop in [`src/main/pty-manager.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/pty-manager.ts).