@bridge_gpt/mcp-server 0.2.25 → 0.2.26

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.
@@ -0,0 +1,396 @@
1
+ /**
2
+ * tool-surface-gating — the fail-open MCP tool-surface capability consumer
3
+ * (BAPI-641).
4
+ *
5
+ * This module owns the client-side half of dynamic capability gating. The
6
+ * backend `GET /jira/mcp/tool-surface` route (BAPI-640) reports the set of
7
+ * physical tool IDs it would hard-block for a repo right now. This module:
8
+ *
9
+ * 1. probes that endpoint under one absolute 500 ms deadline (`probeToolSurface`),
10
+ * always resolving to a result union — never rejecting;
11
+ * 2. installs a custom `tools/list` handler that delegates to the SDK's own
12
+ * handler and then SUBTRACTS the capability-hidden tool names, intersected
13
+ * with the locally advertised profile surface (`createToolSurfaceGate`);
14
+ * 3. re-probes on a jittered 12–18 s poll and emits `tools/list_changed` only
15
+ * when the effective visible-name set actually changes.
16
+ *
17
+ * Design invariants:
18
+ * - FAIL-OPEN. A timeout, unreachable backend, non-2xx, malformed payload,
19
+ * `complete: false`, or unsupported schema all advertise the FULL profile
20
+ * surface (empty hidden set). The backend, not this module, is the
21
+ * authoritative enforcement boundary.
22
+ * - NEVER `.disable()`. Capability gating only subtracts from `tools/list`
23
+ * projection. It must NEVER call `.disable()` or mutate the SDK `enabled`
24
+ * flag, because doing so would ALSO block `tools/call` and the in-process
25
+ * `TOOL_HANDLERS` dispatch — preventing a stale client call from reaching
26
+ * the authoritative backend refusal.
27
+ * - SECRET-SAFE. Logs and result unions carry only stable reason/subtype
28
+ * vocabulary, secret-free tool IDs, counts, and catalog revisions — never
29
+ * URLs, headers, response bodies, credentials, or raw exceptions.
30
+ */
31
+ import { getMethodLiteral } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
32
+ // ---------------------------------------------------------------------------
33
+ // Protocol + timing constants
34
+ // ---------------------------------------------------------------------------
35
+ /** The `/mcp/tool-surface` surface-contract versions this consumer recognizes. */
36
+ export const RECOGNIZED_TOOL_SURFACE_SCHEMA_VERSIONS = new Set([1]);
37
+ /** The single recognized schema version (convenience export for tests). */
38
+ export const TOOL_SURFACE_SCHEMA_VERSION = 1;
39
+ /** Absolute end-to-end deadline for one probe (header resolve + fetch + parse). */
40
+ export const TOOL_SURFACE_PROBE_DEADLINE_MS = 500;
41
+ /** Inclusive lower bound of the recurring-poll jitter window. */
42
+ export const TOOL_SURFACE_POLL_MIN_MS = 12_000;
43
+ /** Inclusive upper bound of the recurring-poll jitter window. */
44
+ export const TOOL_SURFACE_POLL_MAX_MS = 18_000;
45
+ function timeoutResult() {
46
+ return { reason: "timeout", blockedTools: new Set() };
47
+ }
48
+ function malformedResult(subtype) {
49
+ return { reason: "malformed", subtype, blockedTools: new Set() };
50
+ }
51
+ /**
52
+ * Validate a parsed JSON body against the exact backend contract. Returns a
53
+ * `blocked` decision for a valid, complete, recognized response (including one
54
+ * with zero blocked tools) or a fail-open `malformed` decision with a sanitized
55
+ * subtype. An unfamiliar `catalog_revision` is INFORMATIONAL and never a
56
+ * fail-open trigger.
57
+ */
58
+ export function validateToolSurfacePayload(body) {
59
+ if (body === null || typeof body !== "object" || Array.isArray(body)) {
60
+ return malformedResult("invalid-shape");
61
+ }
62
+ const p = body;
63
+ if (typeof p.schema_version !== "number" ||
64
+ !Number.isInteger(p.schema_version)) {
65
+ return malformedResult("invalid-shape");
66
+ }
67
+ if (!RECOGNIZED_TOOL_SURFACE_SCHEMA_VERSIONS.has(p.schema_version)) {
68
+ return malformedResult("unsupported-schema");
69
+ }
70
+ if (typeof p.complete !== "boolean") {
71
+ return malformedResult("invalid-shape");
72
+ }
73
+ if (typeof p.evaluated_tool_count !== "number" ||
74
+ !Number.isInteger(p.evaluated_tool_count) ||
75
+ p.evaluated_tool_count < 0) {
76
+ return malformedResult("invalid-shape");
77
+ }
78
+ if (typeof p.catalog_revision !== "string") {
79
+ return malformedResult("invalid-shape");
80
+ }
81
+ if (!Array.isArray(p.blocked_tools) ||
82
+ !p.blocked_tools.every((t) => typeof t === "string")) {
83
+ return malformedResult("invalid-shape");
84
+ }
85
+ if (!p.complete) {
86
+ return malformedResult("incomplete");
87
+ }
88
+ // A complete response MUST carry a non-empty catalog revision.
89
+ if (p.catalog_revision.length === 0) {
90
+ return malformedResult("invalid-shape");
91
+ }
92
+ // Insertion-preserving set; duplicates collapse harmlessly. Unknown physical
93
+ // IDs remain valid at the probe boundary — the later projection intersects
94
+ // them with the locally advertised surface.
95
+ const blockedTools = new Set(p.blocked_tools);
96
+ return {
97
+ reason: "blocked",
98
+ catalogRevision: p.catalog_revision,
99
+ evaluatedToolCount: p.evaluated_tool_count,
100
+ blockedTools,
101
+ };
102
+ }
103
+ /**
104
+ * Issue ONE authenticated GET to the tool-surface endpoint and classify the
105
+ * result under a single absolute deadline that covers async header resolution,
106
+ * the fetch, response-body consumption, JSON parsing, and validation.
107
+ *
108
+ * This function NEVER rejects: every failure — timeout, lifecycle abort, network
109
+ * error, non-2xx, invalid JSON, invalid shape, incomplete evaluation, or
110
+ * unsupported schema — resolves to a fail-open result union. That contract lets
111
+ * the non-awaited startup probe run without risking an unhandled rejection.
112
+ */
113
+ export async function probeToolSurface(options) {
114
+ const deadlineMs = options.deadlineMs ?? TOOL_SURFACE_PROBE_DEADLINE_MS;
115
+ // Combined abort controller: the deadline timer OR the lifecycle signal aborts
116
+ // an in-flight fetch. Composing manually (rather than AbortSignal.any) keeps
117
+ // the runtime floor low and the abort wiring explicit.
118
+ const controller = new AbortController();
119
+ const onLifecycleAbort = () => controller.abort();
120
+ if (options.abortSignal) {
121
+ if (options.abortSignal.aborted)
122
+ controller.abort();
123
+ else
124
+ options.abortSignal.addEventListener("abort", onLifecycleAbort, {
125
+ once: true,
126
+ });
127
+ }
128
+ let timer;
129
+ const deadlinePromise = new Promise((resolve) => {
130
+ timer = setTimeout(() => {
131
+ controller.abort();
132
+ resolve(timeoutResult());
133
+ }, deadlineMs);
134
+ });
135
+ // Resolve promptly to timeout on any abort, regardless of which async phase is
136
+ // pending (header resolution can never begin a fetch, so it needs this too).
137
+ const abortPromise = new Promise((resolve) => {
138
+ if (controller.signal.aborted) {
139
+ resolve(timeoutResult());
140
+ return;
141
+ }
142
+ controller.signal.addEventListener("abort", () => resolve(timeoutResult()), {
143
+ once: true,
144
+ });
145
+ });
146
+ const workPromise = (async () => {
147
+ try {
148
+ const headers = await options.resolveHeaders();
149
+ if (controller.signal.aborted)
150
+ return timeoutResult();
151
+ const resp = await options.fetchFn(options.url, {
152
+ method: "GET",
153
+ headers,
154
+ signal: controller.signal,
155
+ });
156
+ if (!resp.ok)
157
+ return malformedResult("non-2xx");
158
+ let parsed;
159
+ try {
160
+ parsed = await resp.json();
161
+ }
162
+ catch {
163
+ if (controller.signal.aborted)
164
+ return timeoutResult();
165
+ return malformedResult("invalid-json");
166
+ }
167
+ return validateToolSurfacePayload(parsed);
168
+ }
169
+ catch {
170
+ // Abort (deadline or lifecycle) surfaces here as a rejected fetch. Any
171
+ // other rejection is a network-class failure. Raw exception text is never
172
+ // propagated into the result.
173
+ if (controller.signal.aborted)
174
+ return timeoutResult();
175
+ return malformedResult("network");
176
+ }
177
+ })();
178
+ try {
179
+ return await Promise.race([workPromise, deadlinePromise, abortPromise]);
180
+ }
181
+ finally {
182
+ if (timer)
183
+ clearTimeout(timer);
184
+ if (options.abortSignal) {
185
+ options.abortSignal.removeEventListener("abort", onLifecycleAbort);
186
+ }
187
+ }
188
+ }
189
+ const defaultScheduler = {
190
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
191
+ clearTimeout: (handle) => clearTimeout(handle),
192
+ random: () => Math.random(),
193
+ };
194
+ /** Log a gating decision with secret-free fields only. */
195
+ function logDecision(logger, result, hiddenCount, hiddenNames) {
196
+ const revision = result.reason === "blocked" ? result.catalogRevision : "n/a";
197
+ const subtype = result.reason === "malformed" ? result.subtype : "n/a";
198
+ logger(`tool-surface gating: reason=${result.reason} subtype=${subtype} ` +
199
+ `hidden=${hiddenCount} revision=${revision} ` +
200
+ `hidden_tools=[${hiddenNames.join(", ")}]`);
201
+ }
202
+ /**
203
+ * Build the capability gate. The gate wraps the SDK `tools/list` handler so the
204
+ * FIRST list awaits the stored startup probe, atomically applies the resulting
205
+ * hidden-set snapshot, delegates to the SDK handler, and filters ONLY the
206
+ * capability-hidden names out of the returned tools (preserving every SDK tool
207
+ * definition object, its order, and all non-`tools` response properties).
208
+ */
209
+ export function createToolSurfaceGate(options) {
210
+ const { startupProbe, advertised, originalListHandler, freshProbe, notify, logger, lifecycleController, } = options;
211
+ const scheduler = options.scheduler ?? defaultScheduler;
212
+ const advertisedNames = new Set(advertised.map((r) => r.name));
213
+ // Immutable-replacement state. `hiddenNames` is always swapped as a whole
214
+ // snapshot so a list request or poll completion never observes a partial set.
215
+ let hiddenNames = new Set();
216
+ // The last visible-name set actually served to a client, or null before the
217
+ // first list. Notifications are suppressed until a client has been served.
218
+ let lastServedVisible = null;
219
+ let catalogRevision = null;
220
+ let startupApplied = false;
221
+ let timer;
222
+ let closed = false;
223
+ /** Derive the effective hidden set: backend IDs ∩ advertised names. */
224
+ function deriveHidden(result) {
225
+ if (result.reason !== "blocked" || result.blockedTools.size === 0) {
226
+ return new Set();
227
+ }
228
+ const hidden = new Set();
229
+ for (const id of result.blockedTools) {
230
+ if (advertisedNames.has(id))
231
+ hidden.add(id);
232
+ }
233
+ return hidden;
234
+ }
235
+ /**
236
+ * Derive the visible-name set: advertised registrations that are BOTH
237
+ * currently SDK-enabled AND not capability-hidden. This intersects profile
238
+ * registration (advertised), current enabled state, and capability hiding.
239
+ */
240
+ function deriveVisible(hidden) {
241
+ const visible = new Set();
242
+ for (const reg of advertised) {
243
+ if (!reg.isEnabled())
244
+ continue;
245
+ if (hidden.has(reg.name))
246
+ continue;
247
+ visible.add(reg.name);
248
+ }
249
+ return visible;
250
+ }
251
+ /**
252
+ * Apply a probe decision as an immutable snapshot: swap `hiddenNames`, log,
253
+ * and track catalog-revision transitions. Does NOT notify — callers decide.
254
+ */
255
+ function applyDecision(result) {
256
+ const nextHidden = deriveHidden(result);
257
+ hiddenNames = nextHidden;
258
+ logDecision(logger, result, nextHidden.size, Array.from(nextHidden));
259
+ if (result.reason === "blocked" && result.catalogRevision !== catalogRevision) {
260
+ if (catalogRevision !== null) {
261
+ logger(`tool-surface gating: catalog_revision ${catalogRevision} -> ${result.catalogRevision}`);
262
+ }
263
+ catalogRevision = result.catalogRevision;
264
+ }
265
+ }
266
+ /** Project the SDK list result, subtracting only capability-hidden names. */
267
+ function projectList(original) {
268
+ const tools = original.tools.filter((tool) => !hiddenNames.has(tool.name));
269
+ return { ...original, tools };
270
+ }
271
+ const handleList = async (request, extra) => {
272
+ // First list awaits the SAME stored startup promise, then applies its
273
+ // snapshot exactly once (concurrent first calls both await; only the first
274
+ // to run after resolution applies — JS runs applyDecision without preemption).
275
+ const startupResult = await startupProbe;
276
+ if (!startupApplied) {
277
+ startupApplied = true;
278
+ applyDecision(startupResult);
279
+ }
280
+ const original = await originalListHandler(request, extra);
281
+ const projected = projectList(original);
282
+ // Record the first served visible-name baseline (from what we actually
283
+ // serve). Subsequent lists refresh it so poll-driven notifications compare
284
+ // against the latest served surface.
285
+ lastServedVisible = new Set(projected.tools.map((t) => t.name));
286
+ return projected;
287
+ };
288
+ /** One poll cycle: fresh probe, apply, notify only on a real visible change. */
289
+ async function pollOnce() {
290
+ let result;
291
+ try {
292
+ result = await freshProbe();
293
+ }
294
+ catch {
295
+ // freshProbe is contracted never to reject, but stay fail-open regardless.
296
+ result = timeoutResult();
297
+ }
298
+ if (closed)
299
+ return;
300
+ const previousVisibleServed = lastServedVisible;
301
+ applyDecision(result);
302
+ const nextVisible = deriveVisible(hiddenNames);
303
+ // Suppress notifications before any client has received its first list.
304
+ if (previousVisibleServed === null)
305
+ return;
306
+ if (!setsEqual(previousVisibleServed, nextVisible)) {
307
+ // The served surface changes; update the baseline BEFORE notifying so a
308
+ // notification-driven re-list compares against the new baseline.
309
+ lastServedVisible = nextVisible;
310
+ try {
311
+ notify();
312
+ }
313
+ catch {
314
+ // A notification failure must not revert state or stop future polls.
315
+ logger("tool-surface gating: notification failed (suppressed)");
316
+ }
317
+ }
318
+ }
319
+ /** Schedule the next poll after the previous one settles (recursive setTimeout). */
320
+ function scheduleNext() {
321
+ if (closed)
322
+ return;
323
+ const span = TOOL_SURFACE_POLL_MAX_MS - TOOL_SURFACE_POLL_MIN_MS;
324
+ const delay = Math.round(TOOL_SURFACE_POLL_MIN_MS + scheduler.random() * span);
325
+ timer = scheduler.setTimeout(() => {
326
+ // Run one probe to completion, THEN schedule the next — never overlapping.
327
+ void pollOnce().finally(() => {
328
+ scheduleNext();
329
+ });
330
+ }, delay);
331
+ // Do not let the poll timer keep the process alive.
332
+ if (timer && typeof timer.unref === "function")
333
+ timer.unref();
334
+ }
335
+ function startPolling() {
336
+ if (closed)
337
+ return;
338
+ scheduleNext();
339
+ }
340
+ function close() {
341
+ if (closed)
342
+ return;
343
+ closed = true;
344
+ if (timer) {
345
+ scheduler.clearTimeout(timer);
346
+ timer = undefined;
347
+ }
348
+ if (!lifecycleController.signal.aborted)
349
+ lifecycleController.abort();
350
+ }
351
+ return { handleList, startPolling, close };
352
+ }
353
+ /** Value-equality for two string sets. */
354
+ function setsEqual(a, b) {
355
+ if (a.size !== b.size)
356
+ return false;
357
+ for (const v of a) {
358
+ if (!b.has(v))
359
+ return false;
360
+ }
361
+ return true;
362
+ }
363
+ /** Fixed, sanitized boot error (never includes map contents or config). */
364
+ const COMPAT_ERROR = "tool-surface gating: incompatible MCP SDK — the tools/list handler could not be resolved for override.";
365
+ /**
366
+ * Install the custom `tools/list` handler over the SDK's, returning the captured
367
+ * original handler for delegation. Resolves the method literal from the schema
368
+ * and asserts it is exactly `tools/list`; asserts a present, callable existing
369
+ * handler; then overwrites via `setRequestHandler`. Throws the fixed sanitized
370
+ * boot error (no private map contents, config, or credentials) on any mismatch.
371
+ */
372
+ export function installToolSurfaceListOverride(protocolServer, listSchema, customHandler) {
373
+ let method;
374
+ try {
375
+ method = getMethodLiteral(listSchema);
376
+ }
377
+ catch {
378
+ throw new Error(COMPAT_ERROR);
379
+ }
380
+ if (method !== "tools/list") {
381
+ throw new Error(COMPAT_ERROR);
382
+ }
383
+ const handlers = protocolServer?._requestHandlers;
384
+ if (!handlers || typeof handlers.get !== "function") {
385
+ throw new Error(COMPAT_ERROR);
386
+ }
387
+ const original = handlers.get(method);
388
+ if (typeof original !== "function") {
389
+ throw new Error(COMPAT_ERROR);
390
+ }
391
+ // Overwrite in place. We deliberately do NOT rely on removeRequestHandler's
392
+ // return value and do not remove the handler first — setRequestHandler
393
+ // replaces the existing entry.
394
+ protocolServer.setRequestHandler(listSchema, customHandler);
395
+ return original;
396
+ }
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.25";
2
+ export const VERSION = "0.2.26";
@@ -163,7 +163,7 @@ by tier.
163
163
  | `generate_image` | — none beyond an image provider |
164
164
  | `visual_diff` | — none (`LOCAL` render + pixel diff; needs a reachable `target_url`) |
165
165
  | `request_deep_research` / `get_deep_research` | Deep-research flag **[BLOCK]** (403 if `deep_research_enabled` off) |
166
- | `request_brainstorm` / `get_brainstorm` | Code index **[BLOCK] for `technical`/`discovery` modes**; **— none for `design` mode** |
166
+ | `request_council` / `get_council` | Code index **[BLOCK] for `technical`/`discovery` modes**; **— none for `design` mode** |
167
167
 
168
168
  ### VCS & CI
169
169
 
@@ -279,7 +279,7 @@ sandbox-only and destructive.
279
279
  | **Ticket backend** (jira mode) | `create_ticket`, `get_ticket(s)`, `update_ticket_description`, `update_jira_status`, `get_jira_transitions`, and every AI generator (they read the ticket) | — |
280
280
  | **Jira (only)** | `get_comments`, `add_comment`, `attachment`, `estimate_epic` | — |
281
281
  | **Version control (VCS)** | `create_pull_request`, `resolve_ci_checks`, `poll_ci_checks`, `materialize_fresh_base`, `parse_repository`, `regenerate_directory_map`, `wait_for_done_gate`; **Tier-3** `request_plan_generation`/`get_plan`, `request_architecture`/`get_architecture`, `request_reimplement_context`/`get_reimplement_context`, `create_doc`(tdd/architecture) | **Tier-4** `request_clarifying_questions`, `request_ticket_critique`, `request_ticket_review`, `request_prd`, `create_doc`(prd/fsd) |
282
- | **Code index** (succeeded parse) | Tier-3 plan/architecture/reimplement/`create_doc`(tdd/architecture); `request_brainstorm` in `technical`/`discovery` modes | Tier-4 clarifying-questions/critique/review/prd/fsd |
282
+ | **Code index** (succeeded parse) | Tier-3 plan/architecture/reimplement/`create_doc`(tdd/architecture); `request_council` in `technical`/`discovery` modes | Tier-4 clarifying-questions/critique/review/prd/fsd |
283
283
  | **SFCC OCAPI** | `check_permissions` + all 16 SFCC read/write tools | — |
284
284
  | **SFCC WebDAV logs** | `sfcc_log_query` | — |
285
285
  | **Deep-research flag** | `request_deep_research`, `get_deep_research` | — |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.25",
3
+ "version": "0.2.26",
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",
@@ -27,14 +27,14 @@
27
27
  "check:version-generated": "node scripts/bundle-version.js && node scripts/check-version-generated.js",
28
28
  "postbuild": "node scripts/prepend-shebang.cjs",
29
29
  "start": "node build/index.js",
30
- "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/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/credential-materialization.test.js build/mcp-registration-doctor.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/init.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/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/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/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",
31
- "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/start-tickets.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/install-bridge-repo-resolution.integration.test.js",
30
+ "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/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/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/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",
31
+ "test:integration": "node --test build/integration/refresh-main.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",
32
32
  "test:smoke": "node --test build/integration/packaged-cli-smoke.test.js",
33
33
  "prepublishOnly": "node scripts/bundle-assets.js && npm run build && node scripts/verify-shebang.cjs"
34
34
  },
35
35
  "dependencies": {
36
36
  "@modelcontextprotocol/sdk": "^1.29.0",
37
- "pixelmatch": "^6.0.0",
37
+ "pixelmatch": "^7.2.0",
38
38
  "pngjs": "^7.0.0",
39
39
  "zod": "^4.4.3"
40
40
  },
@@ -47,7 +47,7 @@
47
47
  "@types/node": "^26.0.1",
48
48
  "@types/pngjs": "^6.0.5",
49
49
  "esbuild": "^0.28.1",
50
- "typescript": "^6.0.3"
50
+ "typescript": "^7.0.2"
51
51
  },
52
52
  "engines": {
53
53
  "node": ">=18.0.0"
@@ -6211,8 +6211,7 @@ function getChangedFieldEntries() {
6211
6211
 
6212
6212
  /**
6213
6213
  * Final payload value for a changed field. Most fields use their normalized diff
6214
- * value as-is; estimate_scale and score_threshold_for_review keep their existing
6215
- * save shaping.
6214
+ * value as-is; estimate_scale keeps its existing save shaping.
6216
6215
  * @param {string} fieldName
6217
6216
  * @param {*} changedValue - normalized value from the diff result
6218
6217
  * @param {string|null} estimateScaleValue - validated estimate-scale string
@@ -6222,9 +6221,6 @@ function getPayloadValueForField(fieldName, changedValue, estimateScaleValue = n
6222
6221
  if (fieldName === 'estimate_scale') {
6223
6222
  return estimateScaleValue;
6224
6223
  }
6225
- if (fieldName === 'score_threshold_for_review') {
6226
- return parseInt(changedValue, 10) || 85;
6227
- }
6228
6224
  if (fieldName === 'deep_research_stall_timeout_minutes') {
6229
6225
  return changedValue === '' ? null : parseInt(changedValue, 10);
6230
6226
  }
@@ -6306,25 +6302,11 @@ const EXPLANATION_TEXTS = {
6306
6302
  'pwa_overrides_directories': 'Specify the directories that contain your PWA Kit template overrides. Enter one directory path per line (e.g., `overrides`, `app/templates`, `custom-overrides`). These directories will be given special consideration when the AI generates or modifies code.',
6307
6303
  'frontend_correctness_standards': 'Describe how the AI should review frontend Javascript. Consider project standards, error handling, data validation, etc. Required.',
6308
6304
  'backend_correctness_standards': 'Describe how the AI should review backend code. Consider project standards, error handling, data validation, security, best practices',
6309
- 'reviewable_file_types': 'List the file suffixes (e.g., `js, isml, scss`) the AI should review. Required.',
6310
- 'include_path': 'Specify directory paths (one per line) to include in reviews. Prevents reviewing third-party code. Recommended.',
6311
- 'exclude_path': 'Specify directory paths (one per line) to exclude from reviews, even if they are within an included path. Recommended.',
6312
6305
  'template_correctness_standards': 'Define standards for reviewing template code (e.g., HTML, ISML), focusing on issues like accessibility. Recommended. If blank, template correctness is skipped.',
6313
6306
  'style_correctness_standards': 'Define standards for reviewing CSS/SASS/LESS code. Optional. If blank, style correctness for these files is skipped.',
6314
- 'frontend_styleguide': 'Describe style conventions for frontend Javascript. Optional. If blank, frontend style reviews are skipped.',
6315
- 'backend_styleguide': 'Describe style conventions for backend code (e.g., JSDoc, function size). Optional. If blank, backend style reviews are skipped.',
6316
- 'css_styleguide': 'Describe style conventions for CSS/SASS/LESS code. Optional. If blank, CSS style reviews are skipped.',
6317
- 'review_correctness': 'Enable/disable checking code for bugs and errors. Defaults to true. Optional.',
6318
- 'review_style': 'Enable/disable checking code for style guide adherence. Defaults to true. Optional.',
6319
6307
  'review_requirements': 'When enabled, the code reviewer checks whether the pull request actually covers the ticket\'s stated requirements (requires a ticket ID in the branch or commit). Best for initial commits. Defaults to enabled.',
6320
6308
  'check_critical_priorities': 'When enabled, the code reviewer additionally checks each pull request against the Critical Priorities you configure below. If this check is on but no priorities are configured, the check is skipped.',
6321
6309
  'critical_priorities': 'The critical priorities the code reviewer holds every pull request to (e.g., "Never log secrets or PII", "All DB writes must be repo-scoped"). Leave blank to skip the critical-priorities check even when it is enabled.',
6322
- 'review_architecture': 'Enable/disable checking for integration errors and correctness (if correctness review is off). Defaults to true. Optional.',
6323
- 'create_tests': 'Enable/disable AI generation of unit/integration tests for the PR. Defaults to false. Optional.',
6324
- 'create_documentation': 'Enable/disable AI generation of documentation for the PR. Defaults to false. Optional.',
6325
- 'documentation_standards': 'If `Create Documentation` is enabled, describe the desired documentation format and standards here. Optional.',
6326
- 'score_threshold_for_review': 'Set a score (0-100) below which the AI will perform a review. Files scoring at or above this threshold are considered passing. Defaults to 85. Optional.',
6327
- 'no_comment_on_passing_score': 'If enabled, the AI will not leave a comment on files with a passing score. If disabled, it leaves a positive comment (e.g., "Looks good!"). Defaults to true (no comment). Optional.',
6328
6310
  'schedule_interval': 'How frequently should the repository parsing job run? This is measured in days (e.g., every 1 day, 2 days, etc.).',
6329
6311
  'schedule_timezone': 'The timezone for scheduling repository parsing jobs. We will run this job daily at 1:00 AM according to the timezone you select.',
6330
6312
  'deep_research_enabled': 'Enable or disable AI deep research for this project. When enabled, the MCP tool can perform in-depth web research on technical topics using the default Deep Research provider. Defaults to enabled.',