@bridge_gpt/mcp-server 0.2.65 → 0.2.67

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 (132) hide show
  1. package/README.md +269 -82
  2. package/build/agent-capabilities/probes.js +15 -5
  3. package/build/agent-launchers/claude-executor-adapter.js +55 -34
  4. package/build/agent-launchers/executor-adapter.js +34 -4
  5. package/build/agents.generated.js +1 -1
  6. package/build/bounded-wait.js +67 -10
  7. package/build/bridge-config.js +104 -0
  8. package/build/codex-bridge-profile.js +10 -1
  9. package/build/commands.generated.js +12 -12
  10. package/build/conductor/bridge-api-client.js +372 -10
  11. package/build/conductor/bring-up-facts.js +1 -1
  12. package/build/conductor/doctor.js +3 -3
  13. package/build/conductor/epic-child-continuation.js +61 -34
  14. package/build/conductor/epic-implementer-cli.js +34 -6
  15. package/build/conductor/errors.js +4 -3
  16. package/build/conductor/guided-repair.js +327 -0
  17. package/build/conductor/hook-runtime-qualification.js +1 -1
  18. package/build/conductor/merge-execution-contract.js +27 -8
  19. package/build/conductor/merge-execution-eligibility.js +3 -2
  20. package/build/conductor/pr-ci-producer.js +1 -1
  21. package/build/conductor/readiness-cli.js +46 -9
  22. package/build/conductor/readiness-sections.js +41 -6
  23. package/build/conductor/readiness.js +204 -30
  24. package/build/conductor/recovery-cli.js +703 -20
  25. package/build/conductor/recovery-operations.js +392 -3
  26. package/build/conductor/repair-contract.js +112 -3
  27. package/build/conductor/repository-workflow-remedy.js +125 -0
  28. package/build/conductor/tools.js +2 -2
  29. package/build/conductor-bundle-artifacts.js +8 -0
  30. package/build/credential-store.js +5 -4
  31. package/build/diagnostic-text.js +36 -0
  32. package/build/docs.generated.js +8 -3
  33. package/build/doctor.js +70 -10
  34. package/build/drive-epic-key.js +6 -5
  35. package/build/drive-epic.js +479 -131
  36. package/build/epic-coherence-input.js +2 -1
  37. package/build/epic-implementer-bin.js +30 -26
  38. package/build/epic-implementer-bundle-cli.js +2 -2
  39. package/build/epic-launch-diagnostic.js +31 -0
  40. package/build/epic-launch-profile.js +44 -14
  41. package/build/executor/cli.js +3 -2
  42. package/build/executor/completion-contract.js +48 -5
  43. package/build/executor/completion-supervision.js +9 -0
  44. package/build/executor/correction-dispatch.js +60 -0
  45. package/build/executor/env.js +25 -0
  46. package/build/executor/heartbeat.js +1 -1
  47. package/build/executor/http-client.js +35 -0
  48. package/build/executor/integration-job.js +6 -1
  49. package/build/executor/job-errors.js +38 -4
  50. package/build/executor/job-log-registry.js +340 -73
  51. package/build/executor/job-runner.js +485 -74
  52. package/build/executor/job-types.js +8 -13
  53. package/build/executor/observation.js +6 -1
  54. package/build/executor/preflight.js +136 -10
  55. package/build/executor/prompt-spec.js +113 -9
  56. package/build/executor/required-work-ledger.js +6 -1
  57. package/build/executor/resume-pre-spawn.js +2 -0
  58. package/build/executor/runner.js +31 -2
  59. package/build/executor/service-unit.js +7 -1
  60. package/build/executor/stale-artifacts.js +5 -4
  61. package/build/executor/types.js +1 -0
  62. package/build/executor/watch-cli.js +35 -8
  63. package/build/executor/worker-command.js +43 -43
  64. package/build/executor/worker-finalization.js +161 -43
  65. package/build/executor/worker-guard-hook.js +152 -12
  66. package/build/executor/worker-log.js +137 -70
  67. package/build/executor/worktree-gc.js +30 -6
  68. package/build/executor/worktree.js +82 -1
  69. package/build/index.js +482 -660
  70. package/build/init.js +107 -4
  71. package/build/install-bridge-conductor.js +8 -6
  72. package/build/install-bridge.js +287 -23
  73. package/build/install-doctor.js +172 -30
  74. package/build/install-reexec.js +69 -7
  75. package/build/macos-terminal-permission-guidance.js +16 -0
  76. package/build/model-thresholds-tool.js +185 -0
  77. package/build/phase-context-git-probe.js +68 -0
  78. package/build/phase-result-artifacts.js +173 -5
  79. package/build/pipelines.generated.js +19 -25
  80. package/build/plane/alembic-head.js +12 -3
  81. package/build/plane/base-worktree-guard.js +147 -0
  82. package/build/plane/cli.js +217 -17
  83. package/build/plane/code-snapshot.js +468 -0
  84. package/build/plane/defaults.js +48 -3
  85. package/build/plane/endpoint.js +41 -0
  86. package/build/plane/manifest.js +120 -1
  87. package/build/plane/member-roster.js +94 -19
  88. package/build/plane/preflight.js +30 -2
  89. package/build/plane/process-cwd.js +67 -0
  90. package/build/plane/restart.js +542 -0
  91. package/build/plane/shutdown.js +31 -0
  92. package/build/plane/status.js +145 -6
  93. package/build/plane/supervisor.js +588 -177
  94. package/build/plane/types.js +54 -2
  95. package/build/plane/worker-exit-code.js +2 -0
  96. package/build/printed-remedy-commands.js +37 -0
  97. package/build/project-root.js +99 -0
  98. package/build/readiness-check.js +1 -1
  99. package/build/readme.generated.js +1 -1
  100. package/build/review-generation.js +22 -0
  101. package/build/review-tickets.js +156 -13
  102. package/build/setup-epic.js +224 -127
  103. package/build/sfcc/config.js +9 -0
  104. package/build/sfcc/credentials.js +100 -38
  105. package/build/sfcc/hostname.js +67 -0
  106. package/build/sfcc/permissions.js +17 -13
  107. package/build/sfcc/register.js +1 -1
  108. package/build/sfcc/setup-status.js +2 -0
  109. package/build/sfcc/tool-wrapper.js +41 -43
  110. package/build/sfcc/tools-configured.js +96 -0
  111. package/build/sfcc/write-grants.js +17 -11
  112. package/build/sfcc/write-result.js +4 -3
  113. package/build/sfcc/writes-site-preference.js +20 -8
  114. package/build/start-tickets.js +332 -79
  115. package/build/tool-error-envelope.js +61 -0
  116. package/build/upgrade-cli.js +109 -24
  117. package/build/version.generated.js +3 -3
  118. package/build/visual-diff.js +22 -1
  119. package/build/windows-npm-entry.js +103 -0
  120. package/build/worker-guard-hook-bin.js +3 -5
  121. package/build/worker-guard-hook-main.js +47 -0
  122. package/build/worktree-core.js +136 -9
  123. package/docs/EPIC-IMPLEMENTER-INSTALL.md +849 -0
  124. package/docs/EPIC-IMPLEMENTER.md +662 -0
  125. package/docs/install/mcp-tool-integrations.md +25 -15
  126. package/docs/install/sfcc-integration.md +21 -8
  127. package/package.json +1 -1
  128. package/pipelines/implement-ticket.json +1 -1
  129. package/pipelines/plan-epic.json +0 -5
  130. package/pipelines/review-ticket.json +1 -1
  131. package/docs/CONDUCTOR.md +0 -605
  132. package/pipelines/idea-to-pr.json +0 -49
@@ -193,6 +193,28 @@ export class ReviewGenerationRegistry {
193
193
  return null;
194
194
  return { baseline: entry.baseline, expectedLegCount: entry.expectedLegCount };
195
195
  }
196
+ /**
197
+ * Consecutive all-`404` wait windows recorded for the live submission
198
+ * (BAPI-1382), or `0` when there is none or no live entry.
199
+ */
200
+ notFoundWindowCount(repoName, ticketNumber) {
201
+ this.evictExpired();
202
+ return this.entries.get(baselineKey(repoName, ticketNumber))?.notFoundWindows ?? 0;
203
+ }
204
+ /** Count one more all-`404` wait window for the live submission. No-op without one. */
205
+ noteNotFoundWindow(repoName, ticketNumber) {
206
+ this.evictExpired();
207
+ const entry = this.entries.get(baselineKey(repoName, ticketNumber));
208
+ if (!entry)
209
+ return;
210
+ entry.notFoundWindows = (entry.notFoundWindows ?? 0) + 1;
211
+ }
212
+ /** A non-`404` observation resets the count. No-op without a live entry. */
213
+ resetNotFoundWindows(repoName, ticketNumber) {
214
+ const entry = this.entries.get(baselineKey(repoName, ticketNumber));
215
+ if (entry)
216
+ entry.notFoundWindows = 0;
217
+ }
196
218
  /**
197
219
  * Consume a baseline. Clearing means "durable persistence has happened",
198
220
  * not "a terminal envelope was received" (BAPI-1129) — the anchor is
@@ -3,12 +3,22 @@
3
3
  *
4
4
  * Reuses the `start-tickets` terminal spawn layer directly, running reviews in
5
5
  * the current `deps.cwd`. Intentionally does NOT create Worktrunk worktrees,
6
- * provision MCP registrations, run venv hooks, or perform difficulty/model-tier
7
- * routing. One terminal tab per ticket is spawned with `/review-ticket <KEY>
8
- * [--auto] [--rounds=<1|2>]` as the agent prompt. `--rounds` is OMITTED by
9
- * default so the backend's difficulty-adaptive review policy chooses the shape;
10
- * it is only forwarded when the caller passes an explicit `--rounds`/`--review`
11
- * round override.
6
+ * provision MCP registrations, or run venv hooks. One terminal tab per ticket
7
+ * is spawned with `/review-ticket <KEY> [--auto] [--rounds=<1|2>]` as the agent
8
+ * prompt. `--rounds` is OMITTED by default so the backend's difficulty-adaptive
9
+ * review policy chooses the shape; it is only forwarded when the caller passes
10
+ * an explicit `--rounds`/`--review` round override.
11
+ *
12
+ * BAPI-1343 — review-threshold model routing for spawned tabs: an explicit
13
+ * `--model` always wins and no tier lookup is made. Otherwise each ticket's
14
+ * `/tickets/{n}/model-tier?consumer=review` is fetched (the shared bounded
15
+ * `start-tickets` fetcher, at most 3 at once), and a tab gets `--model` only
16
+ * when the server echoes `consumer: "review"` and reports a threshold-decided
17
+ * (`configured` or `anomaly`) tier, mapped to an alias exactly as
18
+ * `start-tickets` maps it. Legacy mode, a failed or timed-out lookup, or an
19
+ * older server that does not echo the consumer passes no `--model` (today's
20
+ * behavior); there is no premium fallback. `--dry-run` makes no lookup, and the
21
+ * epic-dispatch path below is not re-routed.
12
22
  *
13
23
  * Optionally supports a supervisor-driven epic-dispatch path: when an
14
24
  * `EpicDispatchIdentity` is threaded through `ReviewTicketsOptions.epic`, each
@@ -18,7 +28,7 @@
18
28
  * This path is worktree-free and base-branch-free; the human CLI path leaves
19
29
  * `epic` unset and retains byte-for-byte identical behavior.
20
30
  */
21
- import { createDefaultStartTicketsDeps, detectTerminal, runWithConcurrency, buildAgentInvocation, shSquoteInner, powershellSquote, isCommandOnPath, resolveFirstCommandOnPath, tmuxSessionNameForTicket, TICKET_KEY_PATTERN, DEFAULT_MAX_PARALLEL, validateBranchName, fetchAndResolveBaseSha, fetchStartTicketsConfigField, resolveStartTicketsBridgeApiAccess, buildCommandProvisioningDeps, buildMcpProvisioningDeps, } from "./start-tickets.js";
31
+ import { createDefaultStartTicketsDeps, detectTerminal, runWithConcurrency, buildAgentInvocation, shSquoteInner, powershellSquote, isCommandOnPath, resolveFirstCommandOnPath, tmuxSessionNameForTicket, TICKET_KEY_PATTERN, DEFAULT_MAX_PARALLEL, validateBranchName, fetchAndResolveBaseSha, fetchStartTicketsConfigField, resolveStartTicketsBridgeApiAccess, buildCommandProvisioningDeps, buildMcpProvisioningDeps, fetchTicketModelTiersForKeys, isThresholdDecidedTierResponse, normalizeDifficultyModelTierOverrides, fetchCodexModelCatalog, resolveRoutingAlias, validateResolvedModelAliasForAgent, } from "./start-tickets.js";
22
32
  import { provisionCommandsForWorktree } from "./command-provisioning.js";
23
33
  import { resolveCodexLaunchScope } from "./codex-launch-scope.js";
24
34
  import { WINDOWS_TERMINAL_COMMAND, WINDOWS_POWERSHELL_CANDIDATES, TMUX_COMMAND, isSupportedStartTicketsPlatform, unsupportedPlatformMessage, } from "./start-tickets-prereqs.js";
@@ -48,7 +58,7 @@ export function getReviewTicketsUsage() {
48
58
  " --rounds=1|2 Review depth: 1=single-pass, 2=full two-pass (default: adaptive — backend routes by difficulty)",
49
59
  " --review KEY=auto,rounds=1 Per-ticket review mode override (repeatable)",
50
60
  " --agent claude|cursor-agent|codex Agent command to launch in each tab (default: claude)",
51
- " --model VALUE Model alias to pass as --model to the agent (optional passthrough)",
61
+ " --model VALUE Model alias for every tab (always wins). Unset: each tab is routed by the review threshold; legacy mode or an older server passes no --model",
52
62
  " --max-parallel N Max tabs to spawn concurrently (default: 3)",
53
63
  " --dry-run Print intended commands; creates no tabs and opens no sessions",
54
64
  " --base-branch BRANCH Base branch to fetch+pin for grounding (default: config base_branch, else \"main\")",
@@ -527,12 +537,125 @@ export function buildReviewPlanRows(deps, options, agent, status, overrides = {}
527
537
  rounds: mode.rounds,
528
538
  agentName: options.agentName,
529
539
  modelAlias: options.modelAlias,
540
+ // BAPI-1343: plan rows (the dry-run preview) never perform a tier lookup;
541
+ // they show only an explicit --model, or the agent default.
542
+ ...(!options.epic && options.modelAlias ? { modelRoutingReason: "explicit" } : {}),
530
543
  status,
531
544
  command,
532
545
  runId,
533
546
  };
534
547
  });
535
548
  }
549
+ /**
550
+ * BAPI-1343 — resolve each spawned tab's model from the review threshold.
551
+ *
552
+ * One `/model-tier?consumer=review` request per key through the shared
553
+ * `start-tickets` fetcher (bounded at 3, never throws). A tab is routed only
554
+ * when the server echoes `consumer: "review"` AND reports a threshold-decided
555
+ * tier; the tier maps to an alias exactly as `start-tickets` maps it (registry
556
+ * plus `difficulty_model_tier_overrides`, read once per batch; the Codex
557
+ * catalog, fetched once per batch; `validateResolvedModelAliasForAgent`, run
558
+ * at most once per distinct alias per batch). Every other outcome returns
559
+ * `modelAlias: null` (no `--model`, today's behavior) — there is no premium
560
+ * fallback. Callers handle explicit `--model`, `--dry-run`, and epic dispatch
561
+ * before calling this, so it always performs lookups.
562
+ */
563
+ export async function resolveReviewModelRoutingForKeys(deps, keys, agent, maxParallel) {
564
+ const decisions = new Map();
565
+ const noModel = (reason) => ({ modelAlias: null, reason });
566
+ let accessResult;
567
+ try {
568
+ accessResult = await resolveStartTicketsBridgeApiAccess(deps);
569
+ }
570
+ catch {
571
+ for (const key of keys)
572
+ decisions.set(key, noModel("lookup-failed"));
573
+ return decisions;
574
+ }
575
+ if (!accessResult.ok) {
576
+ for (const key of keys)
577
+ decisions.set(key, noModel("lookup-failed"));
578
+ return decisions;
579
+ }
580
+ const access = accessResult.access;
581
+ const tierMap = await fetchTicketModelTiersForKeys(access, keys, maxParallel, { consumer: "review" });
582
+ // Batch-scoped alias inputs, loaded lazily so a batch with no
583
+ // threshold-decided ticket makes no config/catalog request.
584
+ let aliasInputs = null;
585
+ const loadAliasInputs = () => {
586
+ if (!aliasInputs) {
587
+ aliasInputs = (async () => {
588
+ try {
589
+ if (agent.name === "codex") {
590
+ const catalogResult = await fetchCodexModelCatalog(access);
591
+ return catalogResult.ok ? { overrides: null, catalog: catalogResult.catalog } : null;
592
+ }
593
+ const raw = await fetchStartTicketsConfigField(access, "difficulty_model_tier_overrides");
594
+ return { overrides: normalizeDifficultyModelTierOverrides(raw), catalog: null };
595
+ }
596
+ catch {
597
+ return null;
598
+ }
599
+ })();
600
+ }
601
+ return aliasInputs;
602
+ };
603
+ const validations = new Map();
604
+ const validateOnce = (alias) => {
605
+ let pending = validations.get(alias);
606
+ if (!pending) {
607
+ pending = validateResolvedModelAliasForAgent(deps, agent, alias).catch(() => ({ ok: false, warning: "model validation failed" }));
608
+ validations.set(alias, pending);
609
+ }
610
+ return pending;
611
+ };
612
+ for (const key of keys) {
613
+ const result = tierMap.get(key);
614
+ if (!result || !result.ok) {
615
+ decisions.set(key, noModel("lookup-failed"));
616
+ continue;
617
+ }
618
+ const value = result.value;
619
+ if (value.consumer !== "review") {
620
+ decisions.set(key, noModel("consumer-unrecognized"));
621
+ continue;
622
+ }
623
+ if (value.source === "fallback") {
624
+ // The server could not obtain a difficulty; its premium tier is a guess,
625
+ // and review routing has no premium fallback.
626
+ decisions.set(key, noModel("lookup-failed"));
627
+ continue;
628
+ }
629
+ if (!isThresholdDecidedTierResponse(value)) {
630
+ decisions.set(key, noModel("legacy"));
631
+ continue;
632
+ }
633
+ if (!value.tier) {
634
+ decisions.set(key, noModel("lookup-failed"));
635
+ continue;
636
+ }
637
+ if (!agent.supportsModelOverride) {
638
+ decisions.set(key, noModel("alias-unresolved"));
639
+ continue;
640
+ }
641
+ const inputs = await loadAliasInputs();
642
+ const candidate = inputs ? resolveRoutingAlias(agent, value.tier, inputs.overrides, inputs.catalog) : null;
643
+ if (!candidate) {
644
+ decisions.set(key, noModel("alias-unresolved"));
645
+ continue;
646
+ }
647
+ const validation = await validateOnce(candidate);
648
+ if (!validation.ok) {
649
+ decisions.set(key, noModel("alias-unresolved"));
650
+ continue;
651
+ }
652
+ decisions.set(key, {
653
+ modelAlias: validation.alias,
654
+ reason: value.thresholdMode === "anomaly" ? "review-threshold:anomaly" : "review-threshold:configured",
655
+ });
656
+ }
657
+ return decisions;
658
+ }
536
659
  /** Build a dry-run preview base context from parsed CLI options only — never fetches. */
537
660
  function previewBaseContextFromOptions(options) {
538
661
  if (options.noRefreshBase)
@@ -590,10 +713,25 @@ export async function orchestrateReviewTickets(deps, options, overrides = {}) {
590
713
  return { ok: false, error: baseContextResult.error };
591
714
  }
592
715
  const baseContext = baseContextResult.context;
716
+ // BAPI-1343: per-tab review-threshold routing for the human CLI path only.
717
+ // An explicit --model wins with no lookup; the epic-dispatch path keeps its
718
+ // pre-feature shape (Epic workers take their tier from the stamped payload).
719
+ let routing = null;
720
+ if (!options.epic) {
721
+ if (options.modelAlias) {
722
+ routing = new Map(options.keys.map((key) => [key, { modelAlias: options.modelAlias ?? null, reason: "explicit" }]));
723
+ }
724
+ else {
725
+ routing = await resolveReviewModelRoutingForKeys(deps, options.keys, agent, options.maxParallel);
726
+ }
727
+ }
593
728
  const terminal = detectTerminal(undefined, deps.env);
594
729
  const rows = await runWithConcurrency(options.keys, options.maxParallel, async (key) => {
595
730
  const mode = resolveEffectiveReviewMode(key, options);
596
- const baseShellCommand = buildReviewAgentShellCommand(agent, key, mode, deps.platform, deps.cwd, options.modelAlias, baseContext, codexLaunchArgs);
731
+ const decision = routing?.get(key);
732
+ const modelAlias = decision ? decision.modelAlias : options.modelAlias;
733
+ const reasonField = decision ? { modelRoutingReason: decision.reason } : {};
734
+ const baseShellCommand = buildReviewAgentShellCommand(agent, key, mode, deps.platform, deps.cwd, modelAlias, baseContext, codexLaunchArgs);
597
735
  let shellCommand = baseShellCommand;
598
736
  let runId;
599
737
  if (options.epic) {
@@ -608,7 +746,8 @@ export async function orchestrateReviewTickets(deps, options, overrides = {}) {
608
746
  auto: mode.auto,
609
747
  rounds: mode.rounds,
610
748
  agentName: options.agentName,
611
- modelAlias: options.modelAlias,
749
+ modelAlias,
750
+ ...reasonField,
612
751
  status: "spawn-failed",
613
752
  command: shellCommand,
614
753
  runId,
@@ -625,7 +764,8 @@ export async function orchestrateReviewTickets(deps, options, overrides = {}) {
625
764
  auto: mode.auto,
626
765
  rounds: mode.rounds,
627
766
  agentName: options.agentName,
628
- modelAlias: options.modelAlias,
767
+ modelAlias,
768
+ ...reasonField,
629
769
  status: "already-dispatched",
630
770
  command: shellCommand,
631
771
  runId,
@@ -645,7 +785,8 @@ export async function orchestrateReviewTickets(deps, options, overrides = {}) {
645
785
  auto: mode.auto,
646
786
  rounds: mode.rounds,
647
787
  agentName: options.agentName,
648
- modelAlias: options.modelAlias,
788
+ modelAlias,
789
+ ...reasonField,
649
790
  status: "spawned",
650
791
  command: shellCommand,
651
792
  runId,
@@ -687,9 +828,11 @@ export function formatReviewTicketsSummaryReport(rows) {
687
828
  const lines = ["Summary:"];
688
829
  for (const row of rows) {
689
830
  const modelPart = row.modelAlias ? row.modelAlias : "default";
831
+ // BAPI-1343: the closed, secret-free routing reason, when one was recorded.
832
+ const modelReasonPart = row.modelRoutingReason ? ` model_reason=${row.modelRoutingReason}` : "";
690
833
  const roundsPart = row.rounds !== undefined ? String(row.rounds) : "adaptive";
691
834
  const runIdPart = row.runId ? ` run_id=${row.runId}` : "";
692
- lines.push(`${row.key} auto=${row.auto} rounds=${roundsPart} agent=${row.agentName} model=${modelPart} status=${row.status}${runIdPart}`);
835
+ lines.push(`${row.key} auto=${row.auto} rounds=${roundsPart} agent=${row.agentName} model=${modelPart}${modelReasonPart} status=${row.status}${runIdPart}`);
693
836
  }
694
837
  const failedRows = rows.filter((r) => r.status === "spawn-failed" || r.status === "already-dispatched");
695
838
  if (failedRows.length > 0) {