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.
- package/dist/event/bus.d.ts +53 -0
- package/dist/event/bus.d.ts.map +1 -0
- package/dist/event/bus.js +94 -0
- package/dist/event/bus.js.map +1 -0
- package/dist/event/throttle.d.ts +36 -0
- package/dist/event/throttle.d.ts.map +1 -0
- package/dist/event/throttle.js +66 -0
- package/dist/event/throttle.js.map +1 -0
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/process/event-parser.d.ts +37 -0
- package/dist/process/event-parser.d.ts.map +1 -0
- package/dist/process/event-parser.js +141 -0
- package/dist/process/event-parser.js.map +1 -0
- package/dist/process/runner.d.ts +48 -0
- package/dist/process/runner.d.ts.map +1 -0
- package/dist/process/runner.js +227 -0
- package/dist/process/runner.js.map +1 -0
- package/dist/process/types.d.ts +74 -0
- package/dist/process/types.d.ts.map +1 -0
- package/dist/process/types.js +5 -0
- package/dist/process/types.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +128 -36
- package/dist/server.js.map +1 -1
- package/dist/services/account-rotator.d.ts +28 -0
- package/dist/services/account-rotator.d.ts.map +1 -0
- package/dist/services/account-rotator.js +216 -0
- package/dist/services/account-rotator.js.map +1 -0
- package/dist/services/output-file.d.ts.map +1 -1
- package/dist/services/output-file.js +80 -36
- package/dist/services/output-file.js.map +1 -1
- package/dist/services/task-manager.d.ts +6 -0
- package/dist/services/task-manager.d.ts.map +1 -1
- package/dist/services/task-manager.js +24 -1
- package/dist/services/task-manager.js.map +1 -1
- package/dist/services/template-init.d.ts +10 -0
- package/dist/services/template-init.d.ts.map +1 -0
- package/dist/services/template-init.js +41 -0
- package/dist/services/template-init.js.map +1 -0
- package/dist/session/file-storage.d.ts +27 -0
- package/dist/session/file-storage.d.ts.map +1 -0
- package/dist/session/file-storage.js +281 -0
- package/dist/session/file-storage.js.map +1 -0
- package/dist/session/storage.js +1 -1
- package/dist/session/storage.js.map +1 -1
- package/dist/task/state-machine.d.ts +27 -0
- package/dist/task/state-machine.d.ts.map +1 -0
- package/dist/task/state-machine.js +59 -0
- package/dist/task/state-machine.js.map +1 -0
- package/dist/task/store.d.ts +91 -0
- package/dist/task/store.d.ts.map +1 -0
- package/dist/task/store.js +317 -0
- package/dist/task/store.js.map +1 -0
- package/dist/task/types.d.ts +72 -0
- package/dist/task/types.d.ts.map +1 -0
- package/dist/task/types.js +13 -0
- package/dist/task/types.js.map +1 -0
- package/dist/templates/index.d.ts +16 -0
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +57 -5
- package/dist/templates/index.js.map +1 -1
- package/dist/tools/definitions.d.ts +5 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +253 -179
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/description-builder.d.ts +18 -0
- package/dist/tools/description-builder.d.ts.map +1 -0
- package/dist/tools/description-builder.js +88 -0
- package/dist/tools/description-builder.js.map +1 -0
- package/dist/tools/handlers.d.ts +19 -17
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +287 -341
- package/dist/tools/handlers.js.map +1 -1
- package/dist/types.d.ts +5 -12
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -10
- package/dist/types.js.map +1 -1
- package/dist/utils/ring-buffer.d.ts +41 -0
- package/dist/utils/ring-buffer.d.ts.map +1 -0
- package/dist/utils/ring-buffer.js +83 -0
- package/dist/utils/ring-buffer.js.map +1 -0
- package/dist/wave/dag.d.ts +32 -0
- package/dist/wave/dag.d.ts.map +1 -0
- package/dist/wave/dag.js +186 -0
- package/dist/wave/dag.js.map +1 -0
- package/dist/wave/git.d.ts +57 -0
- package/dist/wave/git.d.ts.map +1 -0
- package/dist/wave/git.js +227 -0
- package/dist/wave/git.js.map +1 -0
- package/dist/wave/orchestrator.d.ts +15 -0
- package/dist/wave/orchestrator.d.ts.map +1 -0
- package/dist/wave/orchestrator.js +565 -0
- package/dist/wave/orchestrator.js.map +1 -0
- package/dist/wave/progress.d.ts +51 -0
- package/dist/wave/progress.d.ts.map +1 -0
- package/dist/wave/progress.js +176 -0
- package/dist/wave/progress.js.map +1 -0
- package/dist/wave/registry.d.ts +66 -0
- package/dist/wave/registry.d.ts.map +1 -0
- package/dist/wave/registry.js +340 -0
- package/dist/wave/registry.js.map +1 -0
- package/dist/wave/semaphore.d.ts +42 -0
- package/dist/wave/semaphore.d.ts.map +1 -0
- package/dist/wave/semaphore.js +119 -0
- package/dist/wave/semaphore.js.map +1 -0
- package/dist/wave/types.d.ts +197 -0
- package/dist/wave/types.d.ts.map +1 -0
- package/dist/wave/types.js +147 -0
- package/dist/wave/types.js.map +1 -0
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/process/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -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;
|
|
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 {
|
|
7
|
+
import { getToolDefinitions } from './tools/definitions.js';
|
|
8
8
|
import { toolHandlers, setNotificationSender } from './tools/handlers.js';
|
|
9
|
-
import {
|
|
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 (
|
|
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:
|
|
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
|
|
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
|
-
|
|
251
|
-
|
|
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: `[${
|
|
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
|
|
309
|
+
const allTasks = taskStore.getAllTasks();
|
|
302
310
|
const summary = {
|
|
303
|
-
count:
|
|
304
|
-
tasks:
|
|
305
|
-
const
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
364
|
-
const events = parseOutputLines(
|
|
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:
|
|
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: [
|