Skip to content

Commit 520977d

Browse files
committed
feat: Add agent-acp step type with ACP protocol support
- Implement agent-acp step type for spawning external ACP agent subprocesses via Agent Communication Protocol - Add ACPExecutor with validation, field rendering, and subprocess lifecycle management - Integrate agent-acp field rendering in StepDispatcher (batch and sequential modes) - Add run_agent() utility function for workflows to execute ACP agents from steps and JS context - Add osmedeus agent CLI command for interactive agent execution with --agent, --cwd, --timeout, --stdin, and --list flags - Add /osm/api/agent/chat/completions REST endpoint with OpenAI-compatible chat format and concurrency control - Support agent selection via: built-in names (claude-code, codex, opencode, gemini) or custom acp_config.command - Add step-level configuration: cwd, allowed_paths, acp_config (command, args, env, write_enabled) - Add comprehensive E2E tests for agent-acp workflows (basic, minimal, config, codex variants) - Add test workflows in test/testdata/workflows/agent-and-llm/ - Update AGENTS.md documentation with agent-acp examples, CLI usage, and API endpoints
1 parent 375c159 commit 520977d

30 files changed

+2383
-10
lines changed

CLAUDE.md

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ CLI/API (pkg/cli, pkg/server)
6363
6464
Executor (internal/executor) - coordinates workflow execution
6565
66-
StepDispatcher - routes to: BashExecutor, FunctionExecutor, ForeachExecutor, ParallelExecutor, RemoteBashExecutor, HTTPExecutor, LLMExecutor, AgentExecutor
66+
StepDispatcher - routes to: BashExecutor, FunctionExecutor, ForeachExecutor, ParallelExecutor, RemoteBashExecutor, HTTPExecutor, LLMExecutor, AgentExecutor, ACPExecutor
6767
6868
Runner (internal/runner) - executes commands via: HostRunner, DockerRunner, SSHRunner
6969
```
@@ -92,7 +92,7 @@ Runner (internal/runner) - executes commands via: HostRunner, DockerRunner, SSHR
9292

9393
```go
9494
WorkflowKind: "module" | "flow" // module = single unit, flow = orchestrates modules
95-
StepType: "bash" | "function" | "parallel-steps" | "foreach" | "remote-bash" | "http" | "llm" | "agent"
95+
StepType: "bash" | "function" | "parallel-steps" | "foreach" | "remote-bash" | "http" | "llm" | "agent" | "agent-acp"
9696
RunnerType: "host" | "docker" | "ssh"
9797
TriggerType: "cron" | "event" | "watch" | "manual"
9898
```
@@ -174,6 +174,61 @@ steps:
174174
findings: "{{agent_content}}"
175175
```
176176

177+
### Agent-ACP Step Type
178+
179+
The `agent-acp` step type spawns an external AI coding agent as a subprocess and communicates via the Agent Communication Protocol (ACP). Unlike the `agent` step type (which uses the internal LLM loop), `agent-acp` delegates to real agent binaries.
180+
181+
Built-in agents (defined in `internal/executor/acp_executor.go`):
182+
- `claude-code` — `npx -y @zed-industries/claude-code-acp@latest`
183+
- `codex` — `npx -y @zed-industries/codex-acp`
184+
- `opencode` — `opencode acp`
185+
- `gemini` — `gemini --experimental-acp`
186+
187+
Key YAML fields:
188+
- `agent` - Built-in agent name (required unless `acp_config.command` is set)
189+
- `messages` - Conversation messages (role + content) used as the prompt
190+
- `cwd` - Working directory for the ACP session
191+
- `allowed_paths` - Restrict file reads to these directories
192+
- `acp_config.command` - Custom agent command (overrides built-in registry)
193+
- `acp_config.args` - Custom agent command arguments
194+
- `acp_config.env` - Environment variables for the agent process
195+
- `acp_config.write_enabled` - Allow file writes (default: false)
196+
197+
Available exports: `acp_output`, `acp_stderr`, `acp_agent`
198+
199+
```yaml
200+
steps:
201+
- name: acp-agent
202+
type: agent-acp
203+
agent: claude-code
204+
cwd: "{{Output}}"
205+
allowed_paths:
206+
- "{{Output}}"
207+
acp_config:
208+
env:
209+
CUSTOM_VAR: "hello"
210+
write_enabled: true
211+
messages:
212+
- role: system
213+
content: "You are a security analyst."
214+
- role: user
215+
content: "Analyze the scan results in {{Output}} and create a summary."
216+
exports:
217+
analysis: "{{acp_output}}"
218+
```
219+
220+
### Agent CLI Command
221+
222+
Run an ACP agent interactively from the terminal:
223+
```bash
224+
osmedeus agent "your message here" # Run with claude-code (default)
225+
osmedeus agent --agent codex "your message" # Use a specific agent
226+
osmedeus agent --list # List available agents
227+
osmedeus agent --cwd /path/to/project "msg" # Set working directory
228+
osmedeus agent --timeout 1h "msg" # Custom timeout (default: 30m)
229+
echo "message" | osmedeus agent --stdin # Read from stdin
230+
```
231+
177232
## CLI Commands
178233

179234
```bash
@@ -230,6 +285,12 @@ osmedeus assets --stats -w <workspace> # Stats filtered by workspace
230285
osmedeus assets --columns url,title,status_code # Custom columns
231286
osmedeus assets --limit 100 --offset 50 # Pagination
232287
osmedeus assets --json # JSON output
288+
osmedeus agent "your prompt" # Run ACP agent (default: claude-code)
289+
osmedeus agent --agent codex "your prompt" # Use a specific agent
290+
osmedeus agent --list # List available agents
291+
osmedeus agent --cwd /path/to/project "prompt" # Set working directory
292+
osmedeus agent --timeout 1h "prompt" # Custom timeout (default: 30m)
293+
echo "prompt" | osmedeus agent --stdin # Read from stdin
233294
```
234295

235296
### Event Trigger Input Syntax
@@ -268,6 +329,7 @@ REST API documentation with curl examples is in `docs/api/`. Key endpoint catego
268329
- **Functions**: Execute utility functions via API
269330
- **Snapshots**: Export/import workspace archives
270331
- **LLM**: OpenAI-compatible chat completions and embeddings
332+
- **Agent ACP**: OpenAI-compatible endpoint that spawns local ACP agent subprocesses (`POST /osm/api/agent/chat/completions`)
271333
- **Install**: Binary registry and installation management
272334

273335
## Cloud Documentation

HACKING.md

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This document describes the technical architecture and development practices for
99
- [Core Components](#core-components)
1010
- [Workflow Engine](#workflow-engine)
1111
- [Agent Step Type](#agent-step-type)
12+
- [Agent-ACP Step Type](#agent-acp-step-type)
1213
- [Execution Pipeline](#execution-pipeline)
1314
- [Runner System](#runner-system)
1415
- [Authentication Middleware](#authentication-middleware)
@@ -138,7 +139,7 @@ type Workflow struct {
138139

139140
type Step struct {
140141
Name string
141-
Type StepType // bash, function, foreach, parallel-steps, remote-bash, http, llm, agent
142+
Type StepType // bash, function, foreach, parallel-steps, remote-bash, http, llm, agent, agent-acp
142143
PreCondition string // Skip condition
143144
Command string // For bash/remote-bash
144145
Commands []string // Multiple commands
@@ -167,6 +168,12 @@ type Step struct {
167168
OnToolStart string // JS hook before each tool call
168169
OnToolEnd string // JS hook after each tool call
169170

171+
// Agent-ACP step fields
172+
Agent string // Built-in ACP agent name (claude-code, codex, etc.)
173+
Cwd string // Working directory for ACP session
174+
AllowedPaths []string // Restrict file access to these directories
175+
ACPConfig *ACPStepConfig // Custom agent command, env, write permissions
176+
170177
Exports map[string]string
171178
OnSuccess []Action
172179
OnError []Action
@@ -400,6 +407,115 @@ Hook expressions receive these variables:
400407
- `iteration` - Current agent iteration number
401408
- `error` - Error string (empty if no error)
402409

410+
## Agent-ACP Step Type
411+
412+
The `agent-acp` step type spawns an external AI coding agent as a subprocess and communicates via the Agent Communication Protocol (ACP). Unlike the `agent` step type (which uses the internal LLM loop), `agent-acp` delegates to real agent binaries.
413+
414+
### Built-in Agents
415+
416+
| Agent Name | Command | Args |
417+
|------------|---------|------|
418+
| `claude-code` | `npx` | `-y @zed-industries/claude-code-acp@latest` |
419+
| `codex` | `npx` | `-y @zed-industries/codex-acp` |
420+
| `opencode` | `opencode` | `acp` |
421+
| `gemini` | `gemini` | `--experimental-acp` |
422+
423+
Defined in `builtinACPAgents` map in `internal/executor/acp_executor.go`.
424+
425+
### Architecture
426+
427+
```
428+
┌───────────────────────────────────────────────────────────────┐
429+
│ ACPExecutor.Execute() │
430+
│ 1. Resolve agent name → command + args │
431+
│ 2. Build prompt from step.Messages │
432+
│ 3. Call RunAgentACP() standalone function │
433+
│ a. Spawn subprocess with stdin/stdout pipes │
434+
│ b. Create ACP client (acpClient) for callbacks │
435+
│ c. ACP Initialize → NewSession → Prompt │
436+
│ d. Collect agent output via SessionUpdate callbacks │
437+
│ 4. Return output as StepResult with exports │
438+
└───────────────────────────────────────────────────────────────┘
439+
```
440+
441+
### YAML Structure
442+
443+
```yaml
444+
steps:
445+
- name: acp-agent
446+
type: agent-acp
447+
agent: claude-code # Built-in agent name
448+
cwd: "{{Output}}" # Working directory
449+
allowed_paths:
450+
- "{{Output}}"
451+
acp_config:
452+
env:
453+
CUSTOM_VAR: "value"
454+
write_enabled: true # Allow file writes (default: false)
455+
messages:
456+
- role: system
457+
content: "You are a security analyst."
458+
- role: user
459+
content: "Analyze the scan results."
460+
exports:
461+
analysis: "{{acp_output}}"
462+
```
463+
464+
### ACP Client Callbacks
465+
466+
The `acpClient` (`internal/executor/acp_client.go`) implements the `acp.Client` interface:
467+
468+
| Method | Behavior |
469+
|--------|----------|
470+
| `SessionUpdate` | Accumulates agent text output, logs tool calls and thoughts |
471+
| `RequestPermission` | Auto-approves by selecting `allow_once``allow_always` → first option |
472+
| `ReadTextFile` | Reads files scoped to `allowedPaths` |
473+
| `WriteTextFile` | Writes files if `writeEnabled` is true |
474+
| `CreateTerminal` / `KillTerminalCommand` / etc. | No-op stubs |
475+
476+
### Available Exports
477+
478+
| Export | Description |
479+
|--------|-------------|
480+
| `acp_output` | Collected agent text output |
481+
| `acp_stderr` | Agent process stderr |
482+
| `acp_agent` | Agent name used |
483+
484+
### Standalone Function
485+
486+
`RunAgentACP(ctx, prompt, agentName, cfg)` can be called independently from workflow execution (used by the CLI `agent` command and the API endpoint):
487+
488+
```go
489+
output, stderr, err := executor.RunAgentACP(ctx, "your prompt", "claude-code", &executor.RunAgentACPConfig{
490+
Cwd: "/workspace",
491+
AllowedPaths: []string{"/workspace"},
492+
WriteEnabled: false,
493+
})
494+
```
495+
496+
### Agent CLI Command
497+
498+
```bash
499+
osmedeus agent "your message" # Default agent (claude-code)
500+
osmedeus agent --agent codex "your message" # Specific agent
501+
osmedeus agent --list # List available agents
502+
osmedeus agent --cwd /path "message" # Set working directory
503+
osmedeus agent --timeout 1h "message" # Custom timeout
504+
echo "message" | osmedeus agent --stdin # Read from stdin
505+
```
506+
507+
### API Endpoint
508+
509+
`POST /osm/api/agent/chat/completions` provides an OpenAI-compatible interface:
510+
511+
```bash
512+
curl -X POST http://localhost:8000/osm/api/agent/chat/completions \
513+
-H "Content-Type: application/json" \
514+
-d '{"model":"claude-code","messages":[{"role":"user","content":"Hello"}]}'
515+
```
516+
517+
The `model` field maps to a built-in agent name. Only one ACP agent can run at a time (returns `409 Conflict` if busy).
518+
403519
### Execution Context
404520

405521

@@ -479,6 +595,9 @@ Lookup order:
479595
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
480596
│ HTTPExecutor │ │ LLMExecutor │ │AgentExecutor │
481597
└──────────────┘ └──────────────┘ └──────────────┘
598+
┌──────────────┐
599+
│ ACPExecutor │
600+
└──────────────┘
482601
│ │ │
483602
└────────────────────────────┼────────────────────────────┘
484603
@@ -551,6 +670,7 @@ Built-in executors registered at startup:
551670
- `HTTPExecutor` - handles `http` steps
552671
- `LLMExecutor` - handles `llm` steps
553672
- `AgentExecutor` - handles `agent` steps (agentic LLM loop with tool calling)
673+
- `ACPExecutor` - handles `agent-acp` steps (ACP subprocess agents)
554674

555675
## Run Control Plane
556676

@@ -1674,6 +1794,7 @@ test/e2e/ # E2E CLI tests
16741794
├── ssh_test.go # SSH runner e2e tests (module & step level)
16751795
├── api_test.go # API endpoint e2e tests (all routes)
16761796
├── agent_test.go # Agent step e2e tests
1797+
├── agent_acp_test.go # Agent-ACP step e2e tests
16771798
├── canary_test.go # Canary tests (real-world scans in Docker)
16781799
├── cloud_test.go # Cloud CLI e2e tests
16791800
├── db_clean_test.go # Database cleanup e2e tests

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Built for both beginners and experts, it delivers powerful, composable automatio
2626
- **Distributed Execution** - Redis-based master-worker pattern with queue system, webhook triggers, and file sync across workers
2727
- **Rich Function Library** - 80+ utility functions including nmap integration, tmux sessions, SSH execution, TypeScript/Python scripting, SARIF parsing, and CDN/WAF classification
2828
- **Event-Driven Scheduling** - Cron, file-watch, and event triggers with filtering, deduplication, and delayed task queues
29-
- **Agentic LLM Steps** - Tool-calling agent loops with sub-agent orchestration, memory management, and structured output
29+
- **Agentic LLM Steps** - Tool-calling agent loops with sub-agent orchestration, memory management, and structured output; plus ACP subprocess agents (Claude Code, Codex, OpenCode, Gemini)
3030
- **Cloud Infrastructure** - Provision and run scans across DigitalOcean, AWS, GCP, Linode, and Azure with cost controls and automatic cleanup
3131
- **Rich CLI Interface** - Interactive database queries, bulk function evaluation, workflow linting, progress bars, and comprehensive usage examples
3232
- **REST API & Web UI** - Full API server with webhook triggers, database queries, and embedded dashboard for visualization
@@ -99,6 +99,11 @@ osmedeus worker queue run --concurrency 5 # Process queue
9999
osmedeus worker status # Show workers
100100
osmedeus worker eval -e 'ssh_exec("host", "whoami")' # Eval with distributed hooks
101101

102+
# Run an ACP agent interactively
103+
osmedeus agent "analyze this codebase"
104+
osmedeus agent --agent codex "explain main.go"
105+
osmedeus agent --list
106+
102107
# Show all usage examples
103108
osmedeus --usage-example
104109
```
@@ -133,7 +138,7 @@ For more CLI usage and example commands, refer to the [CLI Reference](https://do
133138
│ │ CONFIG ──▶ PARSER ──▶ EXECUTOR ──▶ STEP DISPATCHER ──▶ RUNNER │ │
134139
│ │ │ │ │
135140
│ │ Step Executors: bash | function | parallel | foreach | remote-bash │ │
136-
│ │ http | llm | agent | SARIF/SAST integration │ │
141+
│ │ http | llm | agent | agent-acp | SARIF/SAST │ │
137142
│ │ Hooks: pre_scan_steps → [main steps] → post_scan_steps │ │
138143
│ │ │ │ │
139144
│ │ Runners: HostRunner | DockerRunner | SSHRunner │ │

cmd/osmedeus/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ var (
1212
)
1313

1414
// @title Osmedeus API
15-
// @version 5.0.0
15+
// @version 5.0.1
1616
// @description Workflow Engine for Offensive Security - REST API for managing security automation workflows, scans, and distributed task execution.
1717
// @termsOfService https://docs.osmedeus.org/terms/
1818

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/charmbracelet/bubbletea v1.3.10
1010
github.com/charmbracelet/glamour v0.10.0
1111
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
12+
github.com/coder/acp-go-sdk v0.6.3
1213
github.com/creativeprojects/go-selfupdate v1.5.2
1314
github.com/dgraph-io/ristretto v0.2.0
1415
github.com/digitalocean/godo v1.175.0
@@ -19,7 +20,7 @@ require (
1920
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
2021
github.com/goccy/go-yaml v1.19.1
2122
github.com/gofiber/adaptor/v2 v2.2.1
22-
github.com/gofiber/fiber/v2 v2.52.10
23+
github.com/gofiber/fiber/v2 v2.52.12
2324
github.com/gofiber/swagger v1.1.1
2425
github.com/golang-jwt/jwt/v5 v5.3.0
2526
github.com/google/uuid v1.6.0

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuh
9797
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
9898
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
9999
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
100+
github.com/coder/acp-go-sdk v0.6.3 h1:LsXQytehdjKIYJnoVWON/nf7mqbiarnyuyE3rrjBsXQ=
101+
github.com/coder/acp-go-sdk v0.6.3/go.mod h1:yKzM/3R9uELp4+nBAwwtkS0aN1FOFjo11CNPy37yFko=
100102
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
101103
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
102104
github.com/creativeprojects/go-selfupdate v1.5.2 h1:3KR3JLrq70oplb9yZzbmJ89qRP78D1AN/9u+l3k0LJ4=
@@ -174,8 +176,8 @@ github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
174176
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
175177
github.com/gofiber/adaptor/v2 v2.2.1 h1:givE7iViQWlsTR4Jh7tB4iXzrlKBgiraB/yTdHs9Lv4=
176178
github.com/gofiber/adaptor/v2 v2.2.1/go.mod h1:AhR16dEqs25W2FY/l8gSj1b51Azg5dtPDmm+pruNOrc=
177-
github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
178-
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
179+
github.com/gofiber/fiber/v2 v2.52.12 h1:0LdToKclcPOj8PktUdIKo9BUohjjwfnQl42Dhw8/WUw=
180+
github.com/gofiber/fiber/v2 v2.52.12/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
179181
github.com/gofiber/swagger v1.1.1 h1:FZVhVQQ9s1ZKLHL/O0loLh49bYB5l1HEAgxDlcTtkRA=
180182
github.com/gofiber/swagger v1.1.1/go.mod h1:vtvY/sQAMc/lGTUCg0lqmBL7Ht9O7uzChpbvJeJQINw=
181183
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=

internal/core/constants.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package core
33
// Project metadata constants
44
const (
55
// VERSION of this project
6-
VERSION = "v5.0.0"
6+
VERSION = "v5.0.1"
77
// DESC description of the tool
88
DESC = "A Modern Orchestration Engine for Security"
99
// BINARY name of osmedeus

0 commit comments

Comments
 (0)