DeepAgentExecutor runs in-process agents via LangGraph's createReactAgent. This is the default runtime for all agents defined in workspace/agents/*.yaml.
Type string: deep-agentPackage: @langchain/langgraph + @langchain/openaiRegistered by: AgentRuntimePlugin — one executor per agent YAML file at install() time.
How it works
AgentRuntimePluginscansworkspace/agents/on startup and creates oneDeepAgentExecutorper YAML file- Each executor is registered in
ExecutorRegistryfor the skills listed in the agent's YAML - When
SkillDispatcherPluginroutes aSkillRequestto this executor, it:- Creates a LangGraph ReAct agent with
ChatOpenAIpointed at the LiteLLM gateway - Resolves the system prompt: if the matched skill has
systemPromptOverride, uses that; otherwise uses the agent'ssystemPrompt - Replays recent conversation history when per-agent memory is enabled (
this.memory.history(...)), so the agent sees the conversation rather than only the latest message - Provides LangChain tools matching the
toolswhitelist from the agent YAML - Runs the agent loop with
recursionLimitderived frommaxTurns - Returns the final AI message as
SkillResult.text
- Creates a LangGraph ReAct agent with
Agent YAML definition
# workspace/agents/ava.yaml
name: ava
role: general
model: claude-opus-4-6
systemPrompt: |
You are Ava, the chief-of-staff protoAgent...
tools:
# Orchestration
- chat_with_agent
- delegate_task
- publish_event
- manage_cron
- run_ceremony
- list_agents
# Observation
- get_projects
- get_ci_health
- get_pr_pipeline
- get_branch_drift
- get_incidents
- get_ceremonies
- searxng_search
# Write / Act
- create_github_issue
- report_incident
- pr_inspector
# Conversation
- react
- send_update
maxTurns: 25
discordBotTokenEnvKey: DISCORD_BOT_TOKEN_AVA
skills:
- name: chat
description: Operational helm — the user's single control interface.
keywords: []
- name: debug_ci_failures
description: Investigate CI failures, delegate to Quinn, file issues.
keywords: [ci, failure, build, pipeline]Skill-level systemPromptOverride
When a skill declares systemPromptOverride, the executor uses it instead of the agent's main systemPrompt for that specific invocation. This allows structured-output skills (like diagnose_pr_stuck) to use narrow, format-enforcing prompts while operational skills use the full conversational prompt.
See Agent Skills Reference for the full YAML schema and available tools.
Streaming & live tool narration
The executor streams the LangGraph run rather than calling invoke(). It iterates agent.stream({ messages }, { streamMode: "values" }), which yields the full accumulated state after each node; the final yield is exactly what invoke() would have returned, so all downstream extraction (final text, memory, structured output) is unchanged.
Streaming exists so progress reaches the caller live — the moment each turn streams in — instead of being replayed in a tight loop after the whole run settles. The executor fires three hooks during the run, all wired by AgentRuntimePlugin onto the bus:
| Hook | When | Bus topic | Payload |
|---|---|---|---|
onProgress | An initial "thinking" frame, emitted before the first LLM turn (which can take 15–20s) so the stream is not byte-silent | agent.skill.progress.{correlationId} | Humanized progress text |
onToolCall | Each tool-call turn as it streams in, before its tools execute | agent.skill.progress.{correlationId} | Humanized tool-call narration (toolNames, toolCalls) |
onToolFrame | A structured tool-call-v1 lifecycle frame (started → completed/failed) per tool call as the graph streams | agent.skill.toolframe.{correlationId} | Structured ToolCallArtifactData frame |
The progress topics carry humanized narration for plain clients; the toolframe topic carries structured frames that the a2a-server emits as streamed artifact-update DataParts for rich clients. All three hooks are best-effort — a throwing callback is logged and never aborts the run.
Available tools
Tools are defined as LangChain tools with zod schemas in DeepAgentExecutor. Each wraps an HTTP call to workstacean's own API. Agents only get the tools listed in their tools: array — unlisted tools are not available.
The tables below are a representative selection. The canonical, complete tool list is the set of name: entries in src/executor/executors/deep-agent-executor.ts.
Orchestration
| Tool | API endpoint | Purpose |
|---|---|---|
chat_with_agent | POST /api/a2a/chat | Multi-turn A2A conversation with another agent |
delegate_task | POST /api/a2a/delegate | Fire-and-forget dispatch to an agent |
publish_event | POST /publish | Inject a raw bus event |
manage_cron | POST /api/ceremonies/* | CRUD scheduled ceremonies |
run_ceremony | POST /api/ceremonies/:id/run | Manually trigger a ceremony |
Observation
| Tool | API endpoint | Purpose |
|---|---|---|
get_projects | GET /api/projects | List registered projects |
get_ci_health | GET /api/ci-health | CI pass rates across repos |
get_pr_pipeline | GET /api/pr-pipeline | Open PRs: total, conflicts, stale, failing CI |
get_branch_drift | GET /api/branch-drift | Dev vs main divergence per project |
get_incidents | GET /api/incidents | Open security/operational incidents |
get_ceremonies | GET /api/ceremonies | List ceremony definitions |
searxng_search | SearXNG /search | Quick web search (5 results) |
Write / Act
| Tool | API endpoint | Purpose |
|---|---|---|
create_github_issue | POST /api/github/issues | File GitHub issues on managed repos |
report_incident | POST /api/incidents | File a security/operational incident |
Conversation feedback
| Tool | API endpoint | Purpose |
|---|---|---|
react | POST /api/discord/react | Add emoji reaction to triggering message |
send_update | POST /api/discord/progress | Send progress update during long work |
Discord operations (protoBot agent)
| Tool | API endpoint | Purpose |
|---|---|---|
discord_server_stats | GET /api/discord/server-stats | Server stats: members, channels, roles |
discord_list_channels | GET /api/discord/channels | List all channels |
discord_create_channel | POST /api/discord/channels/create | Create a channel |
discord_send | POST /api/discord/send | Send a message to a channel |
discord_list_members | GET /api/discord/members | List server members |
LLM gateway
All LLM calls route through LiteLLM at LLM_GATEWAY_URL (or OPENAI_BASE_URL). The executor creates a ChatOpenAI instance with the gateway as baseURL and OPENAI_API_KEY for auth. Model aliases (e.g. protolabs/reasoning, claude-sonnet-4-6) are resolved by the gateway.
Observability
correlationId from the bus message is propagated through the LangGraph invocation. When LANGFUSE_* env vars are set, the LangChain callback handler traces every LLM call and tool invocation to Langfuse.
When to use
Use DeepAgentExecutor for any agent that should run inside the workstacean process with direct access to bus tools. This is the right choice for most agents.
Use A2A instead when the agent lives in a separate service (protopen, protoContent) or needs its own resource isolation.