@swarmify/agents-mcp 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,21 +1,22 @@
1
1
  # @swarmify/agents-mcp
2
2
 
3
- Run subagents from any MCP client.
3
+ Run Subagents, Swarm or Ralph Wiggums from any MCP client.
4
4
 
5
- Spawn CLI agents in parallel so your main agent stays focused. Each subagent runs
6
- in its own context and can be polled for progress.
5
+ Spawn CLI agents in parallel so your main agent stays focused. Each subagent runs in its own context and can be polled for progress.
6
+
7
+ Homepage: https://swarmify.co
8
+ NPM: https://www.npmjs.com/package/@swarmify/agents-mcp
9
+ VS Code Extension: https://marketplace.visualstudio.com/items?itemName=swarmify.swarm-ext
7
10
 
8
11
  ## Why This Exists
9
12
 
10
- MCP adoption is accelerating and the ecosystem is organizing around registries,
11
- gateways, and enterprise governance. At the same time, teams are pushing toward
12
- multi-agent workflows and running into orchestration reliability, tool overload,
13
- and security concerns. This server gives you a focused, practical building block
14
- for real-world swarm work.
13
+ MCP adoption is accelerating and the ecosystem is organizing around registries, gateways, and enterprise governance. At the same time, teams are pushing toward multi-agent workflows and running into orchestration reliability, tool overload, and security concerns. This server gives you a focused, practical building block for real-world swarm work.
14
+
15
+ The API surface is intentionally small, closer to iconic commands like `cd`, `ls`, and `git` than a framework. Fewer knobs means less room for an orchestrator to make mistakes.
15
16
 
16
17
  ## What You Get
17
18
 
18
- - MCP server with `spawn`, `status`, `stop`, and `tasks` tools
19
+ - A dead simple MCP server with `Spawn`, `Status`, `Stop`, and `Tasks` tools
19
20
  - The `/swarm` command to teach an orchestrator how to distribute work
20
21
  - Plan vs edit modes that control file access per agent
21
22
  - Local logs and durable processes that survive IDE restarts
@@ -24,10 +25,13 @@ for real-world swarm work.
24
25
 
25
26
  ```bash
26
27
  # Claude Code
27
- claude mcp add swarmify-agents -- npx -y @swarmify/agents-mcp
28
+ claude mcp add --scope user Swarm -- npx -y @swarmify/agents-mcp
29
+
30
+ # Codex
31
+ codex mcp add swarm -- npx -y @swarmify/agents-mcp@latest
28
32
 
29
33
  # Gemini CLI
30
- gemini mcp add swarmify-agents -- npx -y @swarmify/agents-mcp
34
+ gemini mcp add Swarm -- npx -y @swarmify/agents-mcp
31
35
 
32
36
  # OpenCode (interactive)
33
37
  opencode mcp add
@@ -43,56 +47,54 @@ The server auto-discovers which agent CLIs you have installed.
43
47
  - Parallelize tasks across Claude, Codex, Gemini, and Cursor
44
48
  - Keep long-running loops responsive by offloading work to subagents
45
49
  - Integrate with MCP registries and gateways without changing your workflow
50
+ - Run Ralph Wiggum loops inside spawned Claude Code agents when the plugin is installed
46
51
 
47
52
  ## Under the Hood
48
53
 
49
- Headless CLI agents run as background processes. Output streams to
50
- `~/.swarmify/agents/{id}/stdout.log`.
54
+ Headless CLI agents run as background processes. Output streams to `~/.swarmify/agents/{id}/stdout.log`.
51
55
 
52
56
  Plan mode is read-only:
57
+
53
58
  - Claude: `--permission-mode plan`
54
59
  - Codex: sandboxed (no `--full-auto`)
55
60
  - Gemini and Cursor: no auto-approve flags
56
61
 
57
62
  Edit mode unlocks writes:
63
+
58
64
  - Codex: `--full-auto`
59
65
  - Claude: `acceptEdits`
60
66
  - Gemini: `--yolo`
61
67
  - Cursor: `-f`
62
68
 
63
- Agents are detached from the MCP server process. Close your IDE and reopen it,
64
- then reconnect with `status` and `tasks`.
69
+ Agents are detached from the MCP server process. Close your IDE and reopen it, then reconnect with `Status` and `Tasks`.
65
70
 
66
71
  ## Common Questions
67
72
 
68
- **"Isn't this overkill for simple tasks?"**
69
- Yes. For typo fixes or small changes, one agent is fine. But for implementing a complex feature across 20 files, a single agent drowning in context is slower than a focused swarm working in parallel.
73
+ **"Isn't this overkill for simple tasks?**"Yes. For typo fixes or small changes, one agent is fine. But for implementing a complex feature across 20 files, a single agent drowning in context is slower than a focused swarm working in parallel.
70
74
 
71
- **"What if agents conflict?"**
72
- They don't. The orchestrator (via `/swarm` command) assigns each agent specific files—no overlap. For shared files, it runs agents in sequential waves so there's no collision.
75
+ **"What if agents conflict?**"They don't. The orchestrator (via `/swarm` command) assigns each agent specific files—no overlap. For shared files, it runs agents in sequential waves so there's no collision.
73
76
 
74
- **"How do I monitor what's happening?"**
75
- Use the Swarm `status` tool to see what each agent is doing: files changed, commands run, last messages. No black box—full visibility into the swarm.
77
+ **"How do I monitor what's happening?**"Use the Swarm `Status` tool to see what each agent is doing: files changed, commands run, last messages. No black box—full visibility into the swarm.
76
78
 
77
79
  ## API Reference
78
80
 
79
81
  ### Tools
80
82
 
81
83
  | Tool | Description |
82
- |------|-------------|
83
- | `spawn` | Start an agent on a task. Returns immediately with agent ID. |
84
- | `status` | Get agent progress: files changed, commands run, last messages. |
85
- | `stop` | Stop one agent or all agents in a task. |
86
- | `tasks` | List all tasks with their agents and activity. |
84
+ | --- | --- |
85
+ | `Spawn` | Start an agent on a task. Returns immediately with agent ID. |
86
+ | `Status` | Get agent progress: files changed, commands run, last messages. |
87
+ | `Stop` | Stop one agent or all agents in a task. |
88
+ | `Tasks` | List all tasks with their agents and activity. |
87
89
 
88
- ### spawn
90
+ ### Spawn
89
91
 
90
92
  ```
91
- spawn(task_name, agent_type, prompt, mode?, cwd?, effort?)
93
+ Spawn(task_name, agent_type, prompt, mode?, cwd?, effort?)
92
94
  ```
93
95
 
94
96
  | Parameter | Required | Description |
95
- |-----------|----------|-------------|
97
+ | --- | --- | --- |
96
98
  | `task_name` | Yes | Groups related agents (e.g., "auth-feature") |
97
99
  | `agent_type` | Yes | `claude`, `codex`, `gemini`, or `cursor` |
98
100
  | `prompt` | Yes | The task for the agent |
@@ -100,54 +102,53 @@ spawn(task_name, agent_type, prompt, mode?, cwd?, effort?)
100
102
  | `cwd` | No | Working directory for the agent |
101
103
  | `effort` | No | `fast`, `default` (implicit), or `detailed` for max-capability models |
102
104
 
103
- ### status
105
+ ### Status
104
106
 
105
107
  ```
106
- status(task_name, filter?)
108
+ Status(task_name, filter?)
107
109
  ```
108
110
 
109
111
  | Parameter | Required | Description |
110
- |-----------|----------|-------------|
112
+ | --- | --- | --- |
111
113
  | `task_name` | Yes | Task to check |
112
114
  | `filter` | No | `running` (default), `completed`, `failed`, `stopped`, or `all` |
113
115
 
114
- Returns files created/modified/read/deleted, bash commands executed, and last
115
- messages.
116
+ Returns files created/modified/read/deleted, bash commands executed, and last messages.
116
117
 
117
- ### stop
118
+ ### Stop
118
119
 
119
120
  ```
120
- stop(task_name, agent_id?)
121
+ Stop(task_name, agent_id?)
121
122
  ```
122
123
 
123
124
  Stop all agents in a task, or a specific agent by ID.
124
125
 
125
- ### tasks
126
+ ### Tasks
126
127
 
127
128
  ```
128
- tasks(limit?)
129
+ Tasks(limit?)
129
130
  ```
130
131
 
131
- List all tasks sorted by most recent activity.
132
+ List all tasks sorted by most recent activity. Defaults to 10 tasks when limit is omitted.
132
133
 
133
134
  ## Token Optimization
134
135
 
135
136
  This server is designed to minimize token usage for the calling agent:
136
137
 
137
138
  | Optimization | Benefit |
138
- |--------------|---------|
139
+ | --- | --- |
139
140
  | Status defaults to `filter='running'` | Only shows active agents, not completed history |
140
141
  | Bash commands truncated to 120 chars | Heredocs collapsed to `cat <<EOF > path` |
141
142
  | Last 5 messages only | Not full conversation history |
142
143
  | File operations deduplicated | Sets of created/modified/read/deleted paths |
143
- | Spawn returns immediately | No blocking, poll with status later |
144
+ | Spawn returns immediately | No blocking, poll with Status later |
144
145
 
145
146
  ## Supported Agents
146
147
 
147
148
  The server auto-discovers installed CLIs at startup:
148
149
 
149
150
  | Agent | CLI | Best For |
150
- |-------|-----|----------|
151
+ | --- | --- | --- |
151
152
  | Claude | `claude` | Best for complex research and open-ended exploration |
152
153
  | Codex | `codex` | Fast, cheap. Self-contained features |
153
154
  | Gemini | `gemini` | Complex multi-system features, architectural changes |
@@ -156,7 +157,7 @@ The server auto-discovers installed CLIs at startup:
156
157
  ## Modes
157
158
 
158
159
  | Mode | File Access | Auto-loops? | Use Case |
159
- |------|-------------|-------------|----------|
160
+ | --- | --- | --- | --- |
160
161
  | `plan` | Read-only | No | Research, exploration, code review |
161
162
  | `edit` | Read + Write | No | Implementation, refactoring, fixes |
162
163
  | `ralph` | Full yolo | Yes | Autonomous iteration through RALPH.md tasks |
@@ -196,16 +197,18 @@ Cover auth endpoints.
196
197
  ```
197
198
 
198
199
  **How it works:**
200
+
199
201
  1. Create a `RALPH.md` file in your project directory with tasks
200
- 2. Call `spawn(mode='ralph', cwd='./my-project', prompt='Build the auth system')`
202
+ 2. Call `Spawn(mode='ralph', cwd='./my-project', prompt='Build the auth system')`
201
203
  3. MCP spawns ONE agent with full permissions
202
204
  4. Agent reads RALPH.md, understands the system, picks tasks logically
203
205
  5. For each task: completes work, marks checkbox `## [x]`, adds update
204
- 6. Continues until all tasks checked (or you stop it with `stop` tool)
206
+ 6. Continues until all tasks checked (or you stop it with `Stop` tool)
205
207
 
206
208
  **Multiple ralph agents:** You can spawn multiple ralph agents in parallel for different directories or different RALPH.md files. The orchestrator controls this.
207
209
 
208
210
  **Safety:**
211
+
209
212
  - Scoped by `cwd` - orchestrator controls blast radius
210
213
  - RALPH.md must exist before spawn
211
214
  - Warns if used in home/system directories
@@ -214,18 +217,60 @@ Cover auth endpoints.
214
217
  ## Effort Levels
215
218
 
216
219
  | Level | Models Used |
217
- |-------|-------------|
220
+ | --- | --- |
218
221
  | `fast` | codex: gpt-5.2-codex, claude: claude-haiku-4-5, gemini: gemini-3-flash, cursor: composer-1 |
219
222
  | `default` | codex: gpt-5.2-codex, claude: claude-sonnet-4-5, gemini: gemini-3-flash, cursor: composer-1 |
220
223
  | `detailed` | codex: gpt-5.1-codex-max, claude: claude-opus-4-5, gemini: gemini-3-pro, cursor: composer-1 |
221
224
 
225
+ ## Config
226
+
227
+ Config lives at `~/.swarmify/config.json`.
228
+
229
+ Example with per-agent swarm selection and effort model overrides:
230
+
231
+ ```json
232
+ {
233
+ "agents": {
234
+ "claude": {
235
+ "swarm": true,
236
+ "effort": {
237
+ "models": {
238
+ "fast": "claude-haiku-4-5-20251001",
239
+ "default": "claude-sonnet-4-5",
240
+ "detailed": "claude-opus-4-5"
241
+ }
242
+ }
243
+ },
244
+ "codex": { "swarm": true },
245
+ "gemini": { "swarm": true },
246
+ "cursor": { "swarm": false }
247
+ }
248
+ }
249
+ ```
250
+
251
+ Shorthand for a single effort level override:
252
+
253
+ ```json
254
+ {
255
+ "agents": {
256
+ "claude": {
257
+ "swarm": true,
258
+ "effort": {
259
+ "level": "fast",
260
+ "model": "claude-haiku-4-5-20251001"
261
+ }
262
+ }
263
+ }
264
+ }
265
+ ```
266
+
222
267
  ## Environment Variables
223
268
 
224
269
  | Variable | Description |
225
- |----------|-------------|
226
- | `AGENT_SWARM_DEFAULT_MODE` | Set default mode (`plan` or `edit`) |
227
- | `AGENTS_SWARM_RALPH_FILE` | Task file name for ralph mode (default: `RALPH.md`) |
228
- | `AGENTS_SWARM_DISABLE_RALPH` | Set to `true` or `1` to disable ralph mode |
270
+ | --- | --- |
271
+ | `AGENTS_MCP_DEFAULT_MODE` | Set default mode (`plan` or `edit`) |
272
+ | `AGENTS_MCP_RALPH_FILE` | Task file name for ralph mode (default: `RALPH.md`) |
273
+ | `AGENTS_MCP_DISABLE_RALPH` | Set to `true` or `1` to disable ralph mode |
229
274
 
230
275
  ## Storage
231
276
 
package/dist/agents.d.ts CHANGED
@@ -1,4 +1,11 @@
1
+ import { type ModelOverrides } from './persistence.js';
1
2
  import { AgentType } from './parsers.js';
3
+ /**
4
+ * Compute the Lowest Common Ancestor (LCA) of multiple file paths.
5
+ * Returns the deepest common directory shared by all paths.
6
+ * Returns null if paths is empty or paths have no common ancestor (different roots).
7
+ */
8
+ export declare function computePathLCA(paths: string[]): string | null;
2
9
  export declare enum AgentStatus {
3
10
  RUNNING = "running",
4
11
  COMPLETED = "completed",
@@ -8,7 +15,9 @@ export declare enum AgentStatus {
8
15
  export type { AgentType } from './parsers.js';
9
16
  export declare const AGENT_COMMANDS: Record<AgentType, string[]>;
10
17
  export type EffortLevel = 'fast' | 'default' | 'detailed';
11
- export declare const EFFORT_MODEL_MAP: Record<EffortLevel, Record<AgentType, string>>;
18
+ export type EffortModelMap = Record<EffortLevel, Record<AgentType, string>>;
19
+ export declare const EFFORT_MODEL_MAP: EffortModelMap;
20
+ export declare function resolveEffortModelMap(base: EffortModelMap, overrides: ModelOverrides | null | undefined): EffortModelMap;
12
21
  declare const VALID_MODES: readonly ["plan", "edit", "ralph"];
13
22
  type Mode = typeof VALID_MODES[number];
14
23
  export declare function resolveMode(requestedMode: string | null | undefined, defaultMode?: Mode): Mode;
@@ -25,15 +34,17 @@ export declare class AgentProcess {
25
34
  agentType: AgentType;
26
35
  prompt: string;
27
36
  cwd: string | null;
37
+ workspaceDir: string | null;
28
38
  mode: Mode;
29
39
  pid: number | null;
30
40
  status: AgentStatus;
31
41
  startedAt: Date;
32
42
  completedAt: Date | null;
43
+ parentSessionId: string | null;
33
44
  private eventsCache;
34
45
  private lastReadPos;
35
46
  private baseDir;
36
- constructor(agentId: string, taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode, pid?: number | null, status?: AgentStatus, startedAt?: Date, completedAt?: Date | null, baseDir?: string | null);
47
+ constructor(agentId: string, taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode, pid?: number | null, status?: AgentStatus, startedAt?: Date, completedAt?: Date | null, baseDir?: string | null, parentSessionId?: string | null, workspaceDir?: string | null);
37
48
  get isEditMode(): boolean;
38
49
  getAgentDir(): Promise<string>;
39
50
  getStdoutPath(): Promise<string>;
@@ -61,12 +72,14 @@ export declare class AgentManager {
61
72
  private filterByCwd;
62
73
  private cleanupAgeDays;
63
74
  private defaultMode;
75
+ private effortModelMap;
64
76
  private constructorAgentsDir;
65
- constructor(maxAgents?: number, maxConcurrent?: number, agentsDir?: string | null, defaultMode?: Mode | null, filterByCwd?: string | null, cleanupAgeDays?: number);
77
+ constructor(maxAgents?: number, maxConcurrent?: number, agentsDir?: string | null, defaultMode?: Mode | null, filterByCwd?: string | null, cleanupAgeDays?: number, modelOverrides?: ModelOverrides | null);
66
78
  private initialize;
67
79
  getDefaultMode(): Mode;
80
+ setModelOverrides(overrides: ModelOverrides | null | undefined): void;
68
81
  private loadExistingAgents;
69
- spawn(taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode | null, effort?: EffortLevel): Promise<AgentProcess>;
82
+ spawn(taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode | null, effort?: EffortLevel, parentSessionId?: string | null, workspaceDir?: string | null): Promise<AgentProcess>;
70
83
  private buildCommand;
71
84
  private applyEditMode;
72
85
  private applyRalphMode;
@@ -75,6 +88,7 @@ export declare class AgentManager {
75
88
  listRunning(): Promise<AgentProcess[]>;
76
89
  listCompleted(): Promise<AgentProcess[]>;
77
90
  listByTask(taskName: string): Promise<AgentProcess[]>;
91
+ listByParentSession(parentSessionId: string): Promise<AgentProcess[]>;
78
92
  stopByTask(taskName: string): Promise<{
79
93
  stopped: string[];
80
94
  alreadyStopped: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
package/dist/agents.js CHANGED
@@ -5,6 +5,43 @@ import * as os from 'os';
5
5
  import { randomUUID } from 'crypto';
6
6
  import { resolveAgentsDir } from './persistence.js';
7
7
  import { normalizeEvents } from './parsers.js';
8
+ /**
9
+ * Compute the Lowest Common Ancestor (LCA) of multiple file paths.
10
+ * Returns the deepest common directory shared by all paths.
11
+ * Returns null if paths is empty or paths have no common ancestor (different roots).
12
+ */
13
+ export function computePathLCA(paths) {
14
+ const validPaths = paths.filter(p => p && p.trim());
15
+ if (validPaths.length === 0)
16
+ return null;
17
+ if (validPaths.length === 1)
18
+ return validPaths[0];
19
+ // Normalize and split all paths into segments
20
+ const splitPaths = validPaths.map(p => {
21
+ const normalized = path.resolve(p);
22
+ // Split by path separator, filter empty segments
23
+ return normalized.split(path.sep).filter(seg => seg);
24
+ });
25
+ // Find minimum length
26
+ const minLen = Math.min(...splitPaths.map(p => p.length));
27
+ // Find common prefix
28
+ const commonSegments = [];
29
+ for (let i = 0; i < minLen; i++) {
30
+ const segment = splitPaths[0][i];
31
+ const allMatch = splitPaths.every(p => p[i] === segment);
32
+ if (allMatch) {
33
+ commonSegments.push(segment);
34
+ }
35
+ else {
36
+ break;
37
+ }
38
+ }
39
+ if (commonSegments.length === 0)
40
+ return null;
41
+ // Reconstruct path (add leading separator for absolute paths)
42
+ const lca = path.sep + commonSegments.join(path.sep);
43
+ return lca;
44
+ }
8
45
  export var AgentStatus;
9
46
  (function (AgentStatus) {
10
47
  AgentStatus["RUNNING"] = "running";
@@ -43,6 +80,32 @@ export const EFFORT_MODEL_MAP = {
43
80
  cursor: 'composer-1',
44
81
  },
45
82
  };
83
+ export function resolveEffortModelMap(base, overrides) {
84
+ const resolved = {
85
+ fast: { ...base.fast },
86
+ default: { ...base.default },
87
+ detailed: { ...base.detailed },
88
+ };
89
+ if (!overrides)
90
+ return resolved;
91
+ for (const [agentType, effortOverrides] of Object.entries(overrides)) {
92
+ if (!effortOverrides)
93
+ continue;
94
+ if (!['codex', 'gemini', 'cursor', 'claude'].includes(agentType))
95
+ continue;
96
+ const typedAgent = agentType;
97
+ for (const level of ['fast', 'default', 'detailed']) {
98
+ const model = effortOverrides[level];
99
+ if (typeof model === 'string') {
100
+ const trimmed = model.trim();
101
+ if (trimmed) {
102
+ resolved[level][typedAgent] = trimmed;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ return resolved;
108
+ }
46
109
  // Suffix appended to all prompts to ensure agents provide a summary
47
110
  const PROMPT_SUFFIX = `
48
111
 
@@ -65,7 +128,7 @@ function normalizeModeValue(modeValue) {
65
128
  return null;
66
129
  }
67
130
  function defaultModeFromEnv() {
68
- for (const envVar of ['AGENT_SWARM_MODE', 'AGENT_SWARM_DEFAULT_MODE']) {
131
+ for (const envVar of ['AGENTS_MCP_MODE', 'AGENTS_MCP_DEFAULT_MODE']) {
69
132
  const rawValue = process.env[envVar];
70
133
  const parsed = normalizeModeValue(rawValue);
71
134
  if (parsed) {
@@ -77,6 +140,49 @@ function defaultModeFromEnv() {
77
140
  }
78
141
  return 'plan';
79
142
  }
143
+ function coerceDate(value) {
144
+ if (value === null || value === undefined)
145
+ return null;
146
+ if (typeof value === 'number' && Number.isFinite(value)) {
147
+ const ms = value < 1e12 ? value * 1000 : value;
148
+ const date = new Date(ms);
149
+ return Number.isNaN(date.getTime()) ? null : date;
150
+ }
151
+ if (typeof value === 'string') {
152
+ const trimmed = value.trim();
153
+ if (!trimmed)
154
+ return null;
155
+ const numeric = Number(trimmed);
156
+ if (!Number.isNaN(numeric)) {
157
+ const ms = numeric < 1e12 ? numeric * 1000 : numeric;
158
+ const date = new Date(ms);
159
+ if (!Number.isNaN(date.getTime()))
160
+ return date;
161
+ }
162
+ const date = new Date(trimmed);
163
+ return Number.isNaN(date.getTime()) ? null : date;
164
+ }
165
+ return null;
166
+ }
167
+ function extractTimestamp(raw) {
168
+ if (!raw || typeof raw !== 'object')
169
+ return null;
170
+ const candidates = [
171
+ raw.timestamp,
172
+ raw.time,
173
+ raw.created_at,
174
+ raw.createdAt,
175
+ raw.ts,
176
+ raw.started_at,
177
+ raw.startedAt,
178
+ ];
179
+ for (const candidate of candidates) {
180
+ const date = coerceDate(candidate);
181
+ if (date)
182
+ return date;
183
+ }
184
+ return null;
185
+ }
80
186
  export function resolveMode(requestedMode, defaultMode = 'plan') {
81
187
  const normalizedDefault = normalizeModeValue(defaultMode);
82
188
  if (!normalizedDefault) {
@@ -131,26 +237,30 @@ export class AgentProcess {
131
237
  agentType;
132
238
  prompt;
133
239
  cwd;
240
+ workspaceDir;
134
241
  mode = 'plan';
135
242
  pid = null;
136
243
  status = AgentStatus.RUNNING;
137
244
  startedAt = new Date();
138
245
  completedAt = null;
246
+ parentSessionId = null;
139
247
  eventsCache = [];
140
248
  lastReadPos = 0;
141
249
  baseDir = null;
142
- constructor(agentId, taskName, agentType, prompt, cwd = null, mode = 'plan', pid = null, status = AgentStatus.RUNNING, startedAt = new Date(), completedAt = null, baseDir = null) {
250
+ constructor(agentId, taskName, agentType, prompt, cwd = null, mode = 'plan', pid = null, status = AgentStatus.RUNNING, startedAt = new Date(), completedAt = null, baseDir = null, parentSessionId = null, workspaceDir = null) {
143
251
  this.agentId = agentId;
144
252
  this.taskName = taskName;
145
253
  this.agentType = agentType;
146
254
  this.prompt = prompt;
147
255
  this.cwd = cwd;
256
+ this.workspaceDir = workspaceDir;
148
257
  this.mode = mode;
149
258
  this.pid = pid;
150
259
  this.status = status;
151
260
  this.startedAt = startedAt;
152
261
  this.completedAt = completedAt;
153
262
  this.baseDir = baseDir;
263
+ this.parentSessionId = parentSessionId;
154
264
  }
155
265
  get isEditMode() {
156
266
  return this.mode === 'edit';
@@ -176,6 +286,8 @@ export class AgentProcess {
176
286
  event_count: this.events.length,
177
287
  duration: this.duration(),
178
288
  mode: this.mode,
289
+ parent_session_id: this.parentSessionId,
290
+ workspace_dir: this.workspaceDir,
179
291
  };
180
292
  }
181
293
  duration() {
@@ -225,6 +337,7 @@ export class AgentProcess {
225
337
  const stats = await fs.stat(stdoutPath).catch(() => null);
226
338
  if (!stats)
227
339
  return;
340
+ const fallbackTimestamp = (stats.mtime || new Date()).toISOString();
228
341
  const fd = await fs.open(stdoutPath, 'r');
229
342
  const buffer = Buffer.alloc(1024 * 1024);
230
343
  const { bytesRead } = await fd.read(buffer, 0, buffer.length, this.lastReadPos);
@@ -238,7 +351,9 @@ export class AgentProcess {
238
351
  try {
239
352
  const rawEvent = JSON.parse(line);
240
353
  const events = normalizeEvents(this.agentType, rawEvent);
354
+ const resolvedTimestamp = extractTimestamp(rawEvent)?.toISOString() || fallbackTimestamp;
241
355
  for (const event of events) {
356
+ event.timestamp = resolvedTimestamp;
242
357
  this.eventsCache.push(event);
243
358
  if (event.type === 'result' || event.type === 'turn.completed' || event.type === 'thread.completed') {
244
359
  if (event.status === 'success' || event.type === 'turn.completed') {
@@ -256,7 +371,7 @@ export class AgentProcess {
256
371
  this.eventsCache.push({
257
372
  type: 'raw',
258
373
  content: line,
259
- timestamp: new Date().toISOString(),
374
+ timestamp: fallbackTimestamp,
260
375
  });
261
376
  }
262
377
  }
@@ -274,11 +389,13 @@ export class AgentProcess {
274
389
  agent_type: this.agentType,
275
390
  prompt: this.prompt,
276
391
  cwd: this.cwd,
392
+ workspace_dir: this.workspaceDir,
277
393
  mode: this.mode,
278
394
  pid: this.pid,
279
395
  status: this.status,
280
396
  started_at: this.startedAt.toISOString(),
281
397
  completed_at: this.completedAt?.toISOString() || null,
398
+ parent_session_id: this.parentSessionId,
282
399
  };
283
400
  const metaPath = await this.getMetaPath();
284
401
  await fs.writeFile(metaPath, JSON.stringify(meta, null, 2));
@@ -296,7 +413,7 @@ export class AgentProcess {
296
413
  try {
297
414
  const metaContent = await fs.readFile(metaPath, 'utf-8');
298
415
  const meta = JSON.parse(metaContent);
299
- const agent = new AgentProcess(meta.agent_id, meta.task_name || 'default', meta.agent_type, meta.prompt, meta.cwd || null, meta.mode === 'edit' ? 'edit' : 'plan', meta.pid || null, AgentStatus[meta.status] || AgentStatus.RUNNING, new Date(meta.started_at), meta.completed_at ? new Date(meta.completed_at) : null, baseDir);
416
+ const agent = new AgentProcess(meta.agent_id, meta.task_name || 'default', meta.agent_type, meta.prompt, meta.cwd || null, meta.mode === 'edit' ? 'edit' : 'plan', meta.pid || null, AgentStatus[meta.status] || AgentStatus.RUNNING, new Date(meta.started_at), meta.completed_at ? new Date(meta.completed_at) : null, baseDir, meta.parent_session_id || null, meta.workspace_dir || null);
300
417
  return agent;
301
418
  }
302
419
  catch {
@@ -321,11 +438,11 @@ export class AgentProcess {
321
438
  await this.readNewEvents();
322
439
  return;
323
440
  }
324
- const fallbackCompletion = this.getLatestEventTime() || this.startedAt || new Date();
325
441
  if (this.status === AgentStatus.RUNNING) {
326
442
  const exitCode = await this.reapProcess();
327
443
  await this.readNewEvents();
328
444
  if (this.status === AgentStatus.RUNNING) {
445
+ const fallbackCompletion = this.getLatestEventTime() || this.startedAt || new Date();
329
446
  if (exitCode !== null && exitCode !== 0) {
330
447
  this.status = AgentStatus.FAILED;
331
448
  }
@@ -336,6 +453,8 @@ export class AgentProcess {
336
453
  }
337
454
  }
338
455
  else if (!this.completedAt) {
456
+ await this.readNewEvents();
457
+ const fallbackCompletion = this.getLatestEventTime() || this.startedAt || new Date();
339
458
  this.completedAt = fallbackCompletion;
340
459
  }
341
460
  await this.saveMeta();
@@ -360,8 +479,9 @@ export class AgentManager {
360
479
  filterByCwd;
361
480
  cleanupAgeDays;
362
481
  defaultMode;
482
+ effortModelMap = EFFORT_MODEL_MAP;
363
483
  constructorAgentsDir = null;
364
- constructor(maxAgents = 50, maxConcurrent = 10, agentsDir = null, defaultMode = null, filterByCwd = null, cleanupAgeDays = 7) {
484
+ constructor(maxAgents = 50, maxConcurrent = 10, agentsDir = null, defaultMode = null, filterByCwd = null, cleanupAgeDays = 7, modelOverrides = null) {
365
485
  this.maxAgents = maxAgents;
366
486
  this.maxConcurrent = maxConcurrent;
367
487
  this.constructorAgentsDir = agentsDir;
@@ -372,6 +492,7 @@ export class AgentManager {
372
492
  throw new Error(`Invalid default_mode '${defaultMode}'. Use 'plan' or 'edit'.`);
373
493
  }
374
494
  this.defaultMode = resolvedDefaultMode;
495
+ this.effortModelMap = resolveEffortModelMap(EFFORT_MODEL_MAP, modelOverrides);
375
496
  this.initialize();
376
497
  }
377
498
  async initialize() {
@@ -382,6 +503,9 @@ export class AgentManager {
382
503
  getDefaultMode() {
383
504
  return this.defaultMode;
384
505
  }
506
+ setModelOverrides(overrides) {
507
+ this.effortModelMap = resolveEffortModelMap(EFFORT_MODEL_MAP, overrides);
508
+ }
385
509
  async loadExistingAgents() {
386
510
  try {
387
511
  await fs.access(this.agentsDir);
@@ -432,11 +556,11 @@ export class AgentManager {
432
556
  }
433
557
  console.error(`Loaded ${loadedCount} agents from disk`);
434
558
  }
435
- async spawn(taskName, agentType, prompt, cwd = null, mode = null, effort = 'default') {
559
+ async spawn(taskName, agentType, prompt, cwd = null, mode = null, effort = 'default', parentSessionId = null, workspaceDir = null) {
436
560
  await this.initialize();
437
561
  const resolvedMode = resolveMode(mode, this.defaultMode);
438
562
  // Resolve model from effort level
439
- const resolvedModel = EFFORT_MODEL_MAP[effort][agentType];
563
+ const resolvedModel = this.effortModelMap[effort][agentType];
440
564
  const running = await this.listRunning();
441
565
  if (running.length >= this.maxConcurrent) {
442
566
  throw new Error(`Maximum concurrent agents (${this.maxConcurrent}) reached. Wait for an agent to complete or stop one first.`);
@@ -459,7 +583,7 @@ export class AgentManager {
459
583
  }
460
584
  const agentId = randomUUID().substring(0, 8);
461
585
  const cmd = this.buildCommand(agentType, prompt, resolvedMode, resolvedModel, resolvedCwd);
462
- const agent = new AgentProcess(agentId, taskName, agentType, prompt, resolvedCwd, resolvedMode, null, AgentStatus.RUNNING, new Date(), null, this.agentsDir);
586
+ const agent = new AgentProcess(agentId, taskName, agentType, prompt, resolvedCwd, resolvedMode, null, AgentStatus.RUNNING, new Date(), null, this.agentsDir, parentSessionId, workspaceDir);
463
587
  const agentDir = await agent.getAgentDir();
464
588
  try {
465
589
  await fs.mkdir(agentDir, { recursive: true });
@@ -618,6 +742,10 @@ export class AgentManager {
618
742
  const all = await this.listAll();
619
743
  return all.filter(a => a.taskName === taskName);
620
744
  }
745
+ async listByParentSession(parentSessionId) {
746
+ const all = await this.listAll();
747
+ return all.filter(a => a.parentSessionId === parentSessionId);
748
+ }
621
749
  async stopByTask(taskName) {
622
750
  const agents = await this.listByTask(taskName);
623
751
  const stopped = [];