@bridge_gpt/mcp-server 0.2.34 → 0.2.37

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 (109) hide show
  1. package/README.md +490 -366
  2. package/build/agent-capabilities/probe-context.js +8 -1
  3. package/build/agent-capabilities/probes.js +7 -1
  4. package/build/agents.generated.js +1 -1
  5. package/build/base-url.js +79 -0
  6. package/build/bridge-api-urls.js +9 -0
  7. package/build/chain-orchestrator.js +93 -15
  8. package/build/claude-review-workflow.js +264 -0
  9. package/build/claude-user-config-doctor.js +317 -0
  10. package/build/cli-release.js +53 -0
  11. package/build/commands.generated.js +6 -5
  12. package/build/conductor/bridge-api-client.js +393 -4
  13. package/build/conductor/deny-enforcement-preflight.js +1 -0
  14. package/build/conductor/done-gate.js +44 -5
  15. package/build/conductor/epic-reconcile.js +6 -0
  16. package/build/conductor/install-doctor.js +462 -0
  17. package/build/conductor-bin.js +3 -3
  18. package/build/conductor-bundle-artifacts.js +37 -15
  19. package/build/credential-store.js +205 -4
  20. package/build/direct-ticket-tools.js +70 -0
  21. package/build/doctor.js +473 -81
  22. package/build/executor/cli.js +83 -6
  23. package/build/executor/credentials.js +39 -11
  24. package/build/executor/deps.js +32 -1
  25. package/build/executor/env.js +71 -28
  26. package/build/executor/heartbeat.js +138 -17
  27. package/build/executor/http-client.js +49 -8
  28. package/build/executor/index.js +4 -0
  29. package/build/executor/job-errors.js +4 -0
  30. package/build/executor/job-runner.js +530 -20
  31. package/build/executor/observation.js +130 -0
  32. package/build/executor/permissions.js +114 -8
  33. package/build/executor/preflight.js +127 -8
  34. package/build/executor/prompt-spec.js +51 -0
  35. package/build/executor/runner.js +23 -2
  36. package/build/executor/service-unit.js +876 -0
  37. package/build/executor/test-clock.js +75 -3
  38. package/build/executor/types.js +4 -18
  39. package/build/executor/worker-command.js +73 -12
  40. package/build/executor/worker-config-isolation.js +287 -0
  41. package/build/executor/worker-finalization.js +68 -14
  42. package/build/executor/worktree.js +46 -4
  43. package/build/index.js +1039 -237
  44. package/build/init.js +503 -111
  45. package/build/install-bridge-conductor.js +491 -0
  46. package/build/install-bridge.js +1188 -247
  47. package/build/install-reexec.js +233 -0
  48. package/build/launcher-config-inspection.js +351 -0
  49. package/build/mcp-host-config.js +11 -1
  50. package/build/mcp-install-state.js +32 -0
  51. package/build/mcp-invoke.js +49 -6
  52. package/build/mcp-provisioning.js +52 -13
  53. package/build/mcp-registration-doctor.js +14 -5
  54. package/build/notifications.js +553 -0
  55. package/build/pipeline-orchestrator.js +146 -4
  56. package/build/pipeline-utils.js +3 -0
  57. package/build/pipelines.generated.js +32 -13
  58. package/build/plan-execution-ledger.js +550 -0
  59. package/build/plan-phase-routing.js +272 -0
  60. package/build/plane/alembic-head.js +110 -0
  61. package/build/plane/build-freshness.js +167 -0
  62. package/build/plane/cli.js +480 -0
  63. package/build/plane/defaults.js +266 -0
  64. package/build/plane/manifest.js +377 -0
  65. package/build/plane/member-logs.js +147 -0
  66. package/build/plane/member-roster.js +147 -0
  67. package/build/plane/preflight.js +289 -0
  68. package/build/plane/shutdown.js +195 -0
  69. package/build/plane/status.js +125 -0
  70. package/build/plane/supervisor.js +569 -0
  71. package/build/plane/test-fakes.js +156 -0
  72. package/build/plane/types.js +75 -0
  73. package/build/readme.generated.js +1 -1
  74. package/build/run-unit-tests-launcher.js +259 -0
  75. package/build/setup-epic.js +772 -28
  76. package/build/sfcc/log-gate.js +38 -11
  77. package/build/sfcc/log-query.js +55 -15
  78. package/build/sfcc/ocapi-shape.js +51 -14
  79. package/build/sfcc/output.js +41 -11
  80. package/build/sfcc/permissions.js +24 -2
  81. package/build/sfcc/read-projection.js +181 -0
  82. package/build/sfcc/read-result.js +158 -0
  83. package/build/sfcc/reads-custom-object-def.js +29 -18
  84. package/build/sfcc/reads-site-preference.js +75 -29
  85. package/build/sfcc/reads-system-object.js +40 -34
  86. package/build/sfcc/sfcc-result.js +106 -0
  87. package/build/sfcc/tool-wrapper.js +56 -13
  88. package/build/sfcc/write-grants.js +45 -22
  89. package/build/sfcc/write-guard.js +21 -13
  90. package/build/sfcc/write-result.js +61 -14
  91. package/build/sfcc/write-tool-common.js +126 -32
  92. package/build/sfcc/writes-system-object.js +11 -50
  93. package/build/start-tickets-prereqs.js +129 -0
  94. package/build/start-tickets.js +17 -13
  95. package/build/ticket-backend-metadata.js +59 -0
  96. package/build/ticket-key-utils.js +92 -0
  97. package/build/tool-error-envelope.js +71 -0
  98. package/build/tool-surface-gating.js +72 -0
  99. package/build/update-status.js +102 -0
  100. package/build/upgrade-advice.js +47 -0
  101. package/build/upgrade-cli.js +412 -110
  102. package/build/version.generated.js +1 -1
  103. package/build/worktree-core.js +73 -0
  104. package/docs/CONDUCTOR.md +132 -6
  105. package/docs/install/mcp-tool-integrations.md +29 -21
  106. package/package.json +9 -6
  107. package/pipelines/implement-ticket.json +19 -4
  108. package/build/conductor/supervisor-judgment-python.js +0 -141
  109. package/build/conductor/supervisor-judgment.js +0 -215
@@ -144,6 +144,13 @@ export async function isExistingBranchSafeToReuse(deps, branch, baseStartPoint)
144
144
  * instead of being silently reused as-is. This stays executor-agnostic: no
145
145
  * job-type logic lives here — the caller decides the start point, the stale
146
146
  * guard, and the freshen behavior.
147
+ *
148
+ * BAPI-727: each of the two RESET paths is followed by an untracked-file cleanup
149
+ * (see {@link cleanUntrackedWorktree}). That coupling is what keeps `resume`
150
+ * excluded BY CONSTRUCTION rather than by a job-type check: the resume caller
151
+ * supplies neither `freshenFromOrigin` nor `alignExistingBranchTo`, so it reaches
152
+ * neither reset and therefore neither cleanup, and its unpushed local work is
153
+ * preserved exactly as before.
147
154
  */
148
155
  /**
149
156
  * Hard-reset the branch checked out in `worktreePath` onto `ref`. Returns null on
@@ -159,6 +166,64 @@ async function hardResetWorktree(deps, worktreePath, ref) {
159
166
  }
160
167
  return null;
161
168
  }
169
+ /** The one fixed, sanitized diagnostic emitted for ANY cleanup failure. */
170
+ const CLEANUP_FAILURE_DIAGNOSTIC = "untracked-file cleanup did not complete; continuing with the reset worktree";
171
+ /**
172
+ * BAPI-727: remove untracked residue from a worktree that was just hard-reset.
173
+ *
174
+ * `git reset --hard` restores TRACKED files but leaves untracked ones in place, so
175
+ * a reused worktree carried a prior attempt's scratch files into the next run. This
176
+ * removes them with exactly `git clean -fd`.
177
+ *
178
+ * `-x` IS FORBIDDEN, and `-fdx` doubly so: ignored paths in a prepared worktree are
179
+ * precisely the executor-owned assets — `.mcp.json` / `.cursor/mcp.json`
180
+ * registrations, worker environment files, installed dependencies, worker logs, and
181
+ * the provisioned `.claude/commands/` assets. `-fdx` would destroy all of them.
182
+ *
183
+ * Note that `.claude/settings.local.json` is NOT ignored by this repository, so
184
+ * plain `-fd` may remove it on some hosts. That is safe only because of WHERE this
185
+ * runs: cleanup happens during worktree PREPARATION, before command assets, deny
186
+ * settings, MCP registrations/trust, and worker logging are provisioned again — so
187
+ * anything cleanup removes is re-created by the provisioning that follows it.
188
+ *
189
+ * Stale, non-ignored `.conductor/result.json` and `.conductor/critique.md` from a
190
+ * prior attempt are removed intentionally: a leftover verdict artifact read by a
191
+ * later attempt would be a prior run's answer presented as this one's.
192
+ *
193
+ * FAIL-OPEN. Pre-change behavior performed no cleanup at all, so being unable to
194
+ * clean must never change the worktree row or the job outcome. Every failure path —
195
+ * a non-zero exit, a thrown runner — emits the one fixed diagnostic and returns
196
+ * normally, and the throw is caught HERE so the outer `createWorktreeForTicket()`
197
+ * catch cannot convert it into `create-failed`.
198
+ */
199
+ async function cleanUntrackedWorktree(deps, worktreePath) {
200
+ const emit = deps.onCleanupDiagnostic;
201
+ const report = (message) => {
202
+ if (emit)
203
+ emit(message);
204
+ };
205
+ try {
206
+ const result = await deps.runCommand("git", ["clean", "-fd"], { cwd: worktreePath });
207
+ // Report removals even when the command later failed: paths Git printed on
208
+ // stdout were genuinely removed, and losing that record would make a partial
209
+ // cleanup invisible. Only lines with Git's standard `Removing ` prefix are
210
+ // read; all other output is discarded rather than serialized.
211
+ for (const line of result.stdout.split(/\r?\n/)) {
212
+ if (!line.startsWith("Removing "))
213
+ continue;
214
+ const removed = line.slice("Removing ".length).trim();
215
+ if (removed.length > 0)
216
+ report(`removed untracked path: ${removed}`);
217
+ }
218
+ if (!commandSucceeded(result))
219
+ report(CLEANUP_FAILURE_DIAGNOSTIC);
220
+ }
221
+ catch {
222
+ // Never interpolate the thrown exception: a command-runner error message can
223
+ // carry an environment dump or an argv fragment.
224
+ report(CLEANUP_FAILURE_DIAGNOSTIC);
225
+ }
226
+ }
162
227
  /**
163
228
  * BAPI-586: resolve the worktree's `HEAD^{commit}` and the `expected` ref/commit
164
229
  * and confirm they are equal. Returns null when they match, or a bounded,
@@ -223,6 +288,10 @@ export async function createWorktreeForTicket(deps, key, branchOverrides, worktr
223
288
  if (resetError) {
224
289
  return { key, branch, status: "create-failed", error: resetError };
225
290
  }
291
+ // BAPI-727: the reset restored tracked files; drop the untracked residue a
292
+ // prior attempt left behind. Only after a SUCCESSFUL reset — a failed reset
293
+ // returns above, so a worktree in an unknown state is never cleaned.
294
+ await cleanUntrackedWorktree(deps, worktreePath);
226
295
  }
227
296
  // BAPI-586 (fresh dispatch): a guard-approved pre-existing branch is an
228
297
  // ancestor of base but may sit at an OLDER tip. Align it EXACTLY to the
@@ -235,6 +304,10 @@ export async function createWorktreeForTicket(deps, key, branchOverrides, worktr
235
304
  if (resetError) {
236
305
  return { key, branch, status: "create-failed", error: resetError };
237
306
  }
307
+ // BAPI-727: same cleanup on the fresh-dispatch alignment path. The resulting
308
+ // fresh-dispatch order is: stale-branch guard, Worktrunk switch, hard reset,
309
+ // untracked clean, then `verifyHeadMatches` below.
310
+ await cleanUntrackedWorktree(deps, worktreePath);
238
311
  }
239
312
  // BAPI-586 (fresh dispatch): verify the worktree actually starts at the
240
313
  // pinned base. If Worktrunk seeded from an unexpected sibling despite
package/docs/CONDUCTOR.md CHANGED
@@ -25,6 +25,23 @@ v2 splits the old local tick into two halves:
25
25
  | **Reconciler** | **Server-side**, on the Bridge API worker dyno, every 30s | Selects every epic run whose status is `active`, evaluates gates, and enqueues executor jobs. Nothing to install or schedule. |
26
26
  | **Executor** | **Locally**, on your machine | Polls for jobs, claims them, spawns worker agents, heartbeats. This is the only piece you run. |
27
27
 
28
+ > **⚠️ For a run against a LOCAL server, do NOT use `npx`.** The npm-published
29
+ > package routinely lags `main` — that is the normal steady state right after any
30
+ > conductor ticket merges. When the server is built from a newer `main` it mints
31
+ > `prompt_spec` placeholders the older published executor rejects, and the job dies
32
+ > instantly with `ContractError.Prompt` (`prompt_spec for job N declares unsupported
33
+ > placeholder '…'`). Confirmed on the BAPI-716 run: published `0.2.34` vs. local
34
+ > `0.2.35`, over BAPI-699's `RELATED_CONTEXT`. Build and run the local tree so the
35
+ > executor and server come from the same commit:
36
+ >
37
+ > ```
38
+ > cd mcp_server && npm run build
39
+ > node mcp_server/build/index.js executor --repo <name>
40
+ > ```
41
+
42
+ For a run against a deployed server whose version you match, the published CLI is
43
+ fine:
44
+
28
45
  ```
29
46
  npx -y @bridge_gpt/mcp-server executor --repo <name>
30
47
  ```
@@ -47,17 +64,126 @@ pipeline step. `setup-epic` validates it locally (unique ticket keys, resolvable
47
64
  `depends_on`/edge references, acyclicity) before sending anything, so a malformed
48
65
  plan fails legibly instead of as a bare HTTP 400.
49
66
 
67
+ Since BAPI-754 it then calls the server's **real** validator
68
+ (`POST /jira/epic-runs/plan/validate`) before creating anything, so a plan the
69
+ server would reject — a node missing `status`, say, which the local checks do not
70
+ cover — no longer creates a run and incurs an automation-start charge on its way
71
+ to failing.
72
+
50
73
  It is **idempotent**: re-running it on an epic that already has a live run reuses
51
- that run rather than minting a second one. Use `--dry-run` to validate a plan and
52
- preview the calls without mutating anything.
74
+ that run rather than minting a second one. Use `--dry-run` to run that
75
+ authoritative validation and preview the calls without mutating anything; add
76
+ `--local-only` to skip the server entirely, which reports
77
+ `partial (local checks only)` and is never a substitute for the server's verdict.
53
78
 
54
79
  Once the plan is approved the run becomes `active`, the server-side reconciler
55
80
  picks it up within ~30s, and your local `executor` starts claiming jobs.
56
81
 
57
- > **A caveat worth knowing before you plan an epic.** Conductor has no
58
- > merge-conflict handling, and plan-time file-overlap serialization is currently
59
- > dark (the planner does not yet emit the `touched_files` metadata it needs). If
60
- > two sibling tickets touch the same files, do not let them dispatch in parallel.
82
+ ### `touched_files` optional, but declare it anyway
83
+
84
+ **Plan-time file-overlap serialization is live and on by default.**
85
+ `planner_file_overlap_serialization_enabled` (BAPI-495, defaulted on by BAPI-677 /
86
+ migration 0126, on `config_code_repositories`) makes overlapping siblings gain a
87
+ `depends_on`/edge on each other *before* the DAG is validated, hashed, and stored,
88
+ while siblings with disjoint files stay parallel.
89
+
90
+ `touched_files` is the plan-time metadata that pass reads. It is a **best-effort
91
+ prediction, never reconciled against the real diff**, so BAPI-722 made it optional.
92
+ Three outcomes, and the difference between the first two is the point
93
+ (`_extract_predicted_touched_files`, `api/library/db/epic_runs.py:3195`):
94
+
95
+ | In the node | Meaning | Result |
96
+ |---|---|---|
97
+ | Field **absent** | The node **opts out** of preemptive serialization | **Accepted.** Every pair involving it is skipped (`undeclared_pairs_skipped`); no edge is added in either direction |
98
+ | `"touched_files": []` | A deliberate "this ticket predicts no file overlap" | Accepted as a *declared* node with an empty set |
99
+ | Field present but **malformed** | A non-list, or a list holding a blank, absolute, or non-string path | **400** — `EpicRunConflictError(code="VALIDATION")` |
100
+
101
+ Only omission is relaxed. Data you *did* supply must be well-formed. Paths are
102
+ repo-root-relative.
103
+
104
+ ```json
105
+ {
106
+ "plan_version": 1,
107
+ "nodes": [
108
+ { "ticket_key": "BAPI-405", "status": "planned", "depends_on": [],
109
+ "touched_files": ["api/routes/jira_api.py"] }
110
+ ],
111
+ "edges": []
112
+ }
113
+ ```
114
+
115
+ > **`setup-epic --dry-run` warns on a missing `touched_files`, and that warning is
116
+ > a planning-quality signal, not a predicted failure.** The server accepts the
117
+ > plan. What you lose is scheduling protection: an undeclared node is never
118
+ > pre-serialized against its siblings.
119
+
120
+ Declare it anyway. Serialization only ever **adds** edges — it never removes an
121
+ authored one — so hand-authored `depends_on` and `touched_files` compose safely.
122
+ Author both: the edges as your reviewable statement of intent, `touched_files` so
123
+ the scheduler can catch overlaps you did not anticipate.
124
+
125
+ > **Serialization is a scheduling optimization, not a safety mechanism.** It avoids
126
+ > *dispatching* file-overlapping siblings in parallel; it does not resolve a
127
+ > conflict once one exists. The safety net is reactive and independent of any
128
+ > prediction: a PR that a sibling's merge turns `CONFLICTING` is observed live and
129
+ > routed to `JOB_TYPE_REBASE` (`_merge_entry_failure_requires_rebase` in the
130
+ > reconciler). That backstop covers declared and undeclared nodes alike — which is
131
+ > exactly why omitting `touched_files` is safe to accept.
132
+
133
+ ## The conductor CI gate — `conductor-ci / gate` (BAPI-695)
134
+
135
+ **Before driving a run you want CI-gated, wire its policy.** The reconciler's
136
+ per-ticket `ci` gate reads **`policy_json.required_checks`** (a declared
137
+ `RunPolicy` key) and observes those check-run conclusions at the ticket PR's
138
+ **current head SHA**. If that list is empty, the observer short-circuits to
139
+ `PASSED` with `{"no_required_checks": true}` and **runs no CI at all** — the loop
140
+ merges on hand-verification only. `.github/workflows/conductor-ci.yml` publishes
141
+ the real, always-reporting required context **`conductor-ci / gate`**; set
142
+
143
+ ```json
144
+ "policy_json": { "required_checks": ["conductor-ci / gate"] }
145
+ ```
146
+
147
+ on every run you want gated. The workflow existing is not enough — the context has
148
+ to be in the run policy.
149
+
150
+ > **Wire it at creation with `--policy-file` (BAPI-754).** There is still no
151
+ > `--required-checks` option, but `setup-epic --policy-file <path>` sends the
152
+ > **complete** run policy on the create that mints the run, so the run is born with
153
+ > `required_checks` already in force. A run created *without* a policy still starts
154
+ > with an **empty** `required_checks` and a `ci` gate that short-circuits to
155
+ > `PASSED` — and because `setup-epic` also approves the plan, making the run
156
+ > `active` and visible to the reconciler within ~30s, there is no comfortable window
157
+ > to fix that afterwards. Supply the policy file up front.
158
+ >
159
+ > To change the policy on a run that is already live, re-run with `--policy-file`:
160
+ > an identical policy is a no-op, and a divergent one is **refused** with a redacted
161
+ > diff unless you also pass `--replace-policy` (which applies it as a complete
162
+ > replacement).
163
+
164
+ > **This is NOT `done_gate_config`.** The per-run `policy_json.required_checks`
165
+ > read by the reconciler `ci` gate is a *different* surface from the supervisor
166
+ > `done_gate_config.required_ci_checks_green` documented under "Supervisor config"
167
+ > below. BAPI-695 wires only the former, per run; it does **not** change
168
+ > `done_gate_config` defaults, repo-default policy, or GitHub branch protection.
169
+
170
+ **Once gated, expect this behavior:**
171
+
172
+ - The `ci` gate now waits for `conductor-ci / gate` on the ticket PR's current head.
173
+ A complete **red** aggregator enters the bounded `ci_fix` remediation path; a
174
+ **missing or still-running** current-head context stays pending (never a hard
175
+ fail).
176
+ - A **green from an older head** can never satisfy the gate after a rebase or
177
+ force-push — the poll is bound to the current head SHA, and the workflow's per-PR
178
+ concurrency cancels the superseded run so the new head publishes a fresh context.
179
+ - Out-of-scope PRs (forks, `dependabot/*`, non-`feature/BAPI-*`) get an intentional
180
+ fast green — cost control, **not** proof the suites ran.
181
+
182
+ For the exact new-run/PATCH procedure (and the **whole-object `policy_json`
183
+ replacement** caution — GET, merge `required_checks` locally, PATCH the whole
184
+ object), and the immutable case-sensitive matching of the context string, see the
185
+ operator runbook, **§4a "Wire the conductor CI required check"**
186
+ ([`docs/claude/epic-conductor-v2-operator-runbook.md`](../../docs/claude/epic-conductor-v2-operator-runbook.md)).
61
187
 
62
188
  ## Conductor observability (opt-in via `--conductor`, BAPI-394)
63
189
 
@@ -48,26 +48,31 @@ marked **— none**.
48
48
 
49
49
  ## Profiles (a form of BLOCK)
50
50
 
51
- The server registers **90 tools** total, gated by `BRIDGE_MCP_PROFILE`
52
- (comma-separated group list, resolved once at startup by
53
- `mcp_server/src/mcp-profile.ts`). A tool outside
54
- the active profile is **not registered at all** — an absolute block until the group is
51
+ Tool registration is gated by `BRIDGE_MCP_PROFILE` (comma-separated group list,
52
+ resolved once at startup by `mcp_server/src/mcp-profile.ts`). A tool outside the
53
+ active profile is **not registered at all** — an absolute block until the group is
55
54
  activated. `core` is always implicitly included.
56
55
 
57
- | Profile | Tools | Registered when |
58
- |---|---|---|
59
- | `core` | **57** | always |
60
- | `pipeline-authoring` | **+5** | `BRIDGE_MCP_PROFILE` includes `pipeline-authoring` or `full` |
61
- | `conductor` | **+8** | includes `conductor` or `full` |
62
- | `sfcc` | **+20** (2 diagnostics ship in `core`; 18 gated) | includes `sfcc` or `full` |
56
+ | Profile | Registered when |
57
+ |---|---|
58
+ | `core` | always |
59
+ | `pipeline-authoring` | `BRIDGE_MCP_PROFILE` includes `pipeline-authoring` or `full` |
60
+ | `conductor` | includes `conductor` or `full` |
61
+ | `sfcc` | includes `sfcc` or `full` (2 diagnostics ship in `core`; the rest are gated) |
62
+
63
+ The `pipeline-authoring` tools live in `index.ts` alongside core, gated by
64
+ `ACTIVE_GROUPS.has("pipeline-authoring")`.
63
65
 
64
- (57 core + 5 + 8 + 20 = 90. The 5 pipeline-authoring tools live in `index.ts` alongside
65
- core, gated by `ACTIVE_GROUPS.has("pipeline-authoring")`, so `index.ts` holds 62
66
- `registerTool` calls.)
66
+ > **This document deliberately states no tool counts.** Counts drift on every
67
+ > ticket that adds or moves a tool, and nothing here is generated, so a number
68
+ > written down is a number that goes stale. For the authoritative surface, read
69
+ > `api/library/config/mcp_tool_catalog.json` (the committed catalog, kept in sync
70
+ > by `scripts/sync_mcp_tool_catalog.py --check`) or call `tools/list` against a
71
+ > running server. The per-tool rows below are what this document is for.
67
72
 
68
73
  ---
69
74
 
70
- ## Core profile (57)
75
+ ## Core profile
71
76
 
72
77
  ### Connectivity, config & setup — no integration deps
73
78
 
@@ -78,6 +83,8 @@ These are never gated (`NEVER_GATED_ROUTE_KEYS`) or run entirely locally.
78
83
  | `ping` | — none |
79
84
  | `get_project_standards` | — none |
80
85
  | `get_my_role` | — none |
86
+ | `invite_member` | — none (mints a scoped key in the Bridge DB; admin-only) |
87
+ | `resolve_target_status` | — none (status resolution over the repo's configured mapping) |
81
88
  | `config_field` (get/update/list) | — none |
82
89
  | `get_install_manifest` | — none |
83
90
  | `apply_install_manifest` | — none |
@@ -87,6 +94,7 @@ These are never gated (`NEVER_GATED_ROUTE_KEYS`) or run entirely locally.
87
94
  | `track_ticket` | — none (Bridge DB tracking record) |
88
95
  | `update_ticket_state` | — none (Bridge DB) |
89
96
  | `get_ticket_state` | — none (Bridge DB) |
97
+ | `get_ticket_state_tree` | — none (Bridge DB; live projection over existing rows) |
90
98
  | `get_ticket_model_tier` | — none (Bridge DB difficulty) |
91
99
  | `get_pipeline_recipe` | — none (`LOCAL`, bundled recipes) |
92
100
  | `generate_decision_page` | — none (`LOCAL` HTML) |
@@ -186,7 +194,7 @@ These orchestrate an idea → ticket → review → start chain, so they inherit
186
194
 
187
195
  ---
188
196
 
189
- ## `pipeline-authoring` profile (5)
197
+ ## `pipeline-authoring` profile
190
198
 
191
199
  Profile-gated **[BLOCK]** to register at all. `run_pipeline` / `resume_pipeline`
192
200
  additionally inherit the dependencies of whatever steps the invoked pipeline runs
@@ -203,7 +211,7 @@ Run state is persisted via the Bridge API.
203
211
 
204
212
  ---
205
213
 
206
- ## `conductor` profile (8)
214
+ ## `conductor` profile
207
215
 
208
216
  Profile-gated **[BLOCK]** to register. Most operate on the **local** conductor ledger
209
217
  (`~/.config/bridge/events.db`), so beyond the profile gate they have no remote
@@ -222,7 +230,7 @@ integration dependency.
222
230
 
223
231
  ---
224
232
 
225
- ## `sfcc` group (20)
233
+ ## `sfcc` group
226
234
 
227
235
  ### Always-on diagnostics (ship in `core`)
228
236
 
@@ -231,13 +239,13 @@ integration dependency.
231
239
  | `sfcc_setup_status` | — none (its purpose is to report which SFCC prerequisites are missing) |
232
240
  | `check_permissions` | SFCC OCAPI **[BLOCK]** (probes OCAPI; reports not-OK without access / version config) |
233
241
 
234
- ### Gated behind the `sfcc` group (18)
242
+ ### Gated behind the `sfcc` group
235
243
 
236
244
  All are profile-gated **[BLOCK]** to register, and all require SFCC OCAPI creds +
237
245
  `version` config + a reachable sandbox → SFCC OCAPI **[BLOCK]**. Writes are
238
246
  sandbox-only and destructive.
239
247
 
240
- **Reads (8):**
248
+ **Reads:**
241
249
 
242
250
  | Tool | Dependencies (class) |
243
251
  |---|---|
@@ -264,7 +272,7 @@ sandbox-only and destructive.
264
272
  | `custom_object_definition_attribute_update` | Profile-gated **[BLOCK]** · SFCC OCAPI write **[BLOCK]** |
265
273
  | `site_preference_values_set` | Profile-gated **[BLOCK]** · SFCC OCAPI write **[BLOCK]** |
266
274
 
267
- **Logs (1):**
275
+ **Logs:**
268
276
 
269
277
  | Tool | Dependencies (class) |
270
278
  |---|---|
@@ -283,7 +291,7 @@ sandbox-only and destructive.
283
291
  | **SFCC OCAPI** | `check_permissions` + all 16 SFCC read/write tools | — |
284
292
  | **SFCC WebDAV logs** | `sfcc_log_query` | — |
285
293
  | **Deep-research flag** | `request_deep_research`, `get_deep_research` | — |
286
- | **Profile gating** | all `pipeline-authoring` (5), `conductor` (8), gated `sfcc` (18) tools | — |
294
+ | **Profile gating** | all `pipeline-authoring`, `conductor`, and gated `sfcc` tools | — |
287
295
 
288
296
  ## Notes & caveats
289
297
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.34",
3
+ "version": "0.2.37",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,13 +20,16 @@
20
20
  "docs/",
21
21
  "LICENSE"
22
22
  ],
23
+ "//test": "The `test` script delegates to the bounded launcher (scripts/run-unit-tests.js → src/run-unit-tests-launcher.ts, BAPI-683), which discovers build/*.test.js at runtime and spawns them in size-bounded batches so the argv never exceeds Windows' ~8191-char command-line limit. Two determinism rules from the pre-launcher script are preserved INSIDE the launcher: (1) multi-file batches run WITHOUT --test-force-exit, because that flag makes node:test exit before aggregating per-subprocess summaries and the tally becomes nondeterministic (failures still surface and still set exit 1, but the count cannot be trusted as a completeness signal); (2) build/secret-safety.test.js leaks a handle that holds the event loop ~60s after its tests finish (they run in ~147ms), so the launcher quarantines it into its own single-file --test-force-exit invocation (FORCE_EXIT_QUARANTINE in src/run-unit-tests-launcher.ts) where the tally stays exact. Do NOT add --test-force-exit to batched invocations, and do NOT remove the quarantine, until the underlying handle leak is fixed.",
23
24
  "scripts": {
24
- "build": "node scripts/bundle-version.js && node scripts/bundle-readme.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && node scripts/bundle-docs.js && tsc && node scripts/bundle-esbuild.js",
25
+ "build": "node scripts/bundle-version.js && node scripts/bundle-readme.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && node scripts/bundle-docs.js && tsc && node scripts/sync-agent-mirrors.js && node scripts/bundle-esbuild.js",
25
26
  "check:version-generated": "node scripts/bundle-version.js && node scripts/check-version-generated.js",
26
27
  "postbuild": "node scripts/prepend-shebang.cjs",
27
28
  "start": "node build/index.js",
28
- "test": "node --test --test-force-exit build/pipeline-utils.test.js build/backend-warnings.test.js build/update-check.test.js build/cli-upgrade.test.js build/decision-page-schema.test.js build/decision-page-template.test.js build/bundle-pipelines.test.js build/instructions-contract.test.js build/pipeline-orchestrator-persistence.test.js build/pipeline-orchestrator-execution.test.js build/pipeline-orchestrator-integration.test.js build/index-static.test.js build/config-fields.static.test.js build/request-brainstorm.static.test.js build/execute-plan-instructions.static.test.js build/index-resolvers.test.js build/index-project-root.test.js build/index-pipelines.test.js build/index.test.js build/bridge-config.test.js build/credential-store.test.js build/agent-config-credential-migration.test.js build/mcp-invoke.test.js build/mcp-provisioning.test.js build/mcp-server-invocation.test.js build/third-party-mcp-targets.test.js build/git-ignore-utils.test.js build/command-provisioning.test.js build/command-assets-doctor.test.js build/credential-materialization.test.js build/mcp-registration-doctor.test.js build/mcp-host-targets.test.js build/mcp-install-state.test.js build/mcp-host-config.test.js build/secret-safety.test.js build/base-ref.test.js build/pr-base-contract.test.js build/start-tickets.test.js build/review-tickets.test.js build/start-tickets-base-branch.test.js build/agent-registry.test.js build/agent-registry.model-routing.test.js build/start-tickets.shell-model-routing.test.js build/start-tickets.bridge-api-model-routing.test.js build/start-tickets.tier-fetch-model-routing.test.js build/start-tickets.resolve-model-routing.test.js build/start-tickets.orchestrate-model-routing.test.js build/start-tickets.routing-diagnostics.test.js build/start-tickets-repo.test.js build/start-tickets-credential-invariants.static.test.js build/credentials-cli.test.js build/start-tickets-prereqs.test.js build/regression-check.test.js build/doctor.test.js build/install-doctor.test.js build/install-bridge.test.js build/install-bridge-invite.test.js build/install-bridge-prompt.test.js build/install-bridge-tools.test.js build/install-bridge-join-static.test.js build/init.test.js build/init-docs.test.js build/resolveUploadAttachment.test.js build/package-static.test.js build/chain-utils.test.js build/chain-orchestrator.test.js build/scheduler-backends/types.test.js build/scheduler-backends/escaping.test.js build/scheduler-backends/launchd.test.js build/scheduler-backends/task-scheduler.test.js build/scheduler-backends/systemd-user.test.js build/scheduler-backends/at-fallback.test.js build/scheduler-backends/index.test.js build/command-catalog.test.js build/scheduled-prompt.test.js build/agent-launchers/claude.test.js build/agent-launchers/cursor.test.js build/agent-launchers/index.test.js build/schedule-store.test.js build/schedule-run.test.js build/setup-epic.test.js build/conductor-bundle-artifacts.test.js build/conductor-bundle-cli.test.js build/agent-capabilities/cli.test.js build/agent-capabilities/runner.test.js build/agent-capabilities/probes.test.js build/agent-capabilities/probe-context.test.js build/agent-capabilities/reporter.test.js build/conductor/taxonomy-and-errors.test.js build/conductor/github-mergeability.test.js build/conductor/merge-conflict-routing.test.js build/conductor/redaction-normalization.test.js build/conductor/claude-hook.test.js build/conductor/git-ci-types.test.js build/conductor/done-gate.test.js build/conductor/git-ci-taxonomy-payload.test.js build/conductor/bridge-api-client.test.js build/conductor/plan.test.js build/conductor/producer-ledger.test.js build/conductor/spec-review-producer.test.js build/conductor/git-producer.test.js build/conductor/git-hooks.test.js build/conductor/store-migration.test.js build/conductor/pr-discovery.test.js build/conductor/pr-ci-producer.test.js build/conductor/pr-review-producer.test.js build/conductor/doctor.test.js build/conductor/deny-enforcement-preflight.test.js build/conductor/errors.test.js build/conductor/store.test.js build/conductor/index-poll-ci-producer.test.js build/start-tickets-conductor.test.js build/start-tickets-conductor.spawn.test.js build/start-tickets-conductor-node-propagation.test.js build/start-tickets.non-mutating-base.test.js build/start-tickets-live-source-guard.test.js build/conductor/worker-ledger-cli.test.js build/conductor/supervisor-config.test.js build/conductor/supervisor-ledger.test.js build/conductor/supervisor-state-reducer.test.js build/conductor/supervisor-housekeeping-projection.test.js build/conductor/supervisor-escalation.test.js build/conductor/supervisor-retired-judgment.static.test.js build/conductor/supervisor-runtime.test.js build/conductor/supervisor-store-projection.test.js build/conductor/supervisor-cli.test.js build/conductor/supervisor-start-tickets.test.js build/conductor/supervisor-message-relay.test.js build/conductor/supervisor-state-message-events.test.js build/conductor/store-message-relay.test.js build/start-tickets-message-relay.test.js build/conductor/event-accessors.test.js build/conductor/merge-ledger.test.js build/conductor/deterministic-completion.static.test.js build/conductor/deterministic-completion.integration.test.js build/conductor/local-merge.test.js build/conductor/local-merge.static.test.js build/conductor/supervisor-merge.test.js build/conductor/bridge-api-merge-client.test.js build/conductor/bridge-api-epic-client.test.js build/conductor/supervisor-merge-runtime-state.test.js build/conductor/epic-state.test.js build/executor/claude-settings.test.js build/executor/cli.test.js build/executor/credentials.test.js build/executor/echo-acceptance.test.js build/executor/env.test.js build/executor/base-branch.test.js build/executor/heartbeat.test.js build/executor/http-client.test.js build/executor/job-runner.test.js build/executor/job-runner.payload-timeout.test.js build/executor/worker-finalization.test.js build/executor/job-runner.static.test.js build/executor/prompt-spec.test.js build/executor/job-types.test.js build/executor/recovery-job.static.test.js build/executor/observation.test.js build/executor/permissions.test.js build/executor/preflight.test.js build/executor/process.test.js build/executor/results.test.js build/executor/runner.test.js build/executor/terminal-mutation.test.js build/executor/worker-command.test.js build/executor/worktree-core.test.js build/executor/worktree.test.js build/executor/job-errors.test.js build/executor/worktree-inspection.test.js build/executor/resume-pre-spawn.test.js build/executor/verdict-artifact.test.js build/executor/worker-log.test.js build/executor/job-log-registry.test.js build/executor/viewer-tabs.test.js build/executor/watch-cli.test.js build/executor/merge-job.test.js build/executor/job-runner.merge.test.js build/executor/worktree-gc.test.js build/executor/runner.job-behaviors.test.js build/conductor/cli-freeze.test.js build/conductor/epic-reconcile.test.js build/conductor/epic-runtime.test.js build/conductor/epic-tick-sequence.test.js build/conductor/epic-runtime-post-action.test.js build/conductor/file-scope-guard.test.js build/conductor/file-scope-guard.integration.test.js build/mcp-profile.test.js build/mcp-profile-registration.test.js build/env-flags.test.js build/bridge-api-urls.test.js build/tool-surface-gating.test.js build/tools-budget.test.js build/visual-diff-worker.test.js build/visual-diff.test.js build/estimate-epic.test.js build/integration/measure-tools.test.js build/sfcc/config.test.js build/sfcc/ocapi-shape.test.js build/sfcc/output.test.js build/sfcc/credentials.test.js build/sfcc/ocapi-write-faults.test.js build/sfcc/write-guard.test.js build/sfcc/write-grants.test.js build/sfcc/write-result.test.js build/sfcc/writes.test.js build/sfcc/writes-system-object-payloads.test.js build/sfcc/writes-payloads.test.js build/sfcc/log-gate.test.js build/sfcc/log-query.test.js && node --experimental-test-module-mocks --test --test-force-exit build/index-heavy-read-truncation.test.js build/index-artifacts.test.js build/automation-progress.test.js build/recovery-formatting.test.js build/wait-for-result.test.js build/ticket-wait-recovery.test.js build/council-wait-recovery.test.js build/index.review-rounds.test.js build/index-brainstorm-filenames.test.js build/index-output-path.test.js build/index-generate-decision-page.test.js build/index-generate-decision-page.integration.test.js build/visual-diff.registration.test.js build/visual-diff.attachment-adapter.test.js build/attachment-download.test.js build/attachment-upload.test.js build/conductor/paths.test.js build/conductor/store-lifecycle.test.js build/conductor/store-queries.test.js build/conductor/tools.test.js build/conductor/cli.test.js build/conductor/security-regressions.test.js build/conductor/git-inspection.test.js build/conductor/tools-done-gate.test.js build/conductor/pr-ci-producer-emit-seam.test.js build/conductor/cli-git-hooks.test.js build/sfcc/client.test.js build/sfcc/tool-wrapper.test.js build/sfcc/setup-status.test.js build/sfcc/permissions.test.js build/sfcc/register.test.js build/sfcc/reads-system-object.test.js build/sfcc/reads-custom-object-def.test.js build/sfcc/reads-site-preference.test.js build/sfcc/writes-system-object.test.js build/sfcc/writes-custom-object-def.test.js build/sfcc/writes-site-preference.test.js build/connect-github.test.js build/connect-github-api.test.js build/connect-github-handoff.test.js build/connect-github-dispatch.static.test.js",
29
- "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/command-provisioning.integration.test.js build/integration/start-tickets.integration.test.js build/integration/start-tickets-tier-handoff.integration.test.js build/integration/doctor.integration.test.js build/integration/agent-capabilities.integration.test.js build/integration/conductor-producer.integration.test.js build/integration/conductor-message-relay.integration.test.js build/integration/executor-http-runner.integration.test.js build/integration/executor-job-behaviors.integration.test.js build/integration/executor-recovery-jobs.integration.test.js build/integration/executor-spec-review-prompt.integration.test.js build/integration/resume-pre-spawn.git.integration.test.js build/integration/worker-finalization-origin.integration.test.js build/integration/post-remediation-merge-ci-wait.integration.test.js build/integration/executor-merge-supervision.integration.test.js build/integration/attachment-binary-roundtrip.integration.test.js build/integration/dependent-ticket-fresh-base.integration.test.js build/integration/execute-plan-instructions.integration.test.js build/integration/conductor-bundle-artifacts.integration.test.js build/integration/install-bridge-repo-resolution.integration.test.js build/integration/capability-report-contract.integration.test.js build/integration/request-brainstorm-general.integration.test.js build/integration/request-council-trigger-drop.integration.test.js build/integration/install-bridge-onboarding-launch.integration.test.js build/integration/install-bridge-failure-guards.integration.test.js build/integration/learn-repository-pipeline.integration.test.js build/integration/visual-diff-mcp.integration.test.js",
29
+ "test": "npm run test:normal && npm run test:module-mocks",
30
+ "test:normal": "node scripts/run-unit-tests.js normal",
31
+ "test:module-mocks": "node scripts/run-unit-tests.js module-mocks",
32
+ "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/command-provisioning.integration.test.js build/integration/start-tickets.integration.test.js build/integration/start-tickets-tier-handoff.integration.test.js build/integration/doctor.integration.test.js build/integration/agent-capabilities.integration.test.js build/integration/conductor-producer.integration.test.js build/integration/conductor-message-relay.integration.test.js build/integration/executor-http-runner.integration.test.js build/integration/executor-job-behaviors.integration.test.js build/integration/executor-recovery-jobs.integration.test.js build/integration/executor-spec-review-prompt.integration.test.js build/integration/resume-pre-spawn.git.integration.test.js build/integration/worker-finalization-origin.integration.test.js build/integration/post-remediation-merge-ci-wait.integration.test.js build/integration/executor-merge-supervision.integration.test.js build/integration/attachment-binary-roundtrip.integration.test.js build/integration/dependent-ticket-fresh-base.integration.test.js build/integration/execute-plan-instructions.integration.test.js build/integration/implement-ticket-finalization-order.integration.test.js build/integration/conductor-bundle-artifacts.integration.test.js build/integration/install-bridge-repo-resolution.integration.test.js build/integration/capability-report-contract.integration.test.js build/integration/request-brainstorm-general.integration.test.js build/integration/request-council-trigger-drop.integration.test.js build/integration/install-bridge-onboarding-launch.integration.test.js build/integration/install-bridge-conductor.integration.test.js build/integration/install-bridge-failure-guards.integration.test.js build/integration/learn-repository-pipeline.integration.test.js build/integration/visual-diff-mcp.integration.test.js build/integration/executor-mcp-provisioning.integration.test.js build/integration/executor-worktree-reentry.integration.test.js build/integration/sfcc-read-error-contract.integration.test.js build/integration/sfcc-response-envelope.integration.test.js build/integration/sfcc-total-error-contract.integration.test.js build/integration/serve-stdio.integration.test.js build/integration/install-alias.integration.test.js build/integration/setup-epic-validation.integration.test.js build/integration/plane-cli.integration.test.js",
30
33
  "test:smoke": "node --test build/integration/packaged-cli-smoke.test.js",
31
34
  "canary:agent-capabilities": "npm run build && node scripts/agent-capabilities-canary.mjs",
32
35
  "prepublishOnly": "node scripts/bundle-assets.js && npm run build && node scripts/verify-shebang.cjs"
@@ -52,8 +55,8 @@
52
55
  "node": ">=18.0.0"
53
56
  },
54
57
  "overrides": {
55
- "fast-uri": ">=3.1.2",
56
- "hono": ">=4.12.31",
58
+ "fast-uri": ">=4.1.1",
59
+ "hono": ">=4.13.1",
57
60
  "@hono/node-server": "^1.19.13",
58
61
  "ip-address": ">=10.1.1"
59
62
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "implement-ticket",
3
- "description": "Generate plan, execute implementation, commit, open PR, and monitor CI.",
4
- "variables": ["ticket_key", "docs_dir"],
3
+ "description": "Generate plan, produce artifacts, checkpoint them to origin, run the plan's remaining verification faithfully, commit, open PR, close post-PR gaps, and monitor CI. Re-entrant: recovery jobs (ci_fix/remediate/rebase) safely re-run this whole recipe from step 1 — the plan-generation step reuses a fresh existing plan (reused: true, no new run) and the produce phase recognizes work already recorded in the durable ledger instead of redoing it.",
4
+ "variables": ["ticket_key", "docs_dir", "provider"],
5
5
  "steps": [
6
6
  {
7
7
  "type": "mcp_call",
@@ -18,12 +18,22 @@
18
18
  "save_locally": true,
19
19
  "provider": "{provider}"
20
20
  },
21
- "description": "Generate implementation plan"
21
+ "description": "Generate implementation plan. Re-entry-safe: a non-forced request against a fresh existing plan returns reused: true and runs nothing new — the expected fast path on recovery re-entry. force is reserved for deliberate regeneration; this step never sets it. On a GATEWAY_TIMEOUT/504 response, poll get_plan (or the response's recovery_get URL) until a terminal result — never reissue this step."
22
22
  },
23
23
  {
24
24
  "type": "agent_task",
25
25
  "instruction_file": "execute-plan.md",
26
- "description": "Execute the implementation plan"
26
+ "description": "Produce the plan's implementation artifacts"
27
+ },
28
+ {
29
+ "type": "agent_task",
30
+ "instruction_file": "checkpoint-work.md",
31
+ "description": "Checkpoint produced work to origin before verification begins"
32
+ },
33
+ {
34
+ "type": "agent_task",
35
+ "instruction_file": "execute-plan-verification.md",
36
+ "description": "Execute the plan's remaining review, test, and rendered-UI steps before the PR opens"
27
37
  },
28
38
  {
29
39
  "type": "agent_task",
@@ -36,6 +46,11 @@
36
46
  "instruction_file": "create-pr.md",
37
47
  "description": "Create a pull request with a title derived from the commit subject"
38
48
  },
49
+ {
50
+ "type": "agent_task",
51
+ "instruction_file": "verify-plan.md",
52
+ "description": "Close post-PR plan gaps and push any correction"
53
+ },
39
54
  {
40
55
  "type": "mcp_call",
41
56
  "tool": "update_jira_status",
@@ -1,141 +0,0 @@
1
- /**
2
- * TypeScript-to-Python judgment adapter (BAPI-396, conductor C4).
3
- *
4
- * Lets the Node conductor CLI invoke the approved Python LLM boundary
5
- * (`src.python.conductor.supervisor_judgment`) WITHOUT any direct model-provider
6
- * code in TypeScript. The compact judgment request is passed to the Python
7
- * module over stdin as JSON, the module is spawned with `shell: false` and a
8
- * list of arguments (never a shell string), and stdout is validated through the
9
- * shared {@link parseSupervisorJudgmentResponse}.
10
- *
11
- * Every boundary failure (missing Python, timeout, non-zero exit, malformed
12
- * output) is converted to a SANITIZED {@link SupervisorJudgmentError}: the
13
- * caller ({@link assessSupervisorCandidate}) catches it and degrades to a
14
- * deterministic assessment. Secrets, stderr text, and stack traces are never
15
- * placed in the thrown error.
16
- */
17
- import { spawn as nodeSpawn } from "node:child_process";
18
- import path from "node:path";
19
- import { fileURLToPath } from "node:url";
20
- import { SupervisorJudgmentError, parseSupervisorJudgmentResponse, } from "./supervisor-judgment.js";
21
- /** The Python module the adapter invokes via `python -m <module>`. */
22
- export const SUPERVISOR_JUDGMENT_PYTHON_MODULE = "src.python.conductor.supervisor_judgment";
23
- function nonEmpty(value) {
24
- return typeof value === "string" && value.trim().length > 0;
25
- }
26
- /**
27
- * Resolve the Python executable. Prefers the explicit, safe env override
28
- * `BAPI_CONDUCTOR_PYTHON`; otherwise falls back to `python3`. No shell string is
29
- * ever constructed — the returned value is used as `spawn`'s `command` arg.
30
- */
31
- export function resolveSupervisorJudgmentCommand(env = process.env) {
32
- if (nonEmpty(env.BAPI_CONDUCTOR_PYTHON))
33
- return env.BAPI_CONDUCTOR_PYTHON.trim();
34
- return "python3";
35
- }
36
- /**
37
- * Resolve the working directory the Python module runs from (the repo root, so
38
- * `src.python...` imports resolve). Prefers the explicit `BAPI_CONDUCTOR_PYTHON_CWD`
39
- * override; otherwise derives the repo root relative to this compiled module
40
- * (`<repo>/mcp_server/build/conductor/<file>.js` -> `<repo>`).
41
- */
42
- export function resolveSupervisorJudgmentCwd(env = process.env) {
43
- if (nonEmpty(env.BAPI_CONDUCTOR_PYTHON_CWD))
44
- return env.BAPI_CONDUCTOR_PYTHON_CWD.trim();
45
- const here = fileURLToPath(import.meta.url);
46
- // dirname=conductor, ../=build, ../../=mcp_server, ../../../=repo root.
47
- return path.resolve(path.dirname(here), "..", "..", "..");
48
- }
49
- /** Build the secret-free stdin payload for the Python module. */
50
- function buildRequestPayload(request, env) {
51
- const payload = {
52
- run_id: request.run_id,
53
- candidate: request.candidate,
54
- worker: request.worker,
55
- };
56
- if (nonEmpty(env.BAPI_CONDUCTOR_REPO_NAME))
57
- payload.repo_name = env.BAPI_CONDUCTOR_REPO_NAME.trim();
58
- if (nonEmpty(env.BAPI_CONDUCTOR_RUN_ID))
59
- payload.session_id = env.BAPI_CONDUCTOR_RUN_ID.trim();
60
- return payload;
61
- }
62
- /**
63
- * Spawn the Python judgment module and resolve with the validated response.
64
- * Rejects with a sanitized {@link SupervisorJudgmentError} on any boundary
65
- * failure. The request is passed over stdin (keeping payloads out of the process
66
- * argument list); the timeout is `config.llm_timeout_ms`.
67
- */
68
- export function requestPythonSupervisorJudgment(request, config, deps = {}) {
69
- const spawnFn = deps.spawn ?? nodeSpawn;
70
- const env = deps.env ?? process.env;
71
- const command = resolveSupervisorJudgmentCommand(env);
72
- const cwd = resolveSupervisorJudgmentCwd(env);
73
- return new Promise((resolve, reject) => {
74
- let settled = false;
75
- let stdout = "";
76
- let child;
77
- try {
78
- child = spawnFn(command, ["-m", SUPERVISOR_JUDGMENT_PYTHON_MODULE], {
79
- cwd,
80
- shell: false,
81
- stdio: ["pipe", "pipe", "pipe"],
82
- });
83
- }
84
- catch {
85
- reject(new SupervisorJudgmentError("python judgment process could not be started"));
86
- return;
87
- }
88
- const finish = (fn, value) => {
89
- if (settled)
90
- return;
91
- settled = true;
92
- clearTimeout(timer);
93
- fn(value);
94
- };
95
- // NOTE: the timer is intentionally NOT unref'd. It must keep the event loop
96
- // alive so the timeout actually fires and rejects when the Python subprocess
97
- // hangs (and so the deterministic degraded path is taken). The timer is
98
- // always cleared in `finish`, so it never outlives a settled judgment.
99
- const timer = setTimeout(() => {
100
- try {
101
- child.kill("SIGKILL");
102
- }
103
- catch {
104
- /* best-effort */
105
- }
106
- finish(reject, new SupervisorJudgmentError("python judgment timed out"));
107
- }, config.llm_timeout_ms);
108
- child.on("error", () => finish(reject, new SupervisorJudgmentError("python judgment process error")));
109
- child.stdout?.on("data", (chunk) => {
110
- stdout += String(chunk);
111
- });
112
- child.on("close", (code) => {
113
- if (code !== 0) {
114
- finish(reject, new SupervisorJudgmentError("python judgment exited non-zero"));
115
- return;
116
- }
117
- try {
118
- const parsed = parseSupervisorJudgmentResponse(stdout.trim());
119
- finish(resolve, parsed);
120
- }
121
- catch {
122
- finish(reject, new SupervisorJudgmentError("python judgment returned malformed output"));
123
- }
124
- });
125
- try {
126
- child.stdin?.write(JSON.stringify(buildRequestPayload(request, env)));
127
- child.stdin?.end();
128
- }
129
- catch {
130
- finish(reject, new SupervisorJudgmentError("python judgment stdin write failed"));
131
- }
132
- });
133
- }
134
- /**
135
- * Build the default injectable judgment client used by the runtime. The returned
136
- * function matches {@link SupervisorJudgmentClient}; it forwards each request to
137
- * {@link requestPythonSupervisorJudgment} with the resolved config/deps.
138
- */
139
- export function createDefaultSupervisorJudgmentClient(config, deps = {}) {
140
- return (request) => requestPythonSupervisorJudgment(request, config, deps);
141
- }