@bridge_gpt/mcp-server 0.2.62 → 0.2.66
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 +66 -15
- package/build/agent-launchers/claude-executor-adapter.js +145 -20
- package/build/agent-launchers/executor-adapter.js +81 -2
- package/build/agents.generated.js +10 -1
- package/build/bounded-wait.js +32 -1
- package/build/bridge-config.js +104 -0
- package/build/codex-bridge-profile.js +10 -1
- package/build/commands.generated.js +7 -6
- package/build/conductor/bridge-api-client.js +565 -32
- package/build/conductor/bring-up-facts.js +3 -1
- package/build/conductor/doctor.js +23 -14
- package/build/conductor/epic-child-continuation.js +351 -0
- package/build/conductor/epic-implementer-cli.js +154 -17
- package/build/conductor/git-hooks.js +529 -118
- package/build/conductor/guided-repair.js +323 -0
- package/build/conductor/hook-runtime-qualification.js +340 -0
- package/build/conductor/readiness-cli.js +30 -9
- package/build/conductor/readiness-sections.js +5 -4
- package/build/conductor/readiness.js +45 -12
- package/build/conductor/recovery-cli.js +859 -27
- package/build/conductor/recovery-operations.js +574 -5
- package/build/conductor/repair-contract.js +69 -3
- package/build/conductor-bundle-artifacts.js +1 -0
- package/build/credential-store.js +52 -13
- package/build/diagnostic-text.js +36 -0
- package/build/docs.generated.js +3 -2
- package/build/doctor.js +147 -21
- package/build/drive-epic-key.js +12 -0
- package/build/drive-epic.js +1537 -125
- package/build/epic-coherence-input-cli.js +137 -0
- package/build/epic-coherence-input.js +920 -0
- package/build/epic-coherence-input.test-fixtures.js +175 -0
- package/build/epic-implementer-bin.js +41 -33
- package/build/epic-implementer-bundle-cli.js +14 -1
- package/build/epic-launch-diagnostic.js +31 -0
- package/build/epic-launch-profile.js +58 -19
- package/build/epic-run-status.js +5 -0
- package/build/epic-shared/bridge-client.js +46 -4
- package/build/executor/cli.js +39 -0
- package/build/executor/completion-contract.js +423 -0
- package/build/executor/completion-supervision.js +291 -0
- package/build/executor/correction-dispatch.js +60 -0
- package/build/executor/credentials.js +3 -1
- package/build/executor/deps.js +65 -1
- package/build/executor/env.js +12 -0
- package/build/executor/heartbeat.js +13 -3
- package/build/executor/http-client.js +91 -4
- package/build/executor/integration-job.js +6 -1
- package/build/executor/job-errors.js +61 -4
- package/build/executor/job-log-registry.js +340 -73
- package/build/executor/job-runner.js +1550 -55
- package/build/executor/job-types.js +9 -6
- package/build/executor/preflight.js +189 -1
- package/build/executor/process-containment.js +756 -0
- package/build/executor/process.js +235 -5
- package/build/executor/prompt-spec.js +366 -1
- package/build/executor/provider-checks.js +69 -0
- package/build/executor/rebase-finalization.js +351 -0
- package/build/executor/reconcile-overlap-job.js +77 -84
- package/build/executor/required-verification.js +313 -0
- package/build/executor/required-work-ledger.js +1121 -0
- package/build/executor/results.js +142 -0
- package/build/executor/resume-pre-spawn.js +87 -4
- package/build/executor/runner.js +168 -0
- package/build/executor/stale-artifacts.js +5 -4
- package/build/executor/test-declaration.js +20 -0
- package/build/executor/types.js +13 -0
- package/build/executor/watch-cli.js +35 -8
- package/build/executor/worker-command.js +70 -34
- package/build/executor/worker-finalization.js +168 -26
- package/build/executor/worker-guard-hook.js +75 -14
- package/build/executor/worker-log.js +137 -70
- package/build/executor/worktree-gc.js +30 -6
- package/build/executor/worktree.js +13 -0
- package/build/executor-launch.js +658 -0
- package/build/index.js +594 -1152
- package/build/init.js +107 -4
- package/build/install-bridge-conductor.js +231 -41
- package/build/install-bridge.js +284 -20
- package/build/install-doctor.js +172 -30
- package/build/install-reexec.js +69 -7
- package/build/macos-terminal-permission-guidance.js +16 -0
- package/build/merge-pull-request.js +1 -1
- package/build/model-thresholds-tool.js +185 -0
- package/build/pipelines.generated.js +9 -8
- package/build/plan-discovery.js +137 -0
- package/build/plan-epic-implementer-eligibility.js +2 -2
- package/build/plane/cli.js +37 -3
- package/build/plane/member-logs.js +3 -0
- package/build/plane/member-roster.js +7 -1
- package/build/plane/shutdown.js +59 -1
- package/build/plane/status.js +4 -0
- package/build/plane/supervisor.js +146 -0
- package/build/plane/worker-exit-code.js +2 -0
- package/build/project-root.js +99 -0
- package/build/readme.generated.js +1 -1
- package/build/review-tickets.js +156 -13
- package/build/scripts/executor-protocol-contract-driver.js +13 -3
- package/build/setup-epic.js +208 -125
- package/build/sfcc/setup-status.js +2 -0
- package/build/sfcc/tool-wrapper.js +2 -0
- package/build/sfcc/tools-configured.js +96 -0
- package/build/start-tickets.js +163 -58
- package/build/upgrade-cli.js +109 -24
- package/build/version.generated.js +3 -3
- package/build/windows-npm-entry.js +103 -0
- package/build/worker-guard-hook-bin.js +5 -4
- package/build/worktree-core.js +6 -5
- package/docs/CONDUCTOR.md +116 -2
- package/docs/install/mcp-tool-integrations.md +1 -0
- package/docs/install/sfcc-integration.md +12 -7
- package/package.json +2 -1
- package/pipelines/implement-ticket.json +1 -1
|
@@ -14,6 +14,7 @@ export function createProcessTerminationController(proc, deps, termGraceMs = DEF
|
|
|
14
14
|
let escalated = false;
|
|
15
15
|
let disposed = false;
|
|
16
16
|
let graceTimer;
|
|
17
|
+
const killListeners = [];
|
|
17
18
|
return {
|
|
18
19
|
requestTermination() {
|
|
19
20
|
// A disposed controller belongs to a settled child: signalling it would
|
|
@@ -28,6 +29,14 @@ export function createProcessTerminationController(proc, deps, termGraceMs = DEF
|
|
|
28
29
|
return;
|
|
29
30
|
escalated = true;
|
|
30
31
|
proc.kill("SIGKILL");
|
|
32
|
+
for (const listener of killListeners.splice(0)) {
|
|
33
|
+
try {
|
|
34
|
+
listener();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* a listener must never break the escalation path */
|
|
38
|
+
}
|
|
39
|
+
}
|
|
31
40
|
}, termGraceMs);
|
|
32
41
|
},
|
|
33
42
|
isTerminationRequested() {
|
|
@@ -41,6 +50,16 @@ export function createProcessTerminationController(proc, deps, termGraceMs = DEF
|
|
|
41
50
|
deps.clearTimer(graceTimer);
|
|
42
51
|
graceTimer = undefined;
|
|
43
52
|
}
|
|
53
|
+
killListeners.length = 0;
|
|
54
|
+
},
|
|
55
|
+
onKillSent(listener) {
|
|
56
|
+
if (escalated) {
|
|
57
|
+
listener();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (disposed)
|
|
61
|
+
return;
|
|
62
|
+
killListeners.push(listener);
|
|
44
63
|
},
|
|
45
64
|
};
|
|
46
65
|
}
|
|
@@ -55,16 +74,23 @@ function appendBounded(existing, chunk, limitBytes) {
|
|
|
55
74
|
return combined;
|
|
56
75
|
return Buffer.from(combined, "utf8").subarray(0, limitBytes).toString("utf8");
|
|
57
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Drain one output stream into `onChunk`. Resolves `"eof"` at end of stream and
|
|
79
|
+
* `"stream_error"` when iteration threw (BAPI-1301 reports which, for the
|
|
80
|
+
* authoritative stdout path; legacy callers ignore it). A null stream is `"eof"`.
|
|
81
|
+
*/
|
|
58
82
|
async function pump(iterable, onChunk) {
|
|
59
83
|
if (!iterable)
|
|
60
|
-
return;
|
|
84
|
+
return "eof";
|
|
61
85
|
try {
|
|
62
86
|
for await (const chunk of iterable) {
|
|
63
87
|
onChunk(typeof chunk === "string" ? chunk : String(chunk));
|
|
64
88
|
}
|
|
89
|
+
return "eof";
|
|
65
90
|
}
|
|
66
91
|
catch {
|
|
67
92
|
/* stream errors degrade observability, never correctness */
|
|
93
|
+
return "stream_error";
|
|
68
94
|
}
|
|
69
95
|
}
|
|
70
96
|
/**
|
|
@@ -100,10 +126,37 @@ export async function runProcessWithTimeout(proc, timeoutSeconds, deps, options
|
|
|
100
126
|
// single grace timer rather than racing three of each.
|
|
101
127
|
const controller = options.terminationController ?? createProcessTerminationController(proc, deps, termGraceMs);
|
|
102
128
|
const terminate = () => controller.requestTermination();
|
|
129
|
+
// BAPI-1301 (D4a): the authoritative consumer, when supplied. `drainClosed`
|
|
130
|
+
// flips once the bounded post-exit drain gives up, so a chunk arriving after
|
|
131
|
+
// the verdict was taken can never mutate the ledger behind the caller's back.
|
|
132
|
+
const authoritative = options.authoritativeStdout;
|
|
133
|
+
const supervised = authoritative !== undefined || options.drainDeadlineMs !== undefined;
|
|
134
|
+
let authoritativeStreamError = false;
|
|
135
|
+
let drainClosed = false;
|
|
103
136
|
const pumpStdout = pump(proc.stdout, (chunk) => {
|
|
137
|
+
if (drainClosed)
|
|
138
|
+
return;
|
|
104
139
|
// Advisory observation ALWAYS runs first and always runs: the tee and the
|
|
105
140
|
// telemetry must see every chunk regardless of what the assertion decides.
|
|
106
|
-
|
|
141
|
+
if (authoritative !== undefined) {
|
|
142
|
+
// Guarded only on the authoritative path: an advisory throw must not end
|
|
143
|
+
// the pump and truncate the stream the ledger depends on.
|
|
144
|
+
try {
|
|
145
|
+
options.onStdout?.(chunk);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
/* advisory — never affects the run */
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
authoritative(chunk);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
authoritativeStreamError = true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
options.onStdout?.(chunk);
|
|
159
|
+
}
|
|
107
160
|
stdoutExcerpt = appendBounded(stdoutExcerpt, chunk, limit);
|
|
108
161
|
if (options.onStdoutTerminationCheck?.(chunk) === true && !terminationRequested) {
|
|
109
162
|
terminationRequested = true;
|
|
@@ -120,14 +173,58 @@ export async function runProcessWithTimeout(proc, timeoutSeconds, deps, options
|
|
|
120
173
|
// races an already-requested termination a no-op rather than a double signal.
|
|
121
174
|
terminate();
|
|
122
175
|
}, timeoutSeconds * 1000);
|
|
123
|
-
|
|
176
|
+
// BAPI-1301 (D4a): bounded waits, only when a drain deadline was supplied.
|
|
177
|
+
// Every timer created here is cleared before returning.
|
|
178
|
+
const drainMs = options.drainDeadlineMs === undefined ? undefined : Math.max(0, options.drainDeadlineMs);
|
|
179
|
+
const drainTimers = [];
|
|
180
|
+
const after = (ms) => new Promise((resolve) => {
|
|
181
|
+
drainTimers.push(deps.setTimer(resolve, ms));
|
|
182
|
+
});
|
|
183
|
+
let exitCode;
|
|
184
|
+
let signal;
|
|
185
|
+
let exitUnconfirmed = false;
|
|
186
|
+
if (drainMs === undefined) {
|
|
187
|
+
({ exitCode, signal } = await proc.wait());
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const racers = [
|
|
191
|
+
proc.wait().then((r) => ({ kind: "closed", ...r })),
|
|
192
|
+
];
|
|
193
|
+
// The process `exit`, independent of its pipes: a descendant holding stdout
|
|
194
|
+
// open must not stop supervision from noticing the child itself is gone.
|
|
195
|
+
if (proc.exited)
|
|
196
|
+
racers.push(proc.exited().then((r) => ({ kind: "exited", ...r })));
|
|
197
|
+
// After SIGKILL, `wait()` gets at most `drainMs` more before the exit is
|
|
198
|
+
// reported unconfirmed rather than awaited forever.
|
|
199
|
+
racers.push(new Promise((resolve) => controller.onKillSent?.(resolve))
|
|
200
|
+
.then(() => after(drainMs))
|
|
201
|
+
.then(() => ({ kind: "kill_bound", exitCode: null, signal: null })));
|
|
202
|
+
const settled = await Promise.race(racers);
|
|
203
|
+
exitCode = settled.exitCode;
|
|
204
|
+
signal = settled.signal;
|
|
205
|
+
exitUnconfirmed = settled.kind === "kill_bound";
|
|
206
|
+
}
|
|
124
207
|
deps.clearTimer(timeoutTimer);
|
|
125
208
|
// The child has settled, so any outstanding escalation is now aimed at a
|
|
126
209
|
// finished process. Disposal is idempotent and safe for a controller this call
|
|
127
210
|
// did not create — the job runner's `finally` may dispose it again.
|
|
128
211
|
controller.dispose();
|
|
129
|
-
|
|
130
|
-
|
|
212
|
+
let stdoutEnd;
|
|
213
|
+
if (drainMs === undefined) {
|
|
214
|
+
stdoutEnd = await pumpStdout;
|
|
215
|
+
await pumpStderr;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const drained = await Promise.race([
|
|
219
|
+
Promise.all([pumpStdout, pumpStderr]).then(([out]) => out),
|
|
220
|
+
after(drainMs).then(() => "drain_deadline"),
|
|
221
|
+
]);
|
|
222
|
+
if (drained === "drain_deadline")
|
|
223
|
+
drainClosed = true;
|
|
224
|
+
stdoutEnd = drained;
|
|
225
|
+
for (const handle of drainTimers)
|
|
226
|
+
deps.clearTimer(handle);
|
|
227
|
+
}
|
|
131
228
|
let classification;
|
|
132
229
|
if (timedOut) {
|
|
133
230
|
classification = "timeout";
|
|
@@ -145,6 +242,11 @@ export async function runProcessWithTimeout(proc, timeoutSeconds, deps, options
|
|
|
145
242
|
stdoutExcerpt,
|
|
146
243
|
stderrExcerpt,
|
|
147
244
|
...(terminationRequested ? { terminationRequested: true } : {}),
|
|
245
|
+
// BAPI-1301 fields appear only for callers that opted in, so every legacy
|
|
246
|
+
// result keeps its exact shape.
|
|
247
|
+
...(supervised ? { stdoutEnd } : {}),
|
|
248
|
+
...(authoritativeStreamError ? { authoritativeStreamError: true } : {}),
|
|
249
|
+
...(exitUnconfirmed ? { exitUnconfirmed: true } : {}),
|
|
148
250
|
};
|
|
149
251
|
}
|
|
150
252
|
/**
|
|
@@ -155,3 +257,131 @@ export function killOwnedProcess(proc, reason) {
|
|
|
155
257
|
proc.kill("SIGKILL");
|
|
156
258
|
return { classification: "killed", reason };
|
|
157
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Wait for `success` on `stdin`, bounded by abort, deadline, `close`, and `error`.
|
|
262
|
+
* `action` runs AFTER every listener is registered, so an implementation (or a
|
|
263
|
+
* fake) that emits synchronously inside `end()` is still observed. Every
|
|
264
|
+
* listener and timer is removed on settle. Never rejects.
|
|
265
|
+
*/
|
|
266
|
+
function awaitStdinOutcome(stdin, success, options, action) {
|
|
267
|
+
return new Promise((resolve) => {
|
|
268
|
+
let settled = false;
|
|
269
|
+
let timer;
|
|
270
|
+
const registered = [];
|
|
271
|
+
const onAbort = () => finish({ ok: false, reason: "aborted" });
|
|
272
|
+
function finish(result) {
|
|
273
|
+
if (settled)
|
|
274
|
+
return;
|
|
275
|
+
settled = true;
|
|
276
|
+
if (timer !== undefined)
|
|
277
|
+
options.deps.clearTimer(timer);
|
|
278
|
+
for (const [event, listener] of registered) {
|
|
279
|
+
try {
|
|
280
|
+
stdin.off?.(event, listener);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
/* best-effort listener cleanup */
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
287
|
+
resolve(result);
|
|
288
|
+
}
|
|
289
|
+
const listen = (event, result) => {
|
|
290
|
+
const listener = () => finish(result);
|
|
291
|
+
registered.push([event, listener]);
|
|
292
|
+
try {
|
|
293
|
+
stdin.once(event, listener);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
finish({ ok: false, reason: "write_error" });
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
listen(success, { ok: true });
|
|
300
|
+
listen("error", { ok: false, reason: "write_error" });
|
|
301
|
+
listen("close", { ok: false, reason: "closed" });
|
|
302
|
+
if (settled)
|
|
303
|
+
return;
|
|
304
|
+
if (options.signal) {
|
|
305
|
+
if (options.signal.aborted) {
|
|
306
|
+
finish({ ok: false, reason: "aborted" });
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
310
|
+
}
|
|
311
|
+
const remaining = options.deadlineAtMs - options.deps.now();
|
|
312
|
+
if (remaining <= 0) {
|
|
313
|
+
finish({ ok: false, reason: "deadline" });
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
timer = options.deps.setTimer(() => {
|
|
317
|
+
timer = undefined;
|
|
318
|
+
finish({ ok: false, reason: "deadline" });
|
|
319
|
+
}, remaining);
|
|
320
|
+
if (action) {
|
|
321
|
+
try {
|
|
322
|
+
action();
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
finish({ ok: false, reason: "write_error" });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/** Pre-checks shared by write and close: stdin present, not aborted, deadline not passed. */
|
|
331
|
+
function stdinPrecheck(proc, options) {
|
|
332
|
+
const stdin = proc.stdin;
|
|
333
|
+
if (!stdin)
|
|
334
|
+
return { failure: { ok: false, reason: "no_stdin" } };
|
|
335
|
+
if (options.signal?.aborted)
|
|
336
|
+
return { failure: { ok: false, reason: "aborted" } };
|
|
337
|
+
if (options.deps.now() >= options.deadlineAtMs)
|
|
338
|
+
return { failure: { ok: false, reason: "deadline" } };
|
|
339
|
+
return { stdin };
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Write `data` to a held child stdin, observing Node backpressure (BAPI-1301).
|
|
343
|
+
*
|
|
344
|
+
* When `write()` accepts the data below the high-water mark this resolves at
|
|
345
|
+
* once. When it returns `false`, it awaits `drain` — but only until the attempt
|
|
346
|
+
* deadline, the abort signal, the pipe closing, or a pipe error, whichever comes
|
|
347
|
+
* first. It NEVER throws: every failure is a normalized
|
|
348
|
+
* {@link StdinTransportResult} the caller turns into an evidence fact
|
|
349
|
+
* (`transport_failed_before_work` / `transport_failed_after_progress`).
|
|
350
|
+
*/
|
|
351
|
+
export async function writeStdinBounded(proc, data, options) {
|
|
352
|
+
const checked = stdinPrecheck(proc, options);
|
|
353
|
+
if ("failure" in checked)
|
|
354
|
+
return checked.failure;
|
|
355
|
+
const { stdin } = checked;
|
|
356
|
+
if (stdin.destroyed === true || stdin.writableEnded === true) {
|
|
357
|
+
return { ok: false, reason: "closed" };
|
|
358
|
+
}
|
|
359
|
+
let accepted;
|
|
360
|
+
try {
|
|
361
|
+
accepted = stdin.write(data);
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return { ok: false, reason: "write_error" };
|
|
365
|
+
}
|
|
366
|
+
if (accepted)
|
|
367
|
+
return { ok: true };
|
|
368
|
+
return awaitStdinOutcome(stdin, "drain", options);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Half-close a held child stdin — EOF for the child — bounded the same way
|
|
372
|
+
* (BAPI-1301). Tolerates an already-ended pipe (`{ ok: true }`: EOF was already
|
|
373
|
+
* delivered); a destroyed pipe is `closed`. Waits for `finish` (the flush) only
|
|
374
|
+
* until deadline/abort/close/error. Never throws.
|
|
375
|
+
*/
|
|
376
|
+
export async function closeStdinBounded(proc, options) {
|
|
377
|
+
// An already-ended pipe is success even past the deadline: EOF was delivered.
|
|
378
|
+
if (proc.stdin?.writableEnded === true)
|
|
379
|
+
return { ok: true };
|
|
380
|
+
const checked = stdinPrecheck(proc, options);
|
|
381
|
+
if ("failure" in checked)
|
|
382
|
+
return checked.failure;
|
|
383
|
+
const { stdin } = checked;
|
|
384
|
+
if (stdin.destroyed === true)
|
|
385
|
+
return { ok: false, reason: "closed" };
|
|
386
|
+
return awaitStdinOutcome(stdin, "finish", options, () => stdin.end());
|
|
387
|
+
}
|