@bridge_gpt/mcp-server 0.2.51 → 0.2.53

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 (79) hide show
  1. package/README.md +59 -13
  2. package/build/agent-capabilities/probe-context.js +15 -7
  3. package/build/agent-capabilities/probes.js +42 -6
  4. package/build/agent-launchers/claude-executor-adapter.js +98 -14
  5. package/build/commands.generated.js +7 -5
  6. package/build/conduct-epic/cut-protocol.js +17 -3
  7. package/build/conductor/bridge-api-client.js +232 -5
  8. package/build/conductor/cli.js +23 -0
  9. package/build/conductor/deny-enforcement-preflight.js +107 -10
  10. package/build/conductor/doctor.js +428 -5
  11. package/build/conductor/install-doctor.js +65 -656
  12. package/build/conductor/local-merge.js +170 -11
  13. package/build/conductor/readiness-cli.js +152 -0
  14. package/build/conductor/readiness-sections.js +666 -0
  15. package/build/conductor/readiness.js +710 -0
  16. package/build/conductor/tools.js +56 -3
  17. package/build/conductor-bin.js +21 -17
  18. package/build/connect-bitbucket-api.js +370 -0
  19. package/build/connect-bitbucket.js +437 -0
  20. package/build/docs.generated.js +1 -1
  21. package/build/doctor.js +40 -1
  22. package/build/drive-epic.js +423 -11
  23. package/build/env-file-link.js +164 -0
  24. package/build/epic-integration-pr.js +10 -0
  25. package/build/executor/cli.js +41 -6
  26. package/build/executor/deps.js +5 -1
  27. package/build/executor/env-file-guard.js +113 -0
  28. package/build/executor/env.js +78 -1
  29. package/build/executor/heartbeat.js +9 -0
  30. package/build/executor/http-client.js +90 -22
  31. package/build/executor/job-errors.js +43 -2
  32. package/build/executor/job-runner.js +130 -28
  33. package/build/executor/merge-job.js +67 -16
  34. package/build/executor/permissions.js +106 -0
  35. package/build/executor/preflight.js +38 -13
  36. package/build/executor/resume-pre-spawn.js +2 -1
  37. package/build/executor/runner.js +175 -4
  38. package/build/executor/service-unit.js +15 -0
  39. package/build/executor/terminal-mutation.js +22 -1
  40. package/build/executor/types.js +86 -0
  41. package/build/executor/worker-command.js +21 -5
  42. package/build/executor/worker-guard-hook.js +939 -0
  43. package/build/executor/worker-log.js +56 -0
  44. package/build/executor/worktree.js +11 -0
  45. package/build/git-reachability.js +147 -0
  46. package/build/index.js +4734 -4270
  47. package/build/install-bridge.js +95 -0
  48. package/build/install-doctor.js +154 -2
  49. package/build/pipelines.generated.js +6 -4
  50. package/build/plan-epic-conductor-eligibility.js +37 -7
  51. package/build/plane/alembic-head.js +40 -11
  52. package/build/plane/build-freshness.js +22 -11
  53. package/build/plane/cli.js +78 -15
  54. package/build/plane/defaults.js +165 -0
  55. package/build/plane/manifest.js +63 -8
  56. package/build/plane/member-logs.js +6 -0
  57. package/build/plane/member-roster.js +195 -11
  58. package/build/plane/preflight.js +402 -44
  59. package/build/plane/shutdown.js +25 -3
  60. package/build/plane/status.js +11 -0
  61. package/build/plane/supervisor.js +343 -14
  62. package/build/plane/test-fakes.js +43 -0
  63. package/build/plane/types.js +118 -11
  64. package/build/pr-base-contract.js +20 -0
  65. package/build/readiness-check.js +412 -0
  66. package/build/readme.generated.js +1 -1
  67. package/build/review-synthesis-config.js +60 -0
  68. package/build/scripts/executor-protocol-contract-driver.js +311 -0
  69. package/build/setup-epic.js +560 -139
  70. package/build/sfcc/log-query.js +2 -1
  71. package/build/start-tickets-conductor.js +11 -2
  72. package/build/start-tickets.js +69 -2
  73. package/build/version.generated.js +3 -3
  74. package/build/worker-containment-diagnostic.js +97 -0
  75. package/build/worker-guard-hook-bin.js +6 -0
  76. package/docs/CONDUCTOR.md +27 -0
  77. package/docs/install/mcp-tool-integrations.md +3 -2
  78. package/package.json +4 -3
  79. package/pipelines/{full-automation.json → idea-to-pr.json} +1 -1
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Executor `/executor/jobs/*` protocol contract driver (BAPI-997, T4).
4
+ *
5
+ * A Python `subprocess` boundary that lets the real pytest suite drive the
6
+ * REAL, emitted TypeScript executor HTTP client (`createExecutorHttpClient` in
7
+ * `../executor/http-client.js`, built from `mcp_server/src/executor/`) against
8
+ * the real FastAPI `/executor/jobs/*` routes, and separately projects the
9
+ * TypeScript protocol's closed vocabularies and structural request/response
10
+ * shapes into canonical JSON so a Python test can compare them against the
11
+ * server's Pydantic models — WITHOUT either side hand-maintaining a duplicate
12
+ * description of the other's contract.
13
+ *
14
+ * This module is deliberately NOT registered as an MCP tool
15
+ * (`server.registerTool`) and is NOT listed in `INTEGRATION_LANE_ONLY`: it is
16
+ * test-support infrastructure invoked directly by a Python subprocess test,
17
+ * never shipped as user-facing functionality.
18
+ *
19
+ * Two independent modes, selected by argv shape:
20
+ *
21
+ * node executor-protocol-contract-driver.js <commandsJsonPath> <resultsJsonPath>
22
+ * Reads a JSON array of commands from `commandsJsonPath`, executes each
23
+ * against a real `createExecutorHttpClient` instance (built from a
24
+ * narrowly-constructed child environment, never argv, for credential
25
+ * material — see `readClientConfigFromEnv`), and atomically writes a JSON
26
+ * array of outcomes to `resultsJsonPath`. See `executeCommand` for the
27
+ * supported `claim` / `heartbeat` / `complete` / `fail` command shapes.
28
+ *
29
+ * node executor-protocol-contract-driver.js --emit-protocol <outputJsonPath>
30
+ * Emits the closed-vocabulary runtime constants (job types,
31
+ * classifications, mutation outcomes, reconciler-liveness states) from the
32
+ * BUILT `../executor/types.js` module, plus structural metadata (property
33
+ * names, optionality, and union-literal members) for the claim/claimed/
34
+ * heartbeat/completion/failure/process-heartbeat wire shapes, projected
35
+ * from the SOURCE `mcp_server/src/executor/types.ts` through the
36
+ * TypeScript compiler API (`typescript/unstable/sync` +
37
+ * `typescript/unstable/ast`) — never regex, substring matching, or a raw
38
+ * source snapshot.
39
+ *
40
+ * Reserved stdout: this driver writes ITS results only to the output file.
41
+ * stdout carries no diagnostics — every diagnostic and error goes to stderr —
42
+ * so a caller that happens to capture stdout never accumulates incidental
43
+ * log noise.
44
+ */
45
+ import { fileURLToPath } from "node:url";
46
+ import path from "node:path";
47
+ import fs from "node:fs";
48
+ import { API, SymbolFlags } from "typescript/unstable/sync";
49
+ import { isInterfaceDeclaration, isTypeAliasDeclaration } from "typescript/unstable/ast";
50
+ import { createExecutorHttpClient, RECONCILER_LIVENESS_HEADER, HEARTBEAT_STOP_FIELD, } from "../executor/http-client.js";
51
+ import { EXECUTOR_JOB_TYPE_VALUES, EXECUTOR_CLASSIFICATION_VALUES, EXECUTOR_MUTATION_OUTCOME_VALUES, RECONCILER_LIVENESS_VALUES, } from "../executor/types.js";
52
+ // ---------------------------------------------------------------------------
53
+ // realFetch — a thin adapter over global fetch. Never constructs a URL,
54
+ // header, or fenced body field itself; it only shapes the Response into what
55
+ // FetchLike expects and observes (never sets) the one response header this
56
+ // driver is allowed to record as evidence.
57
+ // ---------------------------------------------------------------------------
58
+ /**
59
+ * The most recently observed `RECONCILER_LIVENESS_HEADER` value, reset before
60
+ * every command and captured here rather than threaded through the injected
61
+ * `fetch` return type — the client's own `FetchResponseLike` contract is
62
+ * unchanged by this driver (BAPI-997 action item: "do not construct... raw
63
+ * `fetch` requests in the driver" — this reads a header, it does not build
64
+ * one).
65
+ */
66
+ let lastReconcilerLivenessHeaderRaw = null;
67
+ const realFetch = async (url, init) => {
68
+ const response = await fetch(url, {
69
+ method: init.method,
70
+ headers: init.headers,
71
+ body: init.body,
72
+ });
73
+ lastReconcilerLivenessHeaderRaw = response.headers.get(RECONCILER_LIVENESS_HEADER);
74
+ const text = await response.text();
75
+ const adapted = {
76
+ status: response.status,
77
+ text: async () => text,
78
+ headers: response.headers,
79
+ };
80
+ return adapted;
81
+ };
82
+ // ---------------------------------------------------------------------------
83
+ // Client configuration — read from a narrowly constructed child environment,
84
+ // never from argv (argv here is reserved for the two non-sensitive file
85
+ // paths). The Python caller constructs a minimal env containing only these
86
+ // four variables; this driver never inspects or forwards the rest of
87
+ // `process.env`, and never echoes the environment in any output.
88
+ // ---------------------------------------------------------------------------
89
+ const ENV_BASE_URL = "EXECUTOR_CONTRACT_DRIVER_BASE_URL";
90
+ const ENV_API_KEY = "EXECUTOR_CONTRACT_DRIVER_API_KEY";
91
+ const ENV_API_KEY_BY_REPO_JSON = "EXECUTOR_CONTRACT_DRIVER_API_KEY_BY_REPO_JSON";
92
+ const ENV_MCP_VERSION = "EXECUTOR_CONTRACT_DRIVER_MCP_VERSION";
93
+ /** Read the five client configuration entries (BAPI-997 action item 5). */
94
+ function readClientConfigFromEnv(env) {
95
+ const baseUrl = env[ENV_BASE_URL];
96
+ const apiKey = env[ENV_API_KEY];
97
+ const mcpVersion = env[ENV_MCP_VERSION];
98
+ if (!baseUrl)
99
+ throw new Error(`${ENV_BASE_URL} must be set in the child environment`);
100
+ if (!apiKey)
101
+ throw new Error(`${ENV_API_KEY} must be set in the child environment`);
102
+ if (!mcpVersion)
103
+ throw new Error(`${ENV_MCP_VERSION} must be set in the child environment`);
104
+ let apiKeyByRepo;
105
+ const rawApiKeyByRepo = env[ENV_API_KEY_BY_REPO_JSON];
106
+ if (rawApiKeyByRepo) {
107
+ const parsed = JSON.parse(rawApiKeyByRepo);
108
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
109
+ apiKeyByRepo = parsed;
110
+ }
111
+ else {
112
+ throw new Error(`${ENV_API_KEY_BY_REPO_JSON} must decode to a JSON object`);
113
+ }
114
+ }
115
+ return { baseUrl, apiKey, apiKeyByRepo, mcpVersion };
116
+ }
117
+ const KNOWN_OPS = new Set(["claim", "heartbeat", "complete", "fail"]);
118
+ function isPlainObject(value) {
119
+ return !!value && typeof value === "object" && !Array.isArray(value);
120
+ }
121
+ async function executeCommand(client, command) {
122
+ lastReconcilerLivenessHeaderRaw = null;
123
+ try {
124
+ switch (command.op) {
125
+ case "claim": {
126
+ const result = await client.claim(command.manifest);
127
+ return {
128
+ op: command.op,
129
+ ok: true,
130
+ result,
131
+ evidence: { [RECONCILER_LIVENESS_HEADER]: lastReconcilerLivenessHeaderRaw },
132
+ };
133
+ }
134
+ case "heartbeat": {
135
+ const result = await client.heartbeat(command.job, command.payload);
136
+ return { op: command.op, ok: true, result };
137
+ }
138
+ case "complete": {
139
+ const result = await client.complete(command.job, command.completion);
140
+ return { op: command.op, ok: true, result };
141
+ }
142
+ case "fail": {
143
+ const result = await client.fail(command.job, command.failure);
144
+ return { op: command.op, ok: true, result };
145
+ }
146
+ }
147
+ }
148
+ catch (err) {
149
+ const message = err instanceof Error ? err.message : String(err);
150
+ return { op: command.op, ok: false, error: message };
151
+ }
152
+ }
153
+ /** Atomically replace `outputPath`'s contents (write-then-rename, same filesystem). */
154
+ function writeJsonAtomically(outputPath, value) {
155
+ const tmpPath = `${outputPath}.tmp-${process.pid}-${Date.now()}`;
156
+ fs.writeFileSync(tmpPath, JSON.stringify(value, null, 2));
157
+ fs.renameSync(tmpPath, outputPath);
158
+ }
159
+ async function runCommands(inputPath, outputPath) {
160
+ const config = readClientConfigFromEnv(process.env);
161
+ const client = createExecutorHttpClient({
162
+ baseUrl: config.baseUrl,
163
+ apiKey: config.apiKey,
164
+ apiKeyByRepo: config.apiKeyByRepo,
165
+ mcpVersion: config.mcpVersion,
166
+ fetch: realFetch,
167
+ });
168
+ const rawCommands = JSON.parse(fs.readFileSync(inputPath, "utf8"));
169
+ if (!Array.isArray(rawCommands)) {
170
+ throw new Error("commands file must contain a JSON array");
171
+ }
172
+ const outcomes = [];
173
+ for (const raw of rawCommands) {
174
+ if (!isPlainObject(raw) || typeof raw.op !== "string" || !KNOWN_OPS.has(raw.op)) {
175
+ outcomes.push({
176
+ op: isPlainObject(raw) && typeof raw.op === "string" ? raw.op : "unknown",
177
+ ok: false,
178
+ error: "unrecognized or malformed command",
179
+ });
180
+ continue;
181
+ }
182
+ outcomes.push(await executeCommand(client, raw));
183
+ }
184
+ writeJsonAtomically(outputPath, outcomes);
185
+ }
186
+ /** The exported shapes this driver projects (BAPI-997 action item 13). */
187
+ const PROJECTED_SHAPE_NAMES = [
188
+ "ExecutorClaimJobRequest",
189
+ "ClaimedExecutorJob",
190
+ "ExecutorHeartbeatWireRequest",
191
+ "ExecutorCompletionWireRequest",
192
+ "ExecutorFailureWireRequest",
193
+ "ExecutorProcessHeartbeatRequest",
194
+ ];
195
+ /** Return the closed string-literal members of *type*, or `undefined` when it is not one. */
196
+ function literalValuesOf(type) {
197
+ if (type.isStringLiteralType()) {
198
+ return [type.value];
199
+ }
200
+ if (type.isUnionType()) {
201
+ const values = [];
202
+ for (const member of type.getTypes()) {
203
+ if (!member.isStringLiteralType())
204
+ return undefined;
205
+ values.push(member.value);
206
+ }
207
+ return values.sort();
208
+ }
209
+ return undefined;
210
+ }
211
+ function projectShape(checker, declaration, name) {
212
+ const type = checker.getTypeAtLocation(declaration);
213
+ if (!type) {
214
+ throw new Error(`could not resolve a type for ${name}`);
215
+ }
216
+ const properties = checker
217
+ .getPropertiesOfType(type)
218
+ .map((symbol) => {
219
+ const propertyType = checker.getTypeOfSymbolAtLocation(symbol, declaration);
220
+ return {
221
+ name: symbol.name,
222
+ optional: !!(symbol.flags & SymbolFlags.Optional),
223
+ type: checker.typeToString(propertyType),
224
+ literalValues: literalValuesOf(propertyType),
225
+ };
226
+ })
227
+ .sort((a, b) => a.name.localeCompare(b.name));
228
+ return { name, properties };
229
+ }
230
+ async function emitProtocolProjection(outputPath) {
231
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
232
+ // This driver runs from build/scripts/; the emitted tree mirrors src/, so
233
+ // the SOURCE file the compiler API projects lives two levels up, under
234
+ // src/executor/ — never mcp_server/build/.
235
+ const mcpServerRoot = path.resolve(scriptDir, "..", "..");
236
+ const typesSourcePath = path.join(mcpServerRoot, "src", "executor", "types.ts");
237
+ const api = new API({ cwd: mcpServerRoot });
238
+ try {
239
+ const snapshot = api.updateSnapshot({ openFiles: [typesSourcePath] });
240
+ const project = snapshot.getDefaultProjectForFile(typesSourcePath);
241
+ if (!project) {
242
+ throw new Error(`no TypeScript project resolved for ${typesSourcePath}`);
243
+ }
244
+ const checker = project.checker;
245
+ const sourceFile = project.program.getSourceFile(typesSourcePath);
246
+ if (!sourceFile) {
247
+ throw new Error(`could not load source file ${typesSourcePath}`);
248
+ }
249
+ const shapes = {};
250
+ for (const statement of sourceFile.statements) {
251
+ if (!isInterfaceDeclaration(statement) && !isTypeAliasDeclaration(statement))
252
+ continue;
253
+ const name = statement.name.text;
254
+ if (!PROJECTED_SHAPE_NAMES.includes(name))
255
+ continue;
256
+ shapes[name] = projectShape(checker, statement, name);
257
+ }
258
+ const missing = PROJECTED_SHAPE_NAMES.filter((name) => !(name in shapes));
259
+ if (missing.length > 0) {
260
+ throw new Error(`could not project shape(s): ${missing.join(", ")}`);
261
+ }
262
+ const projection = {
263
+ jobTypes: [...EXECUTOR_JOB_TYPE_VALUES],
264
+ classifications: [...EXECUTOR_CLASSIFICATION_VALUES],
265
+ mutationOutcomes: [...EXECUTOR_MUTATION_OUTCOME_VALUES],
266
+ reconcilerLiveness: [...RECONCILER_LIVENESS_VALUES],
267
+ reconcilerLivenessHeader: RECONCILER_LIVENESS_HEADER,
268
+ heartbeatStopField: HEARTBEAT_STOP_FIELD,
269
+ shapes,
270
+ };
271
+ writeJsonAtomically(outputPath, projection);
272
+ project.dispose();
273
+ }
274
+ finally {
275
+ api.close();
276
+ }
277
+ }
278
+ // ---------------------------------------------------------------------------
279
+ // Entry point
280
+ // ---------------------------------------------------------------------------
281
+ export async function main(argv) {
282
+ if (argv[0] === "--emit-protocol") {
283
+ const outputPath = argv[1];
284
+ if (!outputPath) {
285
+ process.stderr.write("usage: executor-protocol-contract-driver --emit-protocol <outputJsonPath>\n");
286
+ process.exitCode = 1;
287
+ return;
288
+ }
289
+ await emitProtocolProjection(outputPath);
290
+ return;
291
+ }
292
+ const [inputPath, outputPath] = argv;
293
+ if (!inputPath || !outputPath) {
294
+ process.stderr.write("usage: executor-protocol-contract-driver <commandsJsonPath> <resultsJsonPath>\n");
295
+ process.exitCode = 1;
296
+ return;
297
+ }
298
+ await runCommands(inputPath, outputPath);
299
+ }
300
+ const invokedDirectly = process.argv[1]
301
+ ? fileURLToPath(import.meta.url) === path.resolve(process.argv[1])
302
+ : false;
303
+ if (invokedDirectly) {
304
+ main(process.argv.slice(2)).catch((err) => {
305
+ // Sanitized top-level catch: never print a stack trace, raw error, or
306
+ // secret material (the child environment/API key) to stderr.
307
+ const message = err instanceof Error ? err.message : String(err);
308
+ process.stderr.write(`executor-protocol-contract-driver: ${message}\n`);
309
+ process.exitCode = 1;
310
+ });
311
+ }