Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c5167a7
feat: refactor-langgraph-to-deep-agents — OpenSpec proposal, design, …
avoidwork Jul 1, 2026
b7c22a0
feat: refactor to Deep Agents architecture
avoidwork Jul 1, 2026
a0257b8
fix: resolve lint errors and formatting issues
avoidwork Jul 1, 2026
15b5473
fix: pass configurable thread_id to Deep Agents stream call
avoidwork Jul 1, 2026
36886ef
fix: use systemPrompt from agent instance, not message array
avoidwork Jul 1, 2026
ce945e9
fix: use both updates and messages stream modes for Deep Agents
avoidwork Jul 1, 2026
0426494
fix: rewrite deepAgents.js — fix syntax error and missing variables
avoidwork Jul 1, 2026
fcd91a7
fix: remove systemPrompt prepending — agent instance handles it
avoidwork Jul 1, 2026
8ab3010
fix: remove unused namespace variable from stream loop
avoidwork Jul 1, 2026
50464cc
refactor: integrate deepagents middleware, remove LRU cache and overl…
avoidwork Jul 1, 2026
d067be4
fix: update tests for deepagents middleware integration
avoidwork Jul 1, 2026
7c28463
fix: pass permissions as [{ paths }] to createFilesystemMiddleware
avoidwork Jul 1, 2026
0b5ba25
fix: resolve permission paths to absolute paths for deepagents middle…
avoidwork Jul 1, 2026
ff23ee3
fix: filter out !node_modules exclusion from deepagents permissions
avoidwork Jul 1, 2026
248194e
fix: remove explicit middleware — deepagents adds it automatically
avoidwork Jul 1, 2026
1f2625d
chore: remove unused invokeAgent and streamAgent functions from deepA…
avoidwork Jul 1, 2026
7925a28
fix: use agent.stream() with proper await and messages stream mode
avoidwork Jul 1, 2026
fd4b1b1
WIP
avoidwork Jul 1, 2026
6c6fb87
fix: wrap streaming callback text in structured event object
avoidwork Jul 1, 2026
edadaaa
fix: wire middleware to deepAgents and fix streaming callback contract
avoidwork Jul 1, 2026
6caad37
docs: update documentation for Deep Agents in-lieu of Sub Agents
avoidwork Jul 1, 2026
4753b10
docs: remove all remaining Sub Agent references
avoidwork Jul 1, 2026
80eb388
docs: remove all subAgent tool references
avoidwork Jul 1, 2026
b2c7bf9
docs: update Built-in Tools section to reflect deepagents middleware
avoidwork Jul 2, 2026
8bdcbb5
chore: remove redundant tools replaced by deepagents middleware
avoidwork Jul 2, 2026
6965522
refactor: rename SUB_AGENT.md to CODE_AGENT.md and rewrite prompt
avoidwork Jul 2, 2026
ea953e1
chore: replace sub-agent terminology with deep agent, remove cache mo…
avoidwork Jul 2, 2026
97d6e63
chore: remove utility-agent, it duplicates the general-purpose agent …
avoidwork Jul 2, 2026
6f58584
Resolving merge conflict
avoidwork Jul 2, 2026
da239a8
WIP
avoidwork Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 28 additions & 38 deletions README.md

Large diffs are not rendered by default.

14 changes: 4 additions & 10 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,13 @@ agent:
recursionLimit: 1000
autoContinueLimit: 1000
nodeTimeout: 600000
turnHashWindow: 20
turnBufferMax: 64
deepAgents:
codingAgent:
description: "Specialized agent for code-related tasks including file editing, debugging, and implementation."
temperature: 0.3
lru:
size: 100
ttl: 600000
process:
subAgent:
timeout: 600000
maxConcurrent: 4
sessionMode: isolated
defaultStrategy: parallel
defaultOnError: continue
temperature: 0.7
persistence:
mode: memory
sqlite_path: memory/checkpoints.db
Expand Down
137 changes: 21 additions & 116 deletions docs/FLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,14 @@ Call chains and data flows for all primary code paths in the project, excluding
- [File Tool Execution Flow](#file-tool-execution-flow)
- [Terminal Tool Execution Flow](#terminal-tool-execution-flow)
- [Web Tool Execution Flow](#web-tool-execution-flow)
- [Sub-Agent Tool Execution Flow](#sub-agent-tool-execution-flow)
- [Deep Agents Orchestration Flow](#deep-agents-orchestration-flow)
- [Sandbox Skill Execution](#sandbox-skill-execution)
- [Memory Persistence Flow](#memory-persistence-flow)
- [Context Loading](#context-loading)
- [Schedule Manager Lifecycle](#schedule-manager-lifecycle)
- [Memory Retention Cleanup](#memory-retention-cleanup)
- [Profile Management](#profile-management)
- [Shutdown Flow](#shutdown-flow)
- [Sub-Agent Log Tool Flow](#sub-agent-log-tool-flow)
- [Sub-Agent Message Tool Flow](#sub-agent-message-tool-flow)
- [Additional Tool Flows](#additional-tool-flows)
- [File Dependencies](#file-dependencies)

Expand Down Expand Up @@ -668,64 +666,28 @@ Multi-engine search backends (webSearch):
```


## Sub-Agent Tool Execution Flow
## Deep Agents Orchestration Flow
## Deep Agents Orchestration Flow

**Entry:** `src/tools/subAgent.js` → `createSubAgentTool()`
**Entry:** `src/agent/deepAgents.js` → `createDeepAgentsOrchestrator()`

```
subAgent tool (zero-permission, always registered):
├── validate input: delegation (required), context (optional), tasks (optional for fan-out), cwd (optional)
├── if tasks provided (fan-out mode):
│ ├── for each task in tasks (bounded by maxConcurrent):
│ │ ├── spawn("node", ["index.js", "--sub-agent=true", `--cwd=${targetCwd}`, `--message="${prompt}"`])
│ │ ├── trackProcess(child, command) → { pid, child, status: "running", startTime }
│ │ ├── wait for completion or timeout (resolveTimeout: per-call > env > config)
│ │ └── parseSubAgentOutput(stdout) → { ok, result, error?, pid? }
│ │ └── Split on "# SubAgent" marker, parse JSON after marker
│ ├── if strategy === "sequential": wait for each to complete before next
│ ├── if strategy === "parallel": run up to maxConcurrent simultaneously
│ └── if onError === "fail-fast": abort remaining on first error
│ └── if onError === "continue": collect errors, return all results
├── else (single execution mode):
│ ├── spawn("node", ["index.js", "--sub-agent=true", `--cwd=${targetCwd}`, `--message="${prompt}"`])
│ ├── trackProcess(child, command) → { pid, child, status: "running", startTime }
│ ├── wait for completion or timeout
│ └── parseSubAgentOutput(stdout) → { ok, result, error?, pid? }
├── if returnParams provided:
│ └── filter result to only include specified keys
│ └── fallback to full text if not valid JSON
└── return { ok, result, error?, pid? }

escapeShellArg(arg):
├── Replace backticks, dollar signs, single quotes, double quotes
├── Escape newlines, tabs, carriage returns
└── Wrap in double quotes for safe shell passing

parseSubAgentOutput(stdout):
├── Split stdout on "# SubAgent" marker
├── Take content after marker
├── Try JSON.parse(content)
├── if valid JSON → { ok: true, result: parsed }
├── else → { ok: false, error: "Failed to parse sub-agent output" }

resolveTimeout(options):
├── if options.timeout provided → options.timeout
└── else → config.process.subAgent.timeout (default 600000)
```

**Process tracking:** Sub-agents share the `processTracker` Map from `terminal.js` for PID tracking and lifecycle management. Each sub-agent gets a unique PID that can be polled, waited on, or killed via the `process` tool.

**Session isolation modes:**

| Mode | Description |
|------|-------------|
| `isolated` | Fresh session, no parent context |
| `forked` | Forked from parent session with compaction |
| `shared` | Shared parent session context |

---
Deep Agents orchestrator (native multi-agent architecture):
├── createDeepAgent({ model, systemPrompt, tools, middleware, subagents, checkpointer })
│ ├── middleware: filesystem, memory, skills, summarization
│ ├── subagents:
│ │ ├── coding-agent: code editing, debugging, implementation, code review
│ └── orchestrator routes tasks automatically based on task nature
├── agent.stream(input, { streamMode: "messages", subgraphs: true })
│ ├── for each chunk:
│ │ ├── extract text content
│ │ └── streamingCallback({ type: "text", text })
│ └── returns { provider, content, tokens }
└── orchestrator manages routing, state, and observability natively

## Scan Agents Tool Flow
No process spawning, no marker-based parsing, no manual fan-out coordination.
The deepagents library handles agent lifecycle, state management, and streaming internally.
```

**Entry:** `src/tools/scanAgents.js` → `createScanAgentsTool()`

Expand Down Expand Up @@ -789,65 +751,8 @@ runScheduledSkill(schedule, sandbox, sessionState)

```

## Sub-Agent Log Tool Flow

**Entry:** `src/tools/subAgentLog.js` → `createSubAgentLogTool()`
## Deep Agents Log Management

```
subAgentLog tool (zero-permission, always registered):
├── validate input: action (required), pid (optional), maxAgeHours (optional)
├── switch action:
│ ├── "list":
│ │ ├── readdir("/tmp") → filter files matching "sub-agent-{pid}.log"
│ │ ├── for each log file:
│ │ │ ├── stat(filePath) → size, mtime
│ │ │ ├── isProcessRunning(pid) → process.kill(pid, 0)
│ │ │ └── { pid, file, size, modified, running }
│ │ └── sort by modified (descending) → return { ok: true, logs }
│ ├── "read":
│ │ ├── if pid missing → { ok: false, error: "PID is required" }
│ │ ├── readFile("/tmp/sub-agent-{pid}.log") → content
│ │ └── return { ok: true, pid, content }
│ └── "cleanup":
│ ├── readdir("/tmp") → filter "sub-agent-{pid}.log"
│ ├── for each file:
│ │ ├── stat(filePath) → mtimeMs
│ │ ├── if age > maxAgeHours * 60 * 60 * 1000 → unlinkSync
│ │ └── removed++
│ └── return { ok: true, removed }
└── default → { ok: false, error: "Unknown action" }

isProcessRunning(pid):
├── process.kill(pid, 0) → true (signal 0 checks existence)
└── catch → false
```

**Log file pattern:** `sub-agent-{pid}.log` stored in `/tmp`. Files are automatically cleaned up by the `cleanup` action based on age threshold.

---

## Sub-Agent Message Tool Flow

**Entry:** `src/tools/subAgentMessage.js` → `createSubAgentMessageTool()`

```
subAgentMessage tool (requires process:spawn permission):
├── validate input: pid (required), message (required)
├── if pid missing → { ok: false, error: "PID is required" }
├── if message missing → { ok: false, error: "Message is required" }
├── lookup processTracker.get(pid):
│ ├── if not found → { ok: false, error: "Process {pid} not found in tracker" }
│ └── if status is "exited" or "error" → { ok: false, error: "Process {pid} is not running" }
├── entry.child.stdin.write(message + "\\n")
│ └── Append newline to message before writing
└── return { ok: true, pid, messageSent: true }
```

**Prerequisites:** The target subAgent process must be spawned with `stdio: ["pipe", "pipe", "pipe"]` (stdin exposed). The subAgent tool was updated to expose stdin for this to work.

---

## Additional Tool Flows

### Code Execution

Expand Down Expand Up @@ -1202,7 +1107,7 @@ index.js
│ ├── tools/moa.js → OPENROUTER_API_KEY — mixture-of-agents (4 parallel OpenRouter calls + aggregation)
│ ├── tools/cron.js → node:fs/promises — cron job CRUD operations
│ ├── tools/compactContext.js → @langchain/core, zod — automatic conversation context compaction on LLM 400 errors (tiered retention, retry loop, error detection)
── tools/subAgentLog.js → node:fs/promises, node:path — subAgent log management (list, read, cleanup); zero-permission, always registered
── tools/...
│ └── tools/...
├── sandbox/pathResolver.js → node:path
├── sandbox/urlFilter.js → node:url
Expand Down
68 changes: 5 additions & 63 deletions docs/OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,38 +126,15 @@ The agent runs: reason → call tool(s) → reason again → answer. Tool array

---

## Sub-Agent
## Deep Agents

`src/tools/subAgent.js` — spawns child processes (`node index.js --sub-agent --cwd=... --message="..."`) to execute prompts as independent sub-agents. Supports single execution and fan-out (parallel/sequential) modes with configurable concurrency, timeout, and error handling.
`src/agent/deepAgents.js` — Deep Agents orchestrator with a specialized coding agent. Uses middleware for filesystem, memory, skills, and summarization capabilities.

| File | Purpose |
|------|---------|
| `subAgent.js` | `createSubAgentTool()` — LangChain tool with marker-based stdout parsing; `parseSubAgentOutput()` — extracts structured results from sub-agent output; `escapeShellArg()` — handles quotes, backticks, dollar signs, newlines, tabs, carriage returns; `resolveTimeout()` — per-call > env var > config default priority; `spawnSubAgentProcess()` — spawns `node index.js --sub-agent --cwd=... --message="..."`, captures OS-level PID |

**Key features:**

1. **Single execution mode** — Spawn one sub-agent with delegation + context, return structured result
2. **Fan-out mode** — Parallel/sequential task execution with configurable `maxConcurrent` limit
3. **Marker-based stdout parsing** — `# SubAgent` marker for result extraction (mirrors compaction tool)
4. **Response contract** — `{ ok, result, error?, pid? }` matching compaction tool pattern
5. **Process tracking** — Shared `processTracker` from terminal.js for PID tracking and lifecycle management
6. **Timeout resolution** — Per-call > env var > config default priority
7. **Parameter extraction** — Optional `returnParams` for JSON result filtering with fallback
8. **Working directory** — `cwd` parameter passed to sub-agent process; all file operations resolved from this directory
9. **Shell escaping** — Handles quotes, backticks, dollar signs, newlines, tabs, carriage returns
10. **Error handling** — `continue` vs `fail-fast` strategies for fan-out batches
11. **OS-level PID tracking** — Captures the actual child process PID from `spawn()` for correlation with tracked processes

**Configuration:** Sub-agent parameters are set via `config.process.subAgent`:

| Key | Default | Description |
| --- | --- | --- |
| `process.subAgent.timeout` | `600000` | Sub-agent process timeout in milliseconds (default 10 minutes) |
| `process.subAgent.maxConcurrent` | `4` | Max concurrent sub-agent processes |
| `process.subAgent.sessionMode` | `isolated` | Session isolation mode (`isolated`, `forked`, `shared`) |
| `process.subAgent.defaultStrategy` | `parallel` | Default fan-out strategy (`parallel`, `sequential`) |
| `process.subAgent.defaultOnError` | `continue` | Default error handling strategy (`continue`, `fail-fast`) |
|------|---------|
| `deepAgents.js` | `createDeepAgentsOrchestrator()` — creates the Deep Agents orchestrator with coding and utility agents; loads per-project agent prompt configuration |

The orchestrator routes tasks automatically — the system prompt delegates every task to the orchestrator, which manages routing, state, and observability natively.
---


Expand All @@ -177,43 +154,8 @@ The agent runs: reason → call tool(s) → reason again → answer. Tool array
4. **Workspace rules** — Returns formatted workspace rules section for system prompt injection


## Sub-Agent Log

`src/tools/subAgentLog.js` — manages and reads subAgent log files stored in `/tmp`. Supports listing all active logs with PID and running status, reading a specific log by PID, and cleaning up old logs beyond a configurable age threshold.

| File | Purpose |
|------|---------|
| `subAgentLog.js` | `createSubAgentLogTool()` — LangChain tool with zero permissions (always registered); `listLogs()` — scans `/tmp` for `sub-agent-{pid}.log` files, returns sorted array with PID, file, size, modified time, and running status; `readLog(pid)` — reads a specific log file by PID; `cleanupLogs(maxAgeHours)` — removes logs older than the configured age threshold (default: 24 hours); `isProcessRunning(pid)` — checks if a PID is still active via `process.kill(pid, 0)` |

**Key features:**

1. **Log discovery** — Scans `/tmp` for files matching `sub-agent-{pid}.log` pattern
2. **Process status** — Reports whether each sub-agent process is still running
3. **Age-based cleanup** — Removes logs older than a configurable threshold (default: 24 hours)
4. **Zero permissions** — Always registered, no sandbox permissions required

**Configuration:** Log directory is hardcoded to `/tmp`. Age threshold is configurable via the `maxAgeHours` parameter (default: 24).

---


## Sub-Agent Message

`src/tools/subAgentMessage.js` — sends messages to running subAgent processes via stdin. Requires the target process to be tracked (spawned via subAgent tool) and have stdin exposed.

| File | Purpose |
|------|---------|
| `subAgentMessage.js` | `createSubAgentMessageTool()` — LangChain tool with `process:spawn` permission; `subAgentMessageImpl(input)` — looks up PID in `processTracker`, validates process is running, writes message to stdin |

**Key features:**

1. **Process lookup** — Validates PID exists in `processTracker`
2. **Status check** — Ensures process is still running before writing
3. **Stdin write** — Appends newline to message before writing to stdin
4. **Error handling** — Clear error messages for missing PID, missing message, process not found, or process not running

**Prerequisites:** The target subAgent process must be spawned with `stdio: ["pipe", "pipe", "pipe"]` (stdin exposed). The subAgent tool was updated to expose stdin for this to work.

---


Expand Down
2 changes: 1 addition & 1 deletion docs/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ license: MIT

Skills are stored in `skills/` and are version-controllable. Simple skills can be chained together into pipelines for complex multi-step processing, or composed by asking `madz` to coordinate between them.

**Built-in tools:** Beyond skills, `madz` ships with built-in tools for common tasks. The `subAgent` tool lets the agent spawn child-process agents to execute prompts as independent workers — supporting both single execution and fan-out modes (parallel or sequential) with configurable concurrency, timeout, and error handling. The `subAgentLog` tool manages and reads subAgent log files (list, read, cleanup). The `subAgentMessage` tool sends messages to running subAgent processes via stdin. The `scanAgents` tool scans for `AGENTS.md` workspace rules files in a target directory. Other built-in tools include filesystem operations, terminal execution, search, memory management, and more.
**Built-in tools:** Beyond skills, `madz` ships with built-in tools for common tasks. The Deep Agents orchestrator (`deepAgents` library) handles multi-agent routing natively — a coding-agent for code work. The `scanAgents` tool scans for `AGENTS.md` workspace rules files. Other built-in tools include filesystem operations, terminal execution, search, memory management, and more.

---

Expand Down
Loading
Loading