@bridge_gpt/mcp-server 0.2.37 → 0.2.39

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 (91) hide show
  1. package/README.md +193 -16
  2. package/build/agent-capabilities/probe-context.js +2 -1
  3. package/build/agent-launchers/claude-executor-adapter.js +392 -0
  4. package/build/agent-launchers/executor-adapter-inspection.js +163 -0
  5. package/build/agent-launchers/executor-adapter-registry.js +90 -0
  6. package/build/agent-launchers/executor-adapter.js +136 -0
  7. package/build/agent-registry.js +28 -0
  8. package/build/agents.generated.js +1 -1
  9. package/build/claude-login.js +85 -0
  10. package/build/claude-user-config-doctor.js +59 -33
  11. package/build/commands.generated.js +12 -11
  12. package/build/conduct-epic/bridge-client.js +345 -0
  13. package/build/conduct-epic/checkpoint-store.js +423 -0
  14. package/build/conduct-epic/cli.js +1732 -0
  15. package/build/conduct-epic/lock.js +302 -0
  16. package/build/conduct-epic/pr-state.js +197 -0
  17. package/build/conduct-epic/spawn.js +101 -0
  18. package/build/conductor/bridge-api-client.js +37 -2
  19. package/build/conductor/doctor.js +11 -1
  20. package/build/conductor/install-doctor.js +184 -10
  21. package/build/conductor-bin.js +7 -7
  22. package/build/credential-store.js +10 -4
  23. package/build/credentials-cli.js +34 -19
  24. package/build/docs.generated.js +1 -1
  25. package/build/doctor.js +579 -88
  26. package/build/executor/agent-identity.js +32 -0
  27. package/build/executor/cli.js +50 -39
  28. package/build/executor/deps.js +15 -1
  29. package/build/executor/env.js +56 -45
  30. package/build/executor/index.js +9 -1
  31. package/build/executor/install-preflight.js +138 -0
  32. package/build/executor/job-errors.js +200 -0
  33. package/build/executor/job-runner.js +619 -268
  34. package/build/executor/observation.js +165 -0
  35. package/build/executor/permissions.js +163 -36
  36. package/build/executor/platform.js +54 -0
  37. package/build/executor/preflight.js +175 -67
  38. package/build/executor/process.js +39 -7
  39. package/build/executor/runner.js +19 -0
  40. package/build/executor/service-lifecycle.js +269 -0
  41. package/build/executor/service-unit.js +121 -12
  42. package/build/executor/stale-artifacts.js +70 -0
  43. package/build/executor/test-clock.js +188 -24
  44. package/build/executor/worker-command.js +22 -58
  45. package/build/executor/worker-log.js +82 -0
  46. package/build/executor/worktree-lock.js +264 -0
  47. package/build/index.js +527 -357
  48. package/build/install-bridge-conductor.js +376 -38
  49. package/build/install-bridge.js +414 -114
  50. package/build/install-doctor.js +13 -0
  51. package/build/install-reexec.js +5 -3
  52. package/build/mcp-install-state.js +130 -0
  53. package/build/mcp-profile.js +11 -2
  54. package/build/mcp-provisioning.js +15 -0
  55. package/build/merge-pull-request.js +562 -0
  56. package/build/phase-result-artifacts.js +450 -0
  57. package/build/pipeline-orchestrator.js +4 -0
  58. package/build/pipeline-utils.js +16 -0
  59. package/build/pipelines.generated.js +7 -7
  60. package/build/plane/preflight.js +18 -14
  61. package/build/plane/supervisor.js +8 -1
  62. package/build/project-root.js +34 -0
  63. package/build/readme.generated.js +1 -1
  64. package/build/run-unit-tests-launcher.js +36 -9
  65. package/build/setup-epic.js +57 -4
  66. package/build/sfcc/ocapi-shape.js +23 -4
  67. package/build/sfcc/permissions.js +25 -6
  68. package/build/sfcc/read-body.js +92 -0
  69. package/build/sfcc/read-projection.js +6 -2
  70. package/build/sfcc/reads-custom-object-def.js +33 -21
  71. package/build/sfcc/reads-site-preference.js +20 -7
  72. package/build/sfcc/reads-system-object.js +11 -5
  73. package/build/sfcc/register.js +61 -23
  74. package/build/sfcc/registration-inventory.js +89 -0
  75. package/build/sfcc/setup-status.js +18 -34
  76. package/build/sfcc/tool-wrapper.js +294 -17
  77. package/build/sfcc/write-grants.js +33 -1
  78. package/build/sfcc/write-guard.js +41 -12
  79. package/build/sfcc/write-result.js +16 -7
  80. package/build/sfcc/writes-custom-object-def.js +12 -4
  81. package/build/sfcc/writes-site-preference.js +6 -1
  82. package/build/sfcc/writes-system-object.js +11 -2
  83. package/build/sfcc/writes.js +13 -8
  84. package/build/start-tickets-prereqs.js +25 -15
  85. package/build/start-tickets.js +123 -21
  86. package/build/version.generated.js +1 -1
  87. package/build/worktree-core.js +9 -3
  88. package/docs/install/mcp-tool-integrations.md +54 -9
  89. package/docs/install/sfcc-integration.md +71 -24
  90. package/package.json +3 -3
  91. package/build/executor/worker-config-isolation.js +0 -287
@@ -9,7 +9,10 @@
9
9
  */
10
10
  import { spawnSync } from "node:child_process";
11
11
  import { existsSync, readFileSync } from "node:fs";
12
+ import { readFile as readFileAsync } from "node:fs/promises";
13
+ import os from "node:os";
12
14
  import { doctorConductorLedger } from "./store.js";
15
+ import { detectClaudeLogin, formatClaudeLoginAdvisory } from "../claude-login.js";
13
16
  import { inspectConductorGitHooks } from "./git-hooks.js";
14
17
  import { resolveMcpShimInvocationForRuntime, resolvePackageRootFromModuleUrl, } from "../mcp-server-invocation.js";
15
18
  /**
@@ -284,6 +287,10 @@ export async function buildConductorDoctorReport(deps = {}) {
284
287
  const deny_enforcement = deps.skipDenyProbe
285
288
  ? skippedDenyEnforcementResult()
286
289
  : await inspectDenyEnforcementSafe(deps.inspectDenyEnforcement);
290
+ const claude_login = await detectClaudeLogin({
291
+ homedir: deps.claudeLoginDeps?.homedir ?? os.homedir,
292
+ readFile: deps.claudeLoginDeps?.readFile ?? ((p) => readFileAsync(p, "utf-8")),
293
+ });
287
294
  return {
288
295
  ledger,
289
296
  git_hooks: inspectHooks(deps.hooksDeps),
@@ -292,6 +299,7 @@ export async function buildConductorDoctorReport(deps = {}) {
292
299
  local_merge: inspectLocalMerge(deps.runCommand),
293
300
  native_ledger,
294
301
  deny_enforcement,
302
+ claude_login,
295
303
  };
296
304
  }
297
305
  /**
@@ -329,7 +337,7 @@ async function inspectDenyEnforcementSafe(inspect) {
329
337
  * status tags consistent with the git hooks section's visual hierarchy.
330
338
  */
331
339
  export function formatConductorDoctorReport(report) {
332
- const { ledger, git_hooks, epic_tick, mcp_profile, local_merge, native_ledger, deny_enforcement } = report;
340
+ const { ledger, git_hooks, epic_tick, mcp_profile, local_merge, native_ledger, deny_enforcement, claude_login } = report;
333
341
  const lines = [
334
342
  "Conductor ledger doctor",
335
343
  "───────────────────────",
@@ -462,5 +470,7 @@ export function formatConductorDoctorReport(report) {
462
470
  for (const w of deny_enforcement.warnings)
463
471
  lines.push(` - ${w}`);
464
472
  }
473
+ lines.push("");
474
+ lines.push(formatClaudeLoginAdvisory(claude_login));
465
475
  return lines.join("\n");
466
476
  }
@@ -30,15 +30,22 @@ import { ConductorBridgeApiError, fetchConductorReadiness, } from "./bridge-api-
30
30
  /** Operator runbook pointer surfaced beside the executor gap. */
31
31
  export const CONDUCTOR_OPERATOR_RUNBOOK_POINTER = "docs/claude/epic-conductor-v2-operator-runbook.md";
32
32
  /**
33
- * Capability-matrix guidance for the executor leg (BAPI-688 shipped in the same
34
- * build as BAPI-679): a persistent unit is *generated* by `executor
35
- * install-service`, but starting/stopping it stays operator-managed, so the
36
- * executor is never claimed as provisioned by a bootstrap alone.
33
+ * Capability-matrix guidance for the executor leg.
34
+ *
35
+ * Amended by BAPI-779 (R-1): `install conductor` can now write the unit AND
36
+ * start it, each behind its own consent. What did NOT change is the readiness
37
+ * rule — `executor_ready` still comes only from a live server observation, so
38
+ * neither a written unit nor a successful local start may produce that claim.
37
39
  */
38
- export const EXECUTOR_PROVISIONING_GUIDANCE = "generate a unit with `executor install-service`; start/stop stays operator-managed";
40
+ export const EXECUTOR_PROVISIONING_GUIDANCE = "`install conductor` writes the unit and starts it, each behind its own consent; " +
41
+ "readiness still comes only from a live server observation";
39
42
  /** Remediation surfaced whenever no healthy generated service unit is detected. */
40
- export const EXECUTOR_INSTALL_SERVICE_REMEDIATION = "generate a persistent unit with `executor install-service` (or run the executor by hand) " +
43
+ export const EXECUTOR_INSTALL_SERVICE_REMEDIATION = "generate a persistent unit with `install conductor --executor-id <id>` or " +
44
+ "`executor install-service` (or run the executor by hand) — " +
41
45
  `see ${CONDUCTOR_OPERATOR_RUNBOOK_POINTER}.`;
46
+ /** Remediation for a unit that exists but whose service is not running. */
47
+ export const EXECUTOR_SERVICE_START_REMEDIATION = "start the service (re-run `install conductor` and accept the start consent, or run the " +
48
+ `printed launchctl/systemctl command by hand) — see ${CONDUCTOR_OPERATOR_RUNBOOK_POINTER}.`;
42
49
  // ---------------------------------------------------------------------------
43
50
  // Section builders
44
51
  // ---------------------------------------------------------------------------
@@ -83,6 +90,45 @@ function supervisorSection(readiness) {
83
90
  status: "ok",
84
91
  detail: `${s.required_checks_count} required check(s) configured`,
85
92
  });
93
+ // BAPI-694 (R5) — its OWN section, and WARN-ONLY.
94
+ //
95
+ // `degraded`, never `fatal`: a divergence between the code_review gate's
96
+ // signal and the merge path's may be exactly what the operator intended, and
97
+ // blocking an install over a legitimate posture would be worse than the
98
+ // silence this replaces. It also changes no gate: both settings are reported,
99
+ // neither is coerced into the other's vocabulary.
100
+ //
101
+ // Absent (`null`) means the server predates BAPI-694 and cannot report
102
+ // alignment, so no section is emitted at all — an advisory addition must not
103
+ // manufacture a finding out of a server that never answered.
104
+ const alignment = s.review_policy_alignment;
105
+ if (alignment !== null) {
106
+ if (alignment.status === "divergent" || alignment.status === "invalid") {
107
+ sections.push({
108
+ id: "supervisor-review-alignment",
109
+ label: "Supervisor review-policy alignment",
110
+ status: "degraded",
111
+ detail: alignment.explanation,
112
+ remediation: alignment.status === "invalid"
113
+ ? "fix the done gate's review_state condition — review configuration " +
114
+ "exists but cannot be read, so the merge path refuses rather than merges."
115
+ : "set the repository review-policy default and the done gate's " +
116
+ "review_state condition to the same signal, or confirm the difference " +
117
+ `is intended — see ${CONDUCTOR_OPERATOR_RUNBOOK_POINTER}.`,
118
+ });
119
+ }
120
+ else {
121
+ // `aligned` and `not_configured` are both non-warning states, and are
122
+ // reported separately: "they agree" and "neither is set" are different
123
+ // facts, and collapsing them would imply a stored default that is absent.
124
+ sections.push({
125
+ id: "supervisor-review-alignment",
126
+ label: "Supervisor review-policy alignment",
127
+ status: "ok",
128
+ detail: alignment.explanation,
129
+ });
130
+ }
131
+ }
86
132
  return sections;
87
133
  }
88
134
  function githubCredentialsSection(readiness) {
@@ -202,9 +248,13 @@ function workflowSection(presence, reviewPolicySource) {
202
248
  * Real detection of BAPI-688 generated service units, composed from
203
249
  * `collectExecutorServiceDiagnostics`. Never `fatal` (a missing unit is a
204
250
  * repairable gap, and blocking every install on it would be wrong) and never
205
- * omitted (the flow must not read as fully provisioned by silence). A healthy
206
- * unit reports `ok` for *provisioning* only — whether the service is running is
207
- * the capability matrix's `executor_ready`, observed server-side.
251
+ * omitted (the flow must not read as fully provisioned by silence).
252
+ *
253
+ * This section reports UNIT PRESENCE and nothing else (BAPI-779). Whether the
254
+ * service manager is actually running the unit is
255
+ * {@link executorServiceStateSection}, and whether the SERVER has observed an
256
+ * executor is {@link executorObservationSection}. Three separate facts, three
257
+ * separate sections — a healthy unit here proves only that a file exists.
208
258
  */
209
259
  function executorProvisioningSection(diagnostics) {
210
260
  const base = { id: "executor-provisioning", label: "Executor provisioning" };
@@ -234,7 +284,7 @@ function executorProvisioningSection(diagnostics) {
234
284
  ...base,
235
285
  status: "ok",
236
286
  detail: `${diagnostics.units.length} generated service unit(s) found (${ids}); ` +
237
- "lifecycle stays operator-managed",
287
+ "unit presence only — see the service-state and observation sections",
238
288
  };
239
289
  }
240
290
  return {
@@ -246,6 +296,91 @@ function executorProvisioningSection(diagnostics) {
246
296
  remediation: EXECUTOR_INSTALL_SERVICE_REMEDIATION,
247
297
  };
248
298
  }
299
+ /**
300
+ * Local service-manager state for the units that were discovered (BAPI-779).
301
+ *
302
+ * Never `fatal`: this is a diagnostic, and an unreadable service manager is a
303
+ * gap in the *report*, not a broken install. `unknown` is deliberately not
304
+ * collapsed into `not-found` — "the probe could not answer" and "the service
305
+ * manager has no such unit" lead to different operator actions.
306
+ */
307
+ function executorServiceStateSection(observations, unitsDiscovered, inspectorAvailable) {
308
+ const base = { id: "executor-service-state", label: "Executor service state" };
309
+ if (!unitsDiscovered) {
310
+ return {
311
+ ...base,
312
+ status: "degraded",
313
+ detail: "no generated unit to inspect, so no service state was collected",
314
+ remediation: EXECUTOR_INSTALL_SERVICE_REMEDIATION,
315
+ };
316
+ }
317
+ if (!inspectorAvailable || observations.length === 0) {
318
+ return {
319
+ ...base,
320
+ status: "degraded",
321
+ detail: "local service state could not be collected on this host",
322
+ remediation: EXECUTOR_SERVICE_START_REMEDIATION,
323
+ };
324
+ }
325
+ // Deterministic ordering by executor id so two runs of the same host render
326
+ // identically regardless of enumeration order.
327
+ const rendered = [...observations]
328
+ .sort((a, b) => (a.executorId < b.executorId ? -1 : a.executorId > b.executorId ? 1 : 0))
329
+ .map((o) => `${o.executorId}=${o.state}`)
330
+ .join(", ");
331
+ const allActive = observations.every((o) => o.state === "active");
332
+ return allActive
333
+ ? { ...base, status: "ok", detail: `service state: ${rendered}` }
334
+ : {
335
+ ...base,
336
+ status: "degraded",
337
+ detail: `service state: ${rendered}`,
338
+ remediation: EXECUTOR_SERVICE_START_REMEDIATION,
339
+ };
340
+ }
341
+ /**
342
+ * The AUTHORITATIVE executor fact: what the server has observed (BAPI-779).
343
+ *
344
+ * This is the only section permitted to say `executor_ready`, and only for
345
+ * `readiness.executor.ready === true`. The three not-ready shapes are kept
346
+ * distinct because they mean different things: `null` is "never observed" (the
347
+ * executor has not claimed yet), `false` is "observed but stale" (it claimed
348
+ * once and went quiet), and an unreadable readiness payload is "unknown" (the
349
+ * doctor cannot say). Collapsing them would let a stale executor read like a
350
+ * fresh install, or vice versa.
351
+ */
352
+ function executorObservationSection(readiness) {
353
+ const base = { id: "executor-observation", label: "Executor server observation" };
354
+ if (readiness === null || !readiness.executor.liveness_readable) {
355
+ return {
356
+ ...base,
357
+ status: "degraded",
358
+ detail: "server executor observation is unknown (readiness could not be read)",
359
+ remediation: "retry once the Bridge API is reachable; no readiness claim is made from local state.",
360
+ };
361
+ }
362
+ const e = readiness.executor;
363
+ const seen = e.last_seen_at === null
364
+ ? "never seen"
365
+ : `last seen ${e.last_seen_at} (${e.last_seen_age_seconds}s ago)`;
366
+ if (e.ready === true) {
367
+ return { ...base, status: "ok", detail: `executor_ready — ${seen}` };
368
+ }
369
+ if (e.ready === false) {
370
+ return {
371
+ ...base,
372
+ status: "degraded",
373
+ detail: `observed but stale/not ready — ${seen}`,
374
+ remediation: EXECUTOR_SERVICE_START_REMEDIATION,
375
+ };
376
+ }
377
+ return {
378
+ ...base,
379
+ status: "degraded",
380
+ detail: `never observed by the server — ${seen}`,
381
+ remediation: EXECUTOR_SERVICE_START_REMEDIATION,
382
+ };
383
+ }
249
384
  // ---------------------------------------------------------------------------
250
385
  // Runner
251
386
  // ---------------------------------------------------------------------------
@@ -380,11 +515,36 @@ export async function runConductorInstallDoctor(deps) {
380
515
  // section reports the collection gap rather than losing the section.
381
516
  }
382
517
  sections.push(executorProvisioningSection(executorService));
518
+ // --- Local service state (BAPI-779): read-only, and ONLY for units that were
519
+ // actually discovered above. Probing a service manager for a unit this host
520
+ // never generated would be a speculative query with nothing to report.
521
+ const discoveredUnits = executorService !== null && executorService.status !== "skipped"
522
+ ? executorService.units
523
+ : [];
524
+ const executorServiceStates = [];
525
+ if (deps.inspectExecutorServiceState && discoveredUnits.length > 0) {
526
+ for (const unit of discoveredUnits) {
527
+ try {
528
+ const state = await deps.inspectExecutorServiceState({ executorId: unit.executorId });
529
+ executorServiceStates.push({ executorId: unit.executorId, state });
530
+ }
531
+ catch {
532
+ // An inspector that throws yields `unknown` for that unit rather than
533
+ // dropping it — a silently missing unit reads as "nothing to see".
534
+ executorServiceStates.push({ executorId: unit.executorId, state: "unknown" });
535
+ }
536
+ }
537
+ }
538
+ sections.push(executorServiceStateSection(executorServiceStates, discoveredUnits.length > 0, Boolean(deps.inspectExecutorServiceState)));
539
+ // --- Server observation: the authoritative executor fact, last so it reads
540
+ // as the conclusion of the three-part executor story.
541
+ sections.push(executorObservationSection(readiness));
383
542
  return {
384
543
  legacyConductor,
385
544
  legacyExecutorPreflight,
386
545
  legacyInstallChecks,
387
546
  executorService,
547
+ executorServiceStates,
388
548
  readiness,
389
549
  sections,
390
550
  };
@@ -450,6 +610,20 @@ export function formatConductorInstallDoctorReport(report) {
450
610
  if (report.executorService) {
451
611
  embed("Executor provisioning", () => formatExecutorServiceDiagnosticsReport(report.executorService));
452
612
  }
613
+ // Per-unit local service state (BAPI-779), rendered as its OWN block rather
614
+ // than folded into the provisioning report above — the whole point of the
615
+ // three-dimension split is that a reader can see "unit written" and "service
616
+ // not running" as two separate lines.
617
+ const states = report.executorServiceStates ?? [];
618
+ if (states.length > 0) {
619
+ lines.push("");
620
+ lines.push("executor service state (local)");
621
+ lines.push("──────────────────────────────");
622
+ for (const observation of [...states].sort((a, b) => a.executorId < b.executorId ? -1 : a.executorId > b.executorId ? 1 : 0)) {
623
+ lines.push(` ${observation.executorId}: ${observation.state}`);
624
+ }
625
+ lines.push(" local state is NOT readiness — only a live server observation produces executor_ready.");
626
+ }
453
627
  return lines.join("\n");
454
628
  }
455
629
  /** `1` when any section is fatal; `0` for ok/degraded-only reports. */