mcp-codex-subagent 2.0.8 → 2.0.10

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.
Files changed (111) hide show
  1. package/dist/event/bus.d.ts +53 -0
  2. package/dist/event/bus.d.ts.map +1 -0
  3. package/dist/event/bus.js +94 -0
  4. package/dist/event/bus.js.map +1 -0
  5. package/dist/event/throttle.d.ts +36 -0
  6. package/dist/event/throttle.d.ts.map +1 -0
  7. package/dist/event/throttle.js +66 -0
  8. package/dist/event/throttle.js.map +1 -0
  9. package/dist/index.js +32 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/process/event-parser.d.ts +37 -0
  12. package/dist/process/event-parser.d.ts.map +1 -0
  13. package/dist/process/event-parser.js +141 -0
  14. package/dist/process/event-parser.js.map +1 -0
  15. package/dist/process/runner.d.ts +48 -0
  16. package/dist/process/runner.d.ts.map +1 -0
  17. package/dist/process/runner.js +227 -0
  18. package/dist/process/runner.js.map +1 -0
  19. package/dist/process/types.d.ts +74 -0
  20. package/dist/process/types.d.ts.map +1 -0
  21. package/dist/process/types.js +5 -0
  22. package/dist/process/types.js.map +1 -0
  23. package/dist/server.d.ts.map +1 -1
  24. package/dist/server.js +128 -36
  25. package/dist/server.js.map +1 -1
  26. package/dist/services/account-rotator.d.ts +28 -0
  27. package/dist/services/account-rotator.d.ts.map +1 -0
  28. package/dist/services/account-rotator.js +216 -0
  29. package/dist/services/account-rotator.js.map +1 -0
  30. package/dist/services/output-file.d.ts.map +1 -1
  31. package/dist/services/output-file.js +80 -36
  32. package/dist/services/output-file.js.map +1 -1
  33. package/dist/services/task-manager.d.ts +6 -0
  34. package/dist/services/task-manager.d.ts.map +1 -1
  35. package/dist/services/task-manager.js +24 -1
  36. package/dist/services/task-manager.js.map +1 -1
  37. package/dist/services/template-init.d.ts +10 -0
  38. package/dist/services/template-init.d.ts.map +1 -0
  39. package/dist/services/template-init.js +41 -0
  40. package/dist/services/template-init.js.map +1 -0
  41. package/dist/session/file-storage.d.ts +27 -0
  42. package/dist/session/file-storage.d.ts.map +1 -0
  43. package/dist/session/file-storage.js +281 -0
  44. package/dist/session/file-storage.js.map +1 -0
  45. package/dist/session/storage.js +1 -1
  46. package/dist/session/storage.js.map +1 -1
  47. package/dist/task/state-machine.d.ts +27 -0
  48. package/dist/task/state-machine.d.ts.map +1 -0
  49. package/dist/task/state-machine.js +59 -0
  50. package/dist/task/state-machine.js.map +1 -0
  51. package/dist/task/store.d.ts +91 -0
  52. package/dist/task/store.d.ts.map +1 -0
  53. package/dist/task/store.js +317 -0
  54. package/dist/task/store.js.map +1 -0
  55. package/dist/task/types.d.ts +72 -0
  56. package/dist/task/types.d.ts.map +1 -0
  57. package/dist/task/types.js +13 -0
  58. package/dist/task/types.js.map +1 -0
  59. package/dist/templates/index.d.ts +16 -0
  60. package/dist/templates/index.d.ts.map +1 -1
  61. package/dist/templates/index.js +57 -5
  62. package/dist/templates/index.js.map +1 -1
  63. package/dist/tools/definitions.d.ts +5 -1
  64. package/dist/tools/definitions.d.ts.map +1 -1
  65. package/dist/tools/definitions.js +253 -179
  66. package/dist/tools/definitions.js.map +1 -1
  67. package/dist/tools/description-builder.d.ts +18 -0
  68. package/dist/tools/description-builder.d.ts.map +1 -0
  69. package/dist/tools/description-builder.js +88 -0
  70. package/dist/tools/description-builder.js.map +1 -0
  71. package/dist/tools/handlers.d.ts +19 -17
  72. package/dist/tools/handlers.d.ts.map +1 -1
  73. package/dist/tools/handlers.js +287 -341
  74. package/dist/tools/handlers.js.map +1 -1
  75. package/dist/types.d.ts +5 -12
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/types.js +7 -10
  78. package/dist/types.js.map +1 -1
  79. package/dist/utils/ring-buffer.d.ts +41 -0
  80. package/dist/utils/ring-buffer.d.ts.map +1 -0
  81. package/dist/utils/ring-buffer.js +83 -0
  82. package/dist/utils/ring-buffer.js.map +1 -0
  83. package/dist/wave/dag.d.ts +32 -0
  84. package/dist/wave/dag.d.ts.map +1 -0
  85. package/dist/wave/dag.js +186 -0
  86. package/dist/wave/dag.js.map +1 -0
  87. package/dist/wave/git.d.ts +57 -0
  88. package/dist/wave/git.d.ts.map +1 -0
  89. package/dist/wave/git.js +227 -0
  90. package/dist/wave/git.js.map +1 -0
  91. package/dist/wave/orchestrator.d.ts +15 -0
  92. package/dist/wave/orchestrator.d.ts.map +1 -0
  93. package/dist/wave/orchestrator.js +565 -0
  94. package/dist/wave/orchestrator.js.map +1 -0
  95. package/dist/wave/progress.d.ts +51 -0
  96. package/dist/wave/progress.d.ts.map +1 -0
  97. package/dist/wave/progress.js +176 -0
  98. package/dist/wave/progress.js.map +1 -0
  99. package/dist/wave/registry.d.ts +66 -0
  100. package/dist/wave/registry.d.ts.map +1 -0
  101. package/dist/wave/registry.js +340 -0
  102. package/dist/wave/registry.js.map +1 -0
  103. package/dist/wave/semaphore.d.ts +42 -0
  104. package/dist/wave/semaphore.d.ts.map +1 -0
  105. package/dist/wave/semaphore.js +119 -0
  106. package/dist/wave/semaphore.js.map +1 -0
  107. package/dist/wave/types.d.ts +197 -0
  108. package/dist/wave/types.d.ts.map +1 -0
  109. package/dist/wave/types.js +147 -0
  110. package/dist/wave/types.js.map +1 -0
  111. package/package.json +15 -15
@@ -0,0 +1,227 @@
1
+ /**
2
+ * CodexProcessRunner — owns the lifecycle of one codex child process.
3
+ *
4
+ * Responsibilities:
5
+ * - Spawn the codex process
6
+ * - Line-buffer stdout for JSONL parsing
7
+ * - Filter stderr noise
8
+ * - Emit normalized events via callbacks
9
+ * - Deterministic cleanup: SIGTERM → grace → SIGKILL → remove listeners
10
+ *
11
+ * Single-use: one runner per task, created once, not reused.
12
+ */
13
+ import { spawn } from 'child_process';
14
+ import { parseCodexEvent, isStderrNoise, createStderrEvent, parseEventString, } from './event-parser.js';
15
+ const DEFAULT_KILL_GRACE_MS = 5000;
16
+ export class CodexProcessRunner {
17
+ child = null;
18
+ _state = 'spawning';
19
+ stdoutBuffer = '';
20
+ killGraceMs;
21
+ callbacks;
22
+ exitPromise = null;
23
+ exitResolve = null;
24
+ wasKilledByUs = false;
25
+ constructor(callbacks, killGraceMs) {
26
+ this.callbacks = callbacks;
27
+ this.killGraceMs = killGraceMs ?? DEFAULT_KILL_GRACE_MS;
28
+ }
29
+ get state() {
30
+ return this._state;
31
+ }
32
+ /**
33
+ * Spawn the codex process. Returns a ProcessHandle for PID access
34
+ * and kill control.
35
+ */
36
+ spawn(options) {
37
+ const isWindows = process.platform === 'win32';
38
+ const child = spawn('codex', options.args, {
39
+ shell: isWindows,
40
+ cwd: options.cwd,
41
+ env: options.env ? { ...process.env, ...options.env } : process.env,
42
+ stdio: ['pipe', 'pipe', 'pipe'],
43
+ detached: false,
44
+ });
45
+ this.child = child;
46
+ // Create exit promise for awaiting process termination
47
+ this.exitPromise = new Promise((resolve) => {
48
+ this.exitResolve = resolve;
49
+ });
50
+ // Wire stdout: line-buffered JSONL parsing
51
+ child.stdout?.on('data', this.onStdout);
52
+ // Wire stderr: noise-filtered
53
+ child.stderr?.on('data', this.onStderr);
54
+ // Wire close: cleanup + exit notification
55
+ child.on('close', this.onClose);
56
+ // Wire error: spawn failure
57
+ child.on('error', this.onError);
58
+ // Transition to running once PID is assigned
59
+ if (child.pid) {
60
+ this._state = 'running';
61
+ }
62
+ else {
63
+ // PID may be assigned asynchronously; check on next tick
64
+ child.on('spawn', () => {
65
+ if (this._state === 'spawning') {
66
+ this._state = 'running';
67
+ }
68
+ });
69
+ }
70
+ const self = this;
71
+ return {
72
+ get pid() {
73
+ return child.pid;
74
+ },
75
+ childProcess: child,
76
+ get state() {
77
+ return self._state;
78
+ },
79
+ kill: () => self.kill(),
80
+ };
81
+ }
82
+ /**
83
+ * Kill the process deterministically.
84
+ * SIGTERM → wait grace period → SIGKILL → wait for exit.
85
+ */
86
+ async kill() {
87
+ if (this._state === 'exited' || this._state === 'exiting') {
88
+ // Already exiting/exited — wait for completion
89
+ if (this.exitPromise) {
90
+ await this.exitPromise;
91
+ }
92
+ return;
93
+ }
94
+ if (!this.child || this.child.exitCode !== null) {
95
+ this._state = 'exited';
96
+ return;
97
+ }
98
+ this._state = 'exiting';
99
+ this.wasKilledByUs = true;
100
+ // Send SIGTERM
101
+ try {
102
+ this.child.kill('SIGTERM');
103
+ }
104
+ catch {
105
+ // Process may already be dead
106
+ this._state = 'exited';
107
+ return;
108
+ }
109
+ // Wait for graceful exit or timeout
110
+ const graceTimeout = new Promise((resolve) => setTimeout(() => resolve('timeout'), this.killGraceMs));
111
+ const result = await Promise.race([
112
+ this.exitPromise.then(() => 'exited'),
113
+ graceTimeout,
114
+ ]);
115
+ if (result === 'timeout' && this.child.exitCode === null) {
116
+ // Force kill
117
+ try {
118
+ this.child.kill('SIGKILL');
119
+ }
120
+ catch {
121
+ // Already dead
122
+ }
123
+ // Wait for exit event (best effort)
124
+ if (this.exitPromise) {
125
+ await Promise.race([
126
+ this.exitPromise,
127
+ new Promise((r) => setTimeout(r, 2000)),
128
+ ]);
129
+ }
130
+ }
131
+ this._state = 'exited';
132
+ }
133
+ /**
134
+ * Wait for the process to exit naturally.
135
+ */
136
+ async waitForExit() {
137
+ if (!this.exitPromise) {
138
+ return { exitCode: null, signal: null, wasKilled: false };
139
+ }
140
+ return this.exitPromise;
141
+ }
142
+ // --- Private event handlers (arrow functions to preserve `this`) ---
143
+ onStdout = (data) => {
144
+ this.stdoutBuffer += data.toString();
145
+ const lines = this.stdoutBuffer.split('\n');
146
+ // Keep incomplete last line in buffer
147
+ this.stdoutBuffer = lines.pop() || '';
148
+ for (const line of lines) {
149
+ const raw = parseCodexEvent(line);
150
+ if (raw) {
151
+ const parsed = parseEventString(raw);
152
+ this.callbacks.onEvent({
153
+ raw,
154
+ parsed: parsed ?? { type: 'unknown' },
155
+ });
156
+ }
157
+ }
158
+ };
159
+ onStderr = (data) => {
160
+ const chunk = data.toString().trim();
161
+ if (!chunk)
162
+ return;
163
+ if (isStderrNoise(chunk))
164
+ return;
165
+ const raw = createStderrEvent(chunk);
166
+ const parsed = parseEventString(raw);
167
+ this.callbacks.onEvent({
168
+ raw,
169
+ parsed: parsed ?? { type: 'stderr', text: chunk },
170
+ });
171
+ };
172
+ onClose = (code, signal) => {
173
+ // Flush remaining stdout buffer
174
+ if (this.stdoutBuffer.trim()) {
175
+ const raw = parseCodexEvent(this.stdoutBuffer);
176
+ if (raw) {
177
+ const parsed = parseEventString(raw);
178
+ this.callbacks.onEvent({
179
+ raw,
180
+ parsed: parsed ?? { type: 'unknown' },
181
+ });
182
+ }
183
+ }
184
+ this.stdoutBuffer = '';
185
+ this._state = 'exited';
186
+ // Remove listeners to prevent leaks
187
+ this.removeListeners();
188
+ const exitInfo = {
189
+ exitCode: code,
190
+ signal,
191
+ wasKilled: this.wasKilledByUs,
192
+ };
193
+ // Resolve the exit promise
194
+ if (this.exitResolve) {
195
+ this.exitResolve(exitInfo);
196
+ this.exitResolve = null;
197
+ }
198
+ // Notify the owner
199
+ this.callbacks.onExit(exitInfo);
200
+ };
201
+ onError = (error) => {
202
+ this._state = 'exited';
203
+ this.removeListeners();
204
+ const exitInfo = {
205
+ exitCode: null,
206
+ signal: null,
207
+ wasKilled: this.wasKilledByUs,
208
+ };
209
+ if (this.exitResolve) {
210
+ this.exitResolve(exitInfo);
211
+ this.exitResolve = null;
212
+ }
213
+ this.callbacks.onExit(exitInfo);
214
+ };
215
+ /**
216
+ * Remove all listeners from the child process.
217
+ */
218
+ removeListeners() {
219
+ if (!this.child)
220
+ return;
221
+ this.child.stdout?.removeListener('data', this.onStdout);
222
+ this.child.stderr?.removeListener('data', this.onStderr);
223
+ this.child.removeListener('close', this.onClose);
224
+ this.child.removeListener('error', this.onError);
225
+ }
226
+ }
227
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/process/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Types for the process management module.
3
+ */
4
+ import type { ChildProcess } from 'child_process';
5
+ /**
6
+ * Internal state of a CodexProcessRunner.
7
+ * - spawning: process.spawn() called but not yet confirmed running
8
+ * - running: process confirmed alive (PID assigned)
9
+ * - exiting: termination requested, waiting for exit
10
+ * - exited: process has exited (terminal)
11
+ */
12
+ export type ProcessState = 'spawning' | 'running' | 'exiting' | 'exited';
13
+ /**
14
+ * Options for spawning a codex process.
15
+ */
16
+ export interface SpawnOptions {
17
+ /** Command args for `codex exec` */
18
+ args: string[];
19
+ /** Working directory */
20
+ cwd: string;
21
+ /** Environment overrides */
22
+ env?: Record<string, string | undefined>;
23
+ /** Grace period in ms between SIGTERM and SIGKILL (default: 5000) */
24
+ killGraceMs?: number;
25
+ }
26
+ /**
27
+ * A normalized event emitted by the process runner.
28
+ * The `raw` field is the JSON string for storage; `parsed` is the object for subscribers.
29
+ */
30
+ export interface ProcessEvent {
31
+ /** The normalized JSON string (for storage in ring buffer / output file) */
32
+ raw: string;
33
+ /** The parsed event object (for EventBus subscribers) */
34
+ parsed: ParsedProcessEvent;
35
+ }
36
+ /**
37
+ * Parsed event from codex JSONL output.
38
+ */
39
+ export interface ParsedProcessEvent {
40
+ type: string;
41
+ [key: string]: unknown;
42
+ }
43
+ /**
44
+ * Callbacks for process runner events.
45
+ */
46
+ export interface ProcessCallbacks {
47
+ /** Called for each normalized JSONL event */
48
+ onEvent: (event: ProcessEvent) => void;
49
+ /** Called when the process exits */
50
+ onExit: (info: ProcessExitInfo) => void;
51
+ }
52
+ /**
53
+ * Information about how a process exited.
54
+ */
55
+ export interface ProcessExitInfo {
56
+ exitCode: number | null;
57
+ signal: NodeJS.Signals | null;
58
+ /** Whether the process was killed by us (cancel/timeout/shutdown) */
59
+ wasKilled: boolean;
60
+ }
61
+ /**
62
+ * Handle to a running process, returned by the runner.
63
+ */
64
+ export interface ProcessHandle {
65
+ /** The PID of the child process (undefined if spawn failed) */
66
+ pid: number | undefined;
67
+ /** The raw ChildProcess reference (for setProcess compatibility) */
68
+ childProcess: ChildProcess;
69
+ /** Current runner state */
70
+ readonly state: ProcessState;
71
+ /** Kill the process deterministically */
72
+ kill(): Promise<void>;
73
+ }
74
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/process/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,oCAAoC;IACpC,MAAM,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,qEAAqE;IACrE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,oEAAoE;IACpE,YAAY,EAAE,YAAY,CAAC;IAC3B,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,yCAAyC;IACzC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Types for the process management module.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/process/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,YAAY,CAAC;AAgNpB,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;gBAE1B,MAAM,EAAE,YAAY;IA6BhC,OAAO,CAAC,aAAa;IAqUrB,OAAO,CAAC,eAAe;IAIjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,YAAY,CAAC;AA2MpB,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;gBAE1B,MAAM,EAAE,YAAY;IA6BhC,OAAO,CAAC,aAAa;IAqarB,OAAO,CAAC,eAAe;IAIjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
package/dist/server.js CHANGED
@@ -4,9 +4,10 @@ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSche
4
4
  import chalk from 'chalk';
5
5
  import { TOOLS, } from './types.js';
6
6
  import { handleError } from './errors.js';
7
- import { toolDefinitions } from './tools/definitions.js';
7
+ import { getToolDefinitions } from './tools/definitions.js';
8
8
  import { toolHandlers, setNotificationSender } from './tools/handlers.js';
9
- import { getTask, getAllTasks, getTaskCounts, } from './services/task-manager.js';
9
+ import { taskStore } from './task/store.js';
10
+ import { groupRegistry } from './wave/registry.js';
10
11
  /**
11
12
  * Parse JSONL output lines into structured events.
12
13
  * Silently skips non-JSON lines (e.g. legacy output or header leftovers).
@@ -96,7 +97,6 @@ function buildAnalytics(events, outputFilePath) {
96
97
  });
97
98
  break;
98
99
  default:
99
- // Items with _failed marker
100
100
  if (evt._failed) {
101
101
  errors.push({
102
102
  type: `${evt.type}_failed`,
@@ -111,23 +111,18 @@ function buildAnalytics(events, outputFilePath) {
111
111
  const f = outputFilePath;
112
112
  if (f) {
113
113
  const jqPrefix = `cat ${f} | grep '^{'`;
114
- // Always useful: agent responses
115
114
  if (agentMessages > 0) {
116
115
  suggested.push(`${jqPrefix} | jq -r 'select(.type=="agent_message") | .text'`);
117
116
  }
118
- // Commands: show if any were run
119
117
  if (commandsRun > 0) {
120
118
  suggested.push(`${jqPrefix} | jq 'select(.type=="command_execution") | {command,exit_code,status}'`);
121
119
  }
122
- // Errors: only if there are actual errors
123
120
  if (errors.length > 0) {
124
121
  suggested.push(`${jqPrefix} | jq 'select(._failed or .type=="error" or .type=="turn_failed" or (.type=="command_execution" and .exit_code!=0))'`);
125
122
  }
126
- // File changes: show if any
127
123
  if (filesChanged > 0) {
128
124
  suggested.push(`${jqPrefix} | jq 'select(.type=="file_change") | .changes'`);
129
125
  }
130
- // MCP calls: show if any
131
126
  if (mcpCalls > 0) {
132
127
  suggested.push(`${jqPrefix} | jq 'select(.type=="mcp_tool_call") | {server,tool,status}'`);
133
128
  }
@@ -147,6 +142,13 @@ function buildAnalytics(events, outputFilePath) {
147
142
  suggested_commands: suggested,
148
143
  };
149
144
  }
145
+ /**
146
+ * Get the output array from a TaskRecord.
147
+ * Handles the ring buffer interface.
148
+ */
149
+ function getTaskOutput(task) {
150
+ return task.output.toArray();
151
+ }
150
152
  export class CodexMcpServer {
151
153
  server;
152
154
  config;
@@ -164,7 +166,7 @@ export class CodexMcpServer {
164
166
  this.setupHandlers();
165
167
  // Wire notification sender so handlers can send resource-changed notifications
166
168
  setNotificationSender({
167
- sendNotification: async (notification) => {
169
+ sendNotification: async (_notification) => {
168
170
  try {
169
171
  await this.server.sendResourceListChanged();
170
172
  }
@@ -175,9 +177,9 @@ export class CodexMcpServer {
175
177
  });
176
178
  }
177
179
  setupHandlers() {
178
- // List tools handler
180
+ // List tools handler — dynamic so template discovery is fresh
179
181
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
180
- return { tools: toolDefinitions };
182
+ return { tools: getToolDefinitions() };
181
183
  });
182
184
  // Call tool handler
183
185
  this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
@@ -231,8 +233,8 @@ export class CodexMcpServer {
231
233
  // --- MCP Resources ---
232
234
  // List resources
233
235
  this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
234
- const tasks = getAllTasks();
235
- const counts = getTaskCounts();
236
+ const allTasks = taskStore.getAllTasks();
237
+ const counts = taskStore.getTaskCounts();
236
238
  const resources = [
237
239
  {
238
240
  uri: 'system:///status',
@@ -247,12 +249,12 @@ export class CodexMcpServer {
247
249
  mimeType: 'application/json',
248
250
  },
249
251
  ];
250
- // Add individual task resources
251
- for (const task of tasks) {
252
+ for (const task of allTasks) {
253
+ const summary = taskStore.toSummary(task);
252
254
  resources.push({
253
255
  uri: `task:///${task.id}`,
254
256
  name: `Task: ${task.id}`,
255
- description: `[${task.status}] ${task.prompt.slice(0, 80)}`,
257
+ description: `[${summary.status}] ${task.prompt.slice(0, 80)}`,
256
258
  mimeType: 'application/json',
257
259
  });
258
260
  }
@@ -268,6 +270,12 @@ export class CodexMcpServer {
268
270
  description: 'Full task detail including output tail, metadata, and timing',
269
271
  mimeType: 'application/json',
270
272
  },
273
+ {
274
+ uriTemplate: 'group:///{group_id}',
275
+ name: 'Agent Group Details',
276
+ description: 'Detailed group state including per-agent summaries, branch, and commit',
277
+ mimeType: 'application/json',
278
+ },
271
279
  ],
272
280
  };
273
281
  });
@@ -276,7 +284,7 @@ export class CodexMcpServer {
276
284
  const { uri } = request.params;
277
285
  // system:///status
278
286
  if (uri === 'system:///status') {
279
- const counts = getTaskCounts();
287
+ const counts = taskStore.getTaskCounts();
280
288
  const status = {
281
289
  server: {
282
290
  name: this.config.name,
@@ -298,11 +306,12 @@ export class CodexMcpServer {
298
306
  }
299
307
  // task:///all
300
308
  if (uri === 'task:///all') {
301
- const tasks = getAllTasks();
309
+ const allTasks = taskStore.getAllTasks();
302
310
  const summary = {
303
- count: tasks.length,
304
- tasks: tasks.map((t) => {
305
- const events = parseOutputLines(t.output);
311
+ count: allTasks.length,
312
+ tasks: allTasks.map((t) => {
313
+ const output = getTaskOutput(t);
314
+ const events = parseOutputLines(output);
306
315
  const analytics = buildAnalytics(events);
307
316
  let durationMs = null;
308
317
  if (t.startTime) {
@@ -311,16 +320,17 @@ export class CodexMcpServer {
311
320
  : Date.now();
312
321
  durationMs = end - new Date(t.startTime).getTime();
313
322
  }
323
+ const s = taskStore.toSummary(t);
314
324
  return {
315
325
  id: t.id,
316
- status: t.status,
326
+ status: s.status,
317
327
  role: t.role,
318
328
  specialization: t.specialization,
319
329
  model: t.model,
320
330
  started: t.startTime,
321
331
  ended: t.endTime,
322
332
  duration_ms: durationMs,
323
- // Lightweight analytics summary
333
+ pid: t.pid ?? null,
324
334
  events: analytics.total_lines,
325
335
  commands: analytics.commands_run,
326
336
  commands_failed: analytics.commands_failed,
@@ -348,7 +358,7 @@ export class CodexMcpServer {
348
358
  const taskIdMatch = uri.match(/^task:\/\/\/(.+)$/);
349
359
  if (taskIdMatch) {
350
360
  const taskId = taskIdMatch[1];
351
- const task = getTask(taskId);
361
+ const task = taskStore.getTask(taskId);
352
362
  if (!task) {
353
363
  return {
354
364
  contents: [
@@ -360,10 +370,9 @@ export class CodexMcpServer {
360
370
  ],
361
371
  };
362
372
  }
363
- // Parse JSONL output and build analytics dashboard
364
- const events = parseOutputLines(task.output);
373
+ const output = getTaskOutput(task);
374
+ const events = parseOutputLines(output);
365
375
  const analytics = buildAnalytics(events, task.outputFilePath);
366
- // Duration
367
376
  let durationMs = null;
368
377
  if (task.startTime) {
369
378
  const end = task.endTime
@@ -371,12 +380,9 @@ export class CodexMcpServer {
371
380
  : Date.now();
372
381
  durationMs = end - new Date(task.startTime).getTime();
373
382
  }
374
- // Extract last agent message as the "result" preview
375
383
  const lastAgentMsg = events
376
384
  .filter((e) => e.type === 'agent_message')
377
385
  .pop();
378
- // Build the output tail — only agent_message and command_execution
379
- // (the stuff the caller actually cares about, not reasoning/stderr)
380
386
  const meaningfulEvents = events.filter((e) => e.type === 'agent_message' ||
381
387
  e.type === 'command_execution' ||
382
388
  e.type === 'file_change' ||
@@ -386,8 +392,9 @@ export class CodexMcpServer {
386
392
  const outputTail = meaningfulEvents
387
393
  .slice(-30)
388
394
  .map((e) => JSON.stringify(e));
395
+ const stallWarning = taskStore.getStallWarning(task);
396
+ const s = taskStore.toSummary(task);
389
397
  const detail = {
390
- // — Dashboard / analytics (top-level summary) —
391
398
  analytics: {
392
399
  total_events: analytics.total_lines,
393
400
  event_counts: analytics.event_counts,
@@ -402,9 +409,8 @@ export class CodexMcpServer {
402
409
  errors: analytics.errors,
403
410
  }),
404
411
  },
405
- // — Task metadata —
406
412
  id: task.id,
407
- status: task.status,
413
+ status: s.status,
408
414
  role: task.role,
409
415
  specialization: task.specialization,
410
416
  model: task.model,
@@ -413,16 +419,15 @@ export class CodexMcpServer {
413
419
  cwd: task.cwd,
414
420
  output_file: task.outputFilePath,
415
421
  prompt: task.prompt,
422
+ pid: task.pid ?? null,
423
+ ...(stallWarning && { stall_warning: stallWarning }),
416
424
  ...(task.error && { error: task.error }),
417
- // — Content —
418
425
  ...(lastAgentMsg && { last_agent_message: lastAgentMsg.text }),
419
426
  output_tail: outputTail.join('\n'),
420
- // — Suggested jq commands (only relevant ones) —
421
427
  ...(analytics.suggested_commands.length > 0 && {
422
428
  suggested_commands: analytics.suggested_commands,
423
429
  }),
424
430
  };
425
- // Include last_message from metadata if available
426
431
  if (task.metadata?.lastMessage) {
427
432
  detail.last_message = task.metadata.lastMessage;
428
433
  }
@@ -436,6 +441,93 @@ export class CodexMcpServer {
436
441
  ],
437
442
  };
438
443
  }
444
+ // group:///all
445
+ if (uri === 'group:///all') {
446
+ const allGroups = groupRegistry.getAllGroups();
447
+ const summary = {
448
+ count: allGroups.length,
449
+ groups: allGroups.map((g) => ({
450
+ id: g.id,
451
+ state: g.state,
452
+ groupName: g.groupName,
453
+ branch: g.branch,
454
+ agentCount: g.agents.length,
455
+ agentsDone: g.agents.filter((a) => a.state === 'done_success' ||
456
+ a.state === 'done_failed' ||
457
+ a.state === 'blocked' ||
458
+ a.state === 'timed_out').length,
459
+ commitSha: g.commitSha,
460
+ createdAt: g.createdAt,
461
+ completedAt: g.completedAt,
462
+ error: g.error,
463
+ })),
464
+ };
465
+ return {
466
+ contents: [
467
+ {
468
+ uri,
469
+ mimeType: 'application/json',
470
+ text: JSON.stringify(summary, null, 2),
471
+ },
472
+ ],
473
+ };
474
+ }
475
+ // group:///{id}
476
+ const groupIdMatch = uri.match(/^group:\/\/\/(.+)$/);
477
+ if (groupIdMatch) {
478
+ const groupId = groupIdMatch[1];
479
+ const group = groupRegistry.getGroup(groupId);
480
+ if (!group) {
481
+ return {
482
+ contents: [
483
+ {
484
+ uri,
485
+ mimeType: 'application/json',
486
+ text: JSON.stringify({
487
+ error: `Group "${groupId}" not found`,
488
+ }),
489
+ },
490
+ ],
491
+ };
492
+ }
493
+ const detail = {
494
+ id: group.id,
495
+ state: group.state,
496
+ groupName: group.groupName,
497
+ branch: group.branch,
498
+ worktreePath: group.worktreePath,
499
+ baseCwd: group.baseCwd,
500
+ baseCommit: group.baseCommit,
501
+ commitSha: group.commitSha,
502
+ dependsOn: group.dependsOn,
503
+ createdAt: group.createdAt,
504
+ completedAt: group.completedAt,
505
+ error: group.error,
506
+ agents: group.agents.map((a) => ({
507
+ alias: a.alias,
508
+ state: a.state,
509
+ taskId: a.taskId,
510
+ role: a.role,
511
+ specialization: a.specialization,
512
+ dependsOn: a.dependsOn,
513
+ attempts: a.attempts,
514
+ maxAttempts: a.maxAttempts,
515
+ error: a.error,
516
+ startedAt: a.startedAt,
517
+ completedAt: a.completedAt,
518
+ pid: a.pid,
519
+ })),
520
+ };
521
+ return {
522
+ contents: [
523
+ {
524
+ uri,
525
+ mimeType: 'application/json',
526
+ text: JSON.stringify(detail, null, 2),
527
+ },
528
+ ],
529
+ };
530
+ }
439
531
  // Unknown URI
440
532
  return {
441
533
  contents: [