Agents & profiles
Last updated: 2025-12-30Agent registry
- The server exposes a pluggable Agents module responsible for:
- Defining the
Agentinterface (capabilities, configuration). - Registering concrete agents (e.g. Codex via SDK and local CLI).
- Emitting events so the UI can stay in sync with the available agents.
- Defining the
- Registry behavior:
registerAgentadds an agent and emitsagent.registered.initializeAgentsdynamically checks availability and registers only installed agents.bindAgentEventBuspublishes the full registry when the event bus becomes available.
- Agent availability detection:
- Agents can optionally implement an
availability()method that checks if required executables or dependencies are installed. - Codex and Droid agents check for their respective CLI executables via environment variables or PATH.
- OpenCode always returns
trueas its SDK is bundled with KanbanAI. - Agents without an
availability()method are registered unconditionally.
- Agents can optionally implement an
- In the current implementation:
- The server uses
initializeServerAgents()to dynamically register only available agents at startup. - The UI focuses on a Codex-based agent backed by the Codex SDK and local Codex CLI.
- OpenCode is a first-class SDK-backed agent exposed via the API and
/agentsendpoints. - Droid is supported for starting card attempts via the API (schema-validated); the agent itself remains experimental and registration may be limited based on installation availability.
- The server uses
Coding agents
KanbanAI’s agent registry hosts multiple coding agents. The currently supported agents for starting attempts are Codex, OpenCode, and Droid, with additional agents under development.-
Codex
- Status: Supported (primary coding agent).
- Implementation:
- Backed by the Codex SDK and local Codex CLI.
- Exposed in the UI (e.g.
/agents/CODEX) and used for all production Attempts by default.
- Capabilities:
- Reads and writes files inside attempt worktrees.
- Runs dev scripts and tools via the Attempts/runner pipeline.
- Streams structured messages (steps, logs, diffs, suggestions) that power the Messages, Processes, and Logs views.
- Configuration:
- Tuned via agent profiles (model, temperature, tools/sandbox config) stored per project or globally.
- Supports
modelReasoningEffort(minimal|low|medium|high|xhigh) to control how much reasoning the Codex backend performs; higher levels, especiallyxhigh, typically trade higher latency and token usage for more robust planning and analysis.
-
Droid
- Status: Supported for starting attempts (experimental agent).
- Implementation:
- Experimental agent module wired into the registry (may be limited to development builds).
- Shares the same Attempt lifecycle and worktree model as Codex.
- Validated in
startAttemptSchemafor card attempts alongside Codex and OpenCode.
- Capabilities:
- Supports standard attempt operations (start, follow-up, stop).
- Streams structured messages into the Attempt model.
- Image support: Accepts image attachments in attempt requests (PNG, JPEG, WebP up to 10MB each, max 5 per message). Images are passed to the Droid SDK as file attachments and included in conversation items for replay. Note: Inline tasks (ticket enhancement, PR summaries) do not support images as they are text-based operations.
- Configuration:
- Uses the same profile system as other agents.
-
OpenCode
- Status: Supported (SDK-backed coding agent).
- Implementation:
- Backed by the official
@opencode-ai/sdkclient. - Uses the OpenCode HTTP API for sessions, messages, and events.
- Can talk either to a local
opencode serveinstance (managed via the SDK) or to a remote OpenCode server when a base URL is configured (viabaseUrlin the profile orOPENCODE_BASE_URL).
- Backed by the official
- Capabilities:
- Reads and writes files inside attempt worktrees via OpenCode tools.
- Streams structured messages, tool invocations, and todos into KanbanAI’s Attempt model.
- Implements the unified inline interface for:
kind = "ticketEnhance"– inline ticket enhancement.kind = "prSummary"– PR inline summary (Create Pull Request dialog suggestions and checklisted indocs/core/pr-inline-summary.md).
- Configuration:
- Tuned via agent profiles (primary model/agent selection, append/inline prompts, optional base URL / API key, port, variant).
- Providing
baseUrl(orOPENCODE_BASE_URL) switches the agent into remote mode whileapiKeyis mirrored intoOPENCODE_API_KEYwhen the SDK runs the local server, keeping credentials inside the profile without extra env setup. - The
portfield configures the local server port (default: 4097). When a server is already running on the configured port, KanbanAI automatically detects and reuses it instead of starting a new instance, logging[opencode] reusing external server at http://127.0.0.1:{port}. Valid ports are 1-65535, excluding reserved ports (80, 443, 22, 25, 53, 110, 143, 993, 995, 3306, 5432, 6379, 8080, 8443). - The optional
variantfield enables extended thinking/reasoning modes when supported by the model. Use this to enable deeper analysis and more robust planning at the cost of higher latency and token usage.
Inline tasks & ticket enhancement
- Agents can optionally implement a unified inline interface:
inline(kind, input, profile, opts?)alongsiderun/resume.kindis anInlineTaskKindsuch as:'ticketEnhance'– current ticket enhancement flow (Title + Description enhancement).'prSummary'– PR inline summary (Create Pull Request dialog suggestions and checklisted indocs/core/pr-inline-summary.md).'prReview'– reserved for future PR review/analysis features.
input/resultshapes are mapped viaInlineTaskInputByKind/InlineTaskResultByKind.- For
ticketEnhance, these map directly toTicketEnhanceInput/TicketEnhanceResult.
- For
opts.contextis anInlineTaskContextproviding:projectId,boardId,repositoryPath,baseBranch.- Optional branch/commit metadata and the effective
agentKey/profileId.
opts.signalis an optionalAbortSignalused to cancel the inline task.
- Ticket enhancement is implemented as a specific inline task:
- Typical use cases:
- Enriching an imported GitHub issue before it becomes a KanbanAI card.
- Rewriting terse card titles/descriptions with additional acceptance criteria before an Attempt starts.
- Custom prompts:
- Projects can configure a custom enhancement prompt via
enhancePromptin project settings. - When set, the custom prompt replaces the default system prompt while the ticket context (title, description, type) is still appended.
- Profile append prompts continue to apply on top of the custom prompt.
- Projects can configure a custom enhancement prompt via
- Helper utilities:
core/agents/utils#splitTicketMarkdown(markdown, fallbackTitle, fallbackDescription)extracts a leading H1 (#) from LLM output, making it easier for agents to return Markdown while still conforming to the required result shape.core/agents/utils#buildTicketEnhancePrompt(input, appendPrompt)builds a standardized English-language prompt for ticket enhancement, used by agents like DROID and CODEX so they can share the same Markdown contract (H1 title, detailed body, and at least onemermaiddiagram). When a custom prompt is configured in project settings, it replaces the default base prompt while still appending the input context and any profile append prompt.TicketEnhanceInput/TicketEnhanceResult,InlineTaskKind,InlineTaskContext, and the inline task maps are exported fromcore/agentTypes(viacore/src/index.ts) so custom agents can share the same types without reaching into private modules.
- Typical use cases:
Core inline orchestrator and inline tasks
-
The core layer exposes:
runInlineTask({agentKey, kind, input, profile, context, signal?})fromcore/agentInline(viacore/src/index.ts) as the reusable inline orchestrator.agentEnhanceTicket(opts)fromcore/agentEnhanceTicket(viacore/src/index.ts) as the single entrypoint for ticket enhancement.agentSummarizePullRequest(opts)fromcore/agentSummarizePullRequest(viacore/src/index.ts) as the entrypoint for PR summaries.
-
runInlineTaskbehavior:- Resolves the agent via the registry and validates that it implements
inline. - Delegates to
agent.inline(kind, input, profile, {context, signal}). - Normalizes errors into
InlineTaskErrorwithkind,agent,code, andmessage:UNKNOWN_AGENT,AGENT_NO_INLINE,INLINE_TASK_FAILED,ABORTED.
- Resolves the agent via the registry and validates that it implements
-
agentEnhanceTicketbehavior:- Inputs:
projectId, optionalboardId,title,description, optionalagentKey, optionalprofileId, and an optionalAbortSignal. - Resolves
boardId,agentKey, andprofileIdfrom project settings and inputs:- Uses the explicit
agentKey/profileIdwhen provided. - Otherwise consults the per-inline-agent profile mapping for
InlineAgentId = "ticketEnhance"when configured so ticket enhancement can use a dedicated profile. - Otherwise uses the project’s configured inline agent/profile by default when set.
- Otherwise falls back to the project’s default agent/profile (or
"DROID"when no default agent is set). - Allows advanced callers to override
agentKey/profileIdexplicitly.
- Uses the explicit
- Constructs a
TicketEnhanceInput(including a cancellation signal) andInlineTaskContext. - Resolves the agent profile using the shared profile resolution helpers.
- Supports specialized inline profiles:
- Agent profile configs may define an
inlineProfilestring used primarily for inline responses (e.g. ticket enhancement), and for OpenCode it also acts as the main system prompt when present. - When
inlineProfileis non-empty, inline prompts prefer it; otherwise they fall back to the primary profile prompt (such asappendPrompt), preserving existing behavior by default.
- Agent profile configs may define an
- Annotates the inline context with
profileSource: "inline" | "primary"so downstream telemetry can see whether the inline or primary profile drove a given request. - Invokes
runInlineTask({kind: 'ticketEnhance', ...})and returns the resultingTicketEnhanceResult. - The server layer should call this function instead of wiring agents, profiles, and settings manually.
POST /projects/:projectId/tickets/enhance(Projects router) is the HTTP surface area: it validates{title, description?, agent?, profileId?}payloads, forwards them intoagentEnhanceTicket, and returns{ticket}or RFC 7807 errors so the client can enrich cards without bespoke agent wiring.
- Inputs:
-
agentSummarizePullRequestbehavior:- Inputs:
projectId, requiredheadBranch, optionalbaseBranch, optionalagentKey, optionalprofileId, optionalattemptId/cardIdfor linked GitHub issues, and an optionalAbortSignal. - Resolves
agentKeyandprofileIdfrom project settings and inputs using the same rules as ticket enhancement:- Uses the explicit
agentKey/profileIdwhen provided. - Otherwise consults the per-inline-agent profile mapping for
InlineAgentId = "prSummary"when configured so PR summaries can use a dedicated profile. - Otherwise prefers the project’s configured inline agent/profile when set.
- Otherwise falls back to the project’s default agent/profile (or
"DROID"when no default agent is set). - Allows advanced callers to override
agentKey/profileIdexplicitly.
- Uses the explicit
- Custom prompts:
- Projects can configure a custom PR summary prompt via
prSummaryPromptin project settings. - When set, the custom prompt replaces the default system prompt while the repository and change context is still appended.
- Profile append prompts continue to apply on top of the custom prompt.
- Projects can configure a custom PR summary prompt via
- Loads the project and settings (including
repositoryPathandbaseBranch) and constructs aPrSummaryInlineInput:repositoryPath,baseBranch,headBranch, plus optional change summaries in the future.
- Resolves the agent profile using the shared profile resolution helpers, including support for inline-specific prompts via
inlineProfile. - Builds an
InlineTaskContextannotated withprofileSource: "inline" | "primary"so downstream telemetry can distinguish which prompt was used. - Invokes
runInlineTask({agentKey, kind: 'prSummary', input, profile, context, signal}), appends an auto-close line likecloses #123when linked GitHub issues exist (or fully-qualifiedowner/repo#123when the PR repo is unknown), and returns the resultingPrSummaryInlineResult{title, body}used to populate PR title/body suggestions in the UI. When bothcardIdandattemptIdare supplied,cardIdwins and mismatches are treated as errors by the server.
- Inputs:
Profiles: configuration for agents
- Agent profiles capture reusable configuration for a specific agent, such as:
- Model and sampling parameters.
- Tool/sandbox settings.
- Any agent-specific options encoded as JSON.
- Profiles are stored in SQLite and managed through the
core/agents/profilesservice:listAgentProfiles(projectId)getAgentProfile(projectId, id)createAgentProfile(projectId, agent, name, config)updateAgentProfile(projectId, id, patch)deleteAgentProfile(projectId, id)
- Each create/update/delete operation emits
agent.profile.changedso caches and UI views can refresh.
Global vs project profiles
- Profiles can be scoped:
- Per project – associated with a specific project’s ID.
- Global – workspace-wide entries (IDs beginning with
apg-).
- The UI surfaces both:
- The Dashboard and settings pages expose an Agents view (e.g.
/agents/CODEX) where profiles can be created and edited. - When starting or following up on an Attempt, the “profile” selector shows relevant profiles from both scopes.
- The Dashboard and settings pages expose an Agents view (e.g.
How Attempts use agents and profiles
- When a new Attempt is started or resumed:
- The Attempts service resolves which agent to use from:
- The explicit agent selected in the UI, or
- The project’s default agent setting.
- If a profile ID is provided (or a project default profile is configured), the service:
- Loads the profile.
- Validates it against the agent’s schema.
- Applies the configuration to the agent runner.
- The Attempts service resolves which agent to use from:
- Behavior on invalid/missing profiles:
- If a referenced profile is missing or fails validation, KanbanAI logs a warning.
- The Attempt falls back to the agent’s default profile so work can continue without manual cleanup.
- During execution:
- Agents implement
runandresumeand stream structured messages back via callbacks. - The Attempts module translates these into
attempt.*events and persists logs + conversation items, powering the Messages, Processes, and Logs tabs in the UI.
- Agents implement