@bridge_gpt/mcp-server 0.2.34 → 0.2.36

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 (49) hide show
  1. package/README.md +456 -370
  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/claude-review-workflow.js +264 -0
  6. package/build/cli-release.js +53 -0
  7. package/build/commands.generated.js +4 -4
  8. package/build/conductor/bridge-api-client.js +215 -0
  9. package/build/conductor/deny-enforcement-preflight.js +1 -0
  10. package/build/conductor/done-gate.js +44 -5
  11. package/build/conductor/epic-reconcile.js +6 -0
  12. package/build/conductor/install-doctor.js +462 -0
  13. package/build/conductor-bin.js +3 -3
  14. package/build/conductor-bundle-artifacts.js +30 -9
  15. package/build/doctor.js +234 -1
  16. package/build/executor/cli.js +32 -5
  17. package/build/executor/credentials.js +45 -11
  18. package/build/executor/deps.js +14 -0
  19. package/build/executor/env.js +23 -6
  20. package/build/executor/index.js +4 -0
  21. package/build/executor/job-runner.js +119 -9
  22. package/build/executor/permissions.js +12 -2
  23. package/build/executor/preflight.js +95 -8
  24. package/build/executor/prompt-spec.js +51 -0
  25. package/build/executor/runner.js +15 -2
  26. package/build/executor/service-unit.js +876 -0
  27. package/build/executor/test-clock.js +8 -0
  28. package/build/executor/types.js +0 -17
  29. package/build/executor/worker-command.js +62 -9
  30. package/build/index.js +575 -143
  31. package/build/init.js +153 -51
  32. package/build/install-bridge-conductor.js +491 -0
  33. package/build/install-bridge.js +628 -175
  34. package/build/install-reexec.js +233 -0
  35. package/build/mcp-host-config.js +11 -1
  36. package/build/mcp-install-state.js +32 -0
  37. package/build/mcp-provisioning.js +22 -6
  38. package/build/pipelines.generated.js +14 -8
  39. package/build/readme.generated.js +1 -1
  40. package/build/run-unit-tests-launcher.js +257 -0
  41. package/build/setup-epic.js +117 -8
  42. package/build/upgrade-cli.js +1 -15
  43. package/build/version.generated.js +1 -1
  44. package/docs/CONDUCTOR.md +115 -4
  45. package/docs/install/mcp-tool-integrations.md +29 -21
  46. package/package.json +8 -5
  47. package/pipelines/implement-ticket.json +6 -1
  48. package/build/conductor/supervisor-judgment-python.js +0 -141
  49. package/build/conductor/supervisor-judgment.js +0 -215
@@ -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.36",
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/serve-stdio.integration.test.js build/integration/install-alias.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,7 +55,7 @@
52
55
  "node": ">=18.0.0"
53
56
  },
54
57
  "overrides": {
55
- "fast-uri": ">=3.1.2",
58
+ "fast-uri": ">=4.1.1",
56
59
  "hono": ">=4.12.31",
57
60
  "@hono/node-server": "^1.19.13",
58
61
  "ip-address": ">=10.1.1"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "implement-ticket",
3
- "description": "Generate plan, execute implementation, commit, open PR, and monitor CI.",
3
+ "description": "Generate plan, execute implementation, commit, open PR, verify, and monitor CI.",
4
4
  "variables": ["ticket_key", "docs_dir"],
5
5
  "steps": [
6
6
  {
@@ -36,6 +36,11 @@
36
36
  "instruction_file": "create-pr.md",
37
37
  "description": "Create a pull request with a title derived from the commit subject"
38
38
  },
39
+ {
40
+ "type": "agent_task",
41
+ "instruction_file": "verify-plan.md",
42
+ "description": "Run bounded post-finalization verification and push any correction"
43
+ },
39
44
  {
40
45
  "type": "mcp_call",
41
46
  "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
- }
@@ -1,215 +0,0 @@
1
- /**
2
- * LLM judgment boundary for the conductor supervisor (BAPI-396, conductor C4).
3
- *
4
- * The LLM is JUDGMENT-ONLY. It is consulted solely to classify ambiguous stalls
5
- * and to draft short escalation text — compact snapshot in, strict JSON out. It
6
- * NEVER executes privileged actions and is NEVER the source of truth: a
7
- * malformed, disabled, exhausted, or timed-out model call degrades to a
8
- * deterministic assessment. This module is pure prompt/parse/budget logic with
9
- * an injectable client and emits NO events.
10
- */
11
- /** Raised when a model response cannot be trusted as a valid judgment. */
12
- export class SupervisorJudgmentError extends Error {
13
- constructor(message) {
14
- super(message);
15
- this.name = "SupervisorJudgmentError";
16
- }
17
- }
18
- /** Classifications the supervisor accepts from the judgment model. */
19
- export const ALLOWED_JUDGMENT_CLASSIFICATIONS = new Set([
20
- "progressing",
21
- "ambiguous",
22
- "stuck",
23
- "blocked",
24
- "unknown",
25
- ]);
26
- /**
27
- * Build the judgment-only system prompt. Uses `## HEADERS ##` sections, states
28
- * the model only classifies stalls / drafts escalation text, prohibits claiming
29
- * to execute actions, and requires strict JSON output.
30
- */
31
- export function buildSupervisorAssessmentSystemPrompt() {
32
- return [
33
- "## ROLE ##",
34
- "You are a read-only watchdog assistant for a multi-agent coding supervisor.",
35
- "You classify whether a worker appears stuck and optionally draft a short, human-readable escalation note.",
36
- "",
37
- "## HARD CONSTRAINTS ##",
38
- "- You ONLY classify ambiguous stalls and draft escalation text.",
39
- "- You have NO ability to take actions. Never claim to have executed, killed, merged, retried, or fixed anything.",
40
- "- You are NEVER the source of truth; deterministic signals override your judgment.",
41
- "- Do not request or reveal secrets, tokens, or raw payloads.",
42
- "",
43
- "## OUTPUT FORMAT ##",
44
- "Return STRICT JSON only — no prose, no markdown fences. The object must have exactly these keys:",
45
- ' "classification": one of "progressing" | "ambiguous" | "stuck" | "blocked" | "unknown"',
46
- ' "confidence": a number between 0 and 1',
47
- ' "should_escalate": a boolean',
48
- ' "reason": a short machine reason string',
49
- ' "draft_escalation_text": a short human-readable string, or null',
50
- ].join("\n");
51
- }
52
- /**
53
- * Build the user prompt from a compact judgment request. The dynamic JSON
54
- * context is wrapped in triple quotes; only compact, secret-free fields are
55
- * included (the request itself must already be secret-free).
56
- */
57
- export function buildSupervisorAssessmentUserPrompt(request) {
58
- const compact = {
59
- run_id: request.run_id,
60
- candidate: {
61
- reason: request.candidate.reason,
62
- state: request.candidate.state,
63
- liveness: request.candidate.liveness,
64
- elapsed_ms: request.candidate.elapsed_ms,
65
- context: request.candidate.context,
66
- },
67
- worker: request.worker,
68
- };
69
- return [
70
- "## SUPERVISOR SNAPSHOT ##",
71
- "Classify the worker situation below and decide whether it warrants escalation.",
72
- "",
73
- '"""',
74
- JSON.stringify(compact, null, 2),
75
- '"""',
76
- ].join("\n");
77
- }
78
- /** Top-level keys that, if present, mark a response as action-like (rejected). */
79
- const ACTION_LIKE_KEYS = new Set([
80
- "executed",
81
- "action",
82
- "actions",
83
- "command",
84
- "commands",
85
- "kill",
86
- "killed",
87
- "merge",
88
- "merged",
89
- "transition",
90
- "deleted",
91
- "wrote",
92
- "mutated",
93
- "ran",
94
- ]);
95
- /** Phrases in drafted text that assert a privileged action was performed. */
96
- const ACTION_LIKE_PHRASES = [
97
- /\bi (?:have )?(?:killed|merged|executed|ran|deleted|restarted|retried|fixed|committed|pushed)\b/i,
98
- /\bhas been (?:killed|merged|executed|restarted|deleted)\b/i,
99
- /\bworker (?:killed|terminated|restarted)\b/i,
100
- ];
101
- /** Parse a JSON value (string or already-parsed object). */
102
- function asObject(raw) {
103
- let value = raw;
104
- if (typeof raw === "string") {
105
- try {
106
- value = JSON.parse(raw);
107
- }
108
- catch {
109
- throw new SupervisorJudgmentError("judgment response is not valid JSON");
110
- }
111
- }
112
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
113
- throw new SupervisorJudgmentError("judgment response must be a JSON object");
114
- }
115
- return value;
116
- }
117
- /**
118
- * Parse + validate a strict-JSON judgment response. Rejects non-objects,
119
- * malformed JSON, unknown classifications, missing/typed-wrong fields, and any
120
- * action-like output (forbidden keys or draft text claiming privileged work).
121
- * Throws {@link SupervisorJudgmentError} on any violation.
122
- */
123
- export function parseSupervisorJudgmentResponse(raw) {
124
- const obj = asObject(raw);
125
- // Reject action-like keys outright.
126
- for (const key of Object.keys(obj)) {
127
- if (ACTION_LIKE_KEYS.has(key.toLowerCase())) {
128
- throw new SupervisorJudgmentError(`judgment response contains forbidden action key "${key}"`);
129
- }
130
- }
131
- const classification = obj.classification;
132
- if (typeof classification !== "string" || !ALLOWED_JUDGMENT_CLASSIFICATIONS.has(classification)) {
133
- throw new SupervisorJudgmentError("judgment response has an invalid 'classification'");
134
- }
135
- const confidence = obj.confidence;
136
- if (typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
137
- throw new SupervisorJudgmentError("judgment response 'confidence' must be a number in [0,1]");
138
- }
139
- const shouldEscalate = obj.should_escalate;
140
- if (typeof shouldEscalate !== "boolean") {
141
- throw new SupervisorJudgmentError("judgment response 'should_escalate' must be a boolean");
142
- }
143
- const reason = obj.reason;
144
- if (typeof reason !== "string" || reason.trim().length === 0) {
145
- throw new SupervisorJudgmentError("judgment response 'reason' must be a non-empty string");
146
- }
147
- let draft = null;
148
- const draftRaw = obj.draft_escalation_text;
149
- if (draftRaw !== null && draftRaw !== undefined) {
150
- if (typeof draftRaw !== "string") {
151
- throw new SupervisorJudgmentError("judgment response 'draft_escalation_text' must be a string or null");
152
- }
153
- for (const pattern of ACTION_LIKE_PHRASES) {
154
- if (pattern.test(draftRaw)) {
155
- throw new SupervisorJudgmentError("judgment draft text claims a privileged action");
156
- }
157
- }
158
- draft = draftRaw;
159
- }
160
- return {
161
- classification,
162
- confidence,
163
- should_escalate: shouldEscalate,
164
- reason,
165
- draft_escalation_text: draft,
166
- };
167
- }
168
- /** Build the deterministic, degraded-mode assessment for a candidate. */
169
- function degradedAssessment(candidate, reason) {
170
- return {
171
- classification: "unknown",
172
- confidence: 0,
173
- // Degraded mode never SUPPRESSES a surfaced stall: a candidate the
174
- // deterministic layer already flagged stays escalated.
175
- should_escalate: true,
176
- reason: `${candidate.reason}:${reason}`,
177
- draft_escalation_text: null,
178
- source: "degraded",
179
- };
180
- }
181
- /**
182
- * Assess one escalation candidate. Enforces the LLM budget BEFORE calling the
183
- * injectable client: when the LLM is disabled, the budget is exhausted, or the
184
- * client times out / errors / returns malformed output, a deterministic
185
- * degraded-mode assessment is returned. On a successful call `budget.used_calls`
186
- * is incremented. This function emits NO events.
187
- */
188
- export async function assessSupervisorCandidate(request, config, budget, client) {
189
- const candidate = request.candidate;
190
- if (!config.llm_enabled || !budget.enabled || budget.max_calls <= 0) {
191
- return degradedAssessment(candidate, "llm_disabled");
192
- }
193
- if (budget.used_calls >= budget.max_calls) {
194
- return degradedAssessment(candidate, "budget_exhausted");
195
- }
196
- // Count the attempt against the budget regardless of outcome so a flaky model
197
- // cannot be retried unboundedly within a single run.
198
- budget.used_calls += 1;
199
- let response;
200
- try {
201
- const raw = await client(request);
202
- response = parseSupervisorJudgmentResponse(raw);
203
- }
204
- catch {
205
- return degradedAssessment(candidate, "llm_failed");
206
- }
207
- return {
208
- classification: response.classification,
209
- confidence: response.confidence,
210
- should_escalate: response.should_escalate,
211
- reason: response.reason,
212
- draft_escalation_text: response.draft_escalation_text,
213
- source: "llm",
214
- };
215
- }