cape.agent.agentcntl: Controller class for CAPE agent

This module provdes the class AgentCntl which serves as an object-oriented interface to the CAPE agentic capability and interface. Most of the actual agent loop, including passing user responses on to an external LLM and processing the results, is implemented by methods of AgentCntl.

The module-level list EDIT_FILE_ALLOW_LIST is a registry of POSIX-style file names, relative to the folder in which the agent is launched, that skills may append to at run time (e.g. the fix-json skill registers the file the user asked to repair). The cape.agent.skills.fileedit module merges these names into its edit allow-list in addition to its own static patterns and the files provided by cape.cfdx.cntl.Cntl.get_edit_allowlist().

class cape.agent.agentcntl.AgentCntl(fname: str | None = None)

Controller class for the CAPE agentic interface

This class implements the agent loop behind cape --agentic: reading user input, passing messages to an external LLM server, and processing any tool calls in its response. The tools and skills exposed to the LLM are filtered based on the options for the model in use (see cape.agent.options); user skills are discovered from the folder in which the agent is launched (see cape.agent.skills).

Call:
>>> cntl = AgentCntl(fname=None)
Inputs:
fname: {None} | str

Name of CAPE-agentic JSON file (defaults to "cape-agent.json")

Outputs:
cntl: AgentCntl

Controller for the CAPE agent loop

RootDir

str Root folder for this controller

activate_skill_tools(name: str) → tuple[list, list]

Activate tools belonging to one available skill

base_url

str Base URL of LLM server’s OpenAI-compatible API

client

openai.OpenAI Client interface to LLM server

fdir

str Folder in which JSON file is located, relative to root dir

fname

str JSON file name (follows links if necessary) rel. to root dir

history

list[dict] | None Message history for current conversation

launch_cli_task(messages: list, cmdlist: list)

Launch a typed command as a background task

Runs cmdlist as a background task and records the launch in the conversation history.

launch_tool_task(name: str, kwargs: dict, result: dict) → dict

Launch a tool call as a background task

Returns the tool’s immediate result; its full result is delivered as a follow-up message when the task completes.

model

str Name of LLM model currently in use

reap_tasks(section_handler=None)

Notify user and history of completed background tasks

run_agent(user_message: str, spinner: bool = True, capture_subprocess: bool = False, section_handler=None) → dict

Run one pass of model with multi-round tool calling

Run one user turn with up to MaxToolCallLoops rounds of tool calls. This allows the agent to chain tool calls, e.g., calling cape_find() followed by cape_c() with the results from the first call.

Inputs:
user_message: str

User prompt or direct command

spinner: {True} | False

Show the readline thinking spinner; Textual supplies its own

capture_subprocess: {False} | True

Pipe direct system-command output through sys.stdout

section_handler: {None} | callable

Optional presentation hook called at the start and end of reasoning and tool sections

Outputs:
result: dict

Counts of tool calls and failures for this turn

tasks

list[bgtasks.BackgroundTask] Background tasks launched during this session

use_skill(name: str) → dict

Load an available skill and activate the tools it provides

class cape.agent.agentcntl.AgentResult(returncode, result)
result

Alias for field number 1

returncode

Alias for field number 0

cape.agent.agentcntl.genr8_image_message(images: list) → dict

Build a user message carrying image content for the model

Call:
>>> msg = genr8_image_message(images)
Inputs:
images: list

Image entries from a tool result; each item is either a dict with url (and optional case/file) or a bare data-URL str

Outputs:
msg: dict

Multimodal user message for the chat API

cape.agent.agentcntl.genr8_system_prompt(skills: dict, tools: dict | None = None) → str

Build the system prompt, listing available agent skills

Tool-specific guidance (e.g. for cape_find or view_subfig) is included only when the corresponding tool is exposed to the model, and available skills are listed at the end.

Call:
>>> prompt = genr8_system_prompt(skills, tools=None)
Inputs:
skills: dict[skills.skillbase.Skill]

Map of skill names to skill definitions

tools: {None} | dict

Map of tool names exposed to the model

Outputs:
prompt: str

System prompt for the LLM

cape.agent.agentcntl.get_reasoning_text(message) → str

Get normalized reasoning text from a completion message

Inputs:
message: object

Chat-completion response message

Outputs:
text: str

Reasoning text, or an empty string when none was exposed

cape.agent.agentcntl.show_reasoning(message, framed: bool = True) → bool

Display post-response reasoning content when available

This uses the nonstandard reasoning field exposed by current vLLM servers, with reasoning_content as a fallback for older compatible servers. The OpenAI Python client keeps unknown response fields as model extras, so getattr() works even when the installed SDK does not declare either field.

Call:
>>> shown = show_reasoning(message)
Inputs:
message: object

Chat-completion response message

framed: {True} | False

Print the classic prompt and horizontal rules

Outputs:
shown: bool

Whether nonempty reasoning content was displayed

cape.agent.agentcntl.start_response(title: str = 'Agent: ', txt: str | None = None)

Produce the header at the start of agent’s actual response

Call:
>>> start_response(title, txt)
Inputs:
title: str

Section title, diplayed purple as f"[{title}]"

txt: {None} | str

Optional text after section title

cape.agent.agentcntl.start_section(title: str, txt: str | None = None)

Produce the header at the start of a section, non-TUI

Call:
>>> start_section(title, txt)
Inputs:
title: str

Section title, diplayed purple as f"[{title}]"

txt: {None} | str

Optional text after section title