@miraland-labs/conduit-bridge 0.16.133 → 0.16.137

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.
package/dist/config.js CHANGED
@@ -29,22 +29,6 @@ const runtimePath = join(directory, "runtime.json");
29
29
  const lockPath = join(directory, "config.lock");
30
30
  const pendingPath = join(directory, "pending-connect.json");
31
31
  const installationPath = join(directory, "installation.json");
32
- /**
33
- * Prefer runtime.json; when absent, adopt legacy activeAttempts/sessions still on config.json
34
- * so one Bridge upgrade does not strand in-flight claims.
35
- */
36
- export function adoptLegacyRuntimeClaims(input) {
37
- if (input.runtime) {
38
- return {
39
- activeAttempts: input.runtime.activeAttempts ?? {},
40
- sessions: input.runtime.sessions,
41
- };
42
- }
43
- return {
44
- activeAttempts: input.legacyAttempts,
45
- sessions: input.legacySessions,
46
- };
47
- }
48
32
  export async function loadConfig() {
49
33
  const config = await loadConfigIfPresent();
50
34
  if (!config)
@@ -71,13 +55,9 @@ export async function loadConfigIfPresent() {
71
55
  }
72
56
  }
73
57
  const runtime = await loadRuntime();
74
- const adopted = adoptLegacyRuntimeClaims({
75
- runtime,
76
- legacyAttempts: config.activeAttempts && typeof config.activeAttempts === "object" ? config.activeAttempts : {},
77
- legacySessions: config.sessions && typeof config.sessions === "object" ? config.sessions : undefined,
78
- });
79
- config.activeAttempts = adopted.activeAttempts;
80
- config.sessions = adopted.sessions;
58
+ // Claims live only in runtime.json; a fresh install has none.
59
+ config.activeAttempts = runtime?.activeAttempts ?? {};
60
+ config.sessions = runtime?.sessions;
81
61
  for (const active of Object.values(config.activeAttempts))
82
62
  active.phase ??= "agent_running";
83
63
  if (config.fuelSource !== "local" && config.fuelSource !== "conduit")
@@ -264,24 +244,10 @@ export async function saveConfig(config, options = {}) {
264
244
  diskPrefs = JSON.parse(await readFile(path, "utf8"));
265
245
  }
266
246
  catch { /* no prior config */ }
267
- // One-time migrate: if runtime.json is missing, preserve legacy activeAttempts from config.json.
268
- const existingRuntime = await loadRuntime();
269
- if (!existingRuntime && diskPrefs) {
270
- const legacyAttempts = diskPrefs.activeAttempts && typeof diskPrefs.activeAttempts === "object"
271
- ? diskPrefs.activeAttempts
272
- : {};
273
- const legacySessions = diskPrefs.sessions && typeof diskPrefs.sessions === "object"
274
- ? diskPrefs.sessions
275
- : undefined;
276
- if (Object.keys(legacyAttempts).length || legacySessions) {
277
- await writeJsonAtomic(runtimePath, { activeAttempts: legacyAttempts, sessions: legacySessions });
278
- }
279
- }
280
247
  const prefs = { ...snapshot, drivers: mergeCurrentDriverQuota(diskPrefs, snapshot.drivers) };
281
248
  delete prefs.activeAttempts;
282
249
  delete prefs.sessions;
283
- const prefsForDisk = { ...prefs, activeAttempts: {}, sessions: undefined };
284
- await writeJsonAtomic(path, prefsForDisk);
250
+ await writeJsonAtomic(path, prefs);
285
251
  if (!options.prefsOnly) {
286
252
  const disk = await loadRuntime();
287
253
  await writeJsonAtomic(runtimePath, mergeRuntimeState(disk, {
@@ -342,7 +308,7 @@ async function updateDriverLane(driverId, update) {
342
308
  if (!lane || typeof lane !== "object")
343
309
  return;
344
310
  update(lane);
345
- await writeJsonAtomic(path, { ...prefs, activeAttempts: {}, sessions: undefined });
311
+ await writeJsonAtomic(path, prefs);
346
312
  });
347
313
  };
348
314
  const next = saveChain.then(run, run);
package/dist/driver.js CHANGED
@@ -787,15 +787,6 @@ export function claudeCodeFuelEnv(fuelSource, model) {
787
787
  return undefined;
788
788
  return { ANTHROPIC_MODEL: model, ANTHROPIC_SMALL_FAST_MODEL: model, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
789
789
  }
790
- /** Map Conduit grants → Codex sandbox (privileged grants never widen the sandbox). */
791
- export function codexSandboxForGrants(grants) {
792
- if (grants.includes("repo_write") || grants.includes("branch_create") || grants.includes("pr_create") || grants.includes("test_run")) {
793
- return "workspace-write";
794
- }
795
- if (grants.includes("repo_read"))
796
- return "read-only";
797
- return null;
798
- }
799
790
  /**
800
791
  * Codex exec arguments. The sandbox is the enforcement boundary: network stays
801
792
  * off inside workspace-write unless the class/capabilities require it.
@@ -2075,12 +2066,6 @@ export function hasGrokLogin(env = process.env) {
2075
2066
  // `~/.grok` exists after install; auth.json is the `grok login` / cached_token file.
2076
2067
  return Boolean(home && existsSync(join(home, ".grok", "auth.json")));
2077
2068
  }
2078
- /** True when local fuel can use a host vendor login for at least one supported agent. */
2079
- export function hasLocalVendorLogin(env = process.env) {
2080
- return hasClaudeLogin(env) || hasOpenAiLogin(env) || hasCursorLogin(env)
2081
- || hasOpenCodeLogin(env) || hasKiroLogin(env) || hasAntigravityLogin(env)
2082
- || hasGrokLogin(env);
2083
- }
2084
2069
  /** Exported for tests — builds the stripped process env with optional Conduit fuel. */
2085
2070
  export function agentProcessEnv(fuel, fuelSource = "conduit", extraEnv) {
2086
2071
  return boundedEnvironment(fuel, fuelSource, extraEnv);
@@ -210,7 +210,7 @@ export async function captureVerificationFailure(input) {
210
210
  // shown to be the same bug.
211
211
  const text = boundedFailureDetail(command, result.stdout.trim() ? result.stdout.trim().split("\n") : [], result.stderr.trim() ? result.stderr.trim().split("\n") : [], result.code, DIAGNOSIS_DETAIL_MAX_CHARS);
212
212
  if (text.trim())
213
- return text;
213
+ return { command, detail: text };
214
214
  }
215
215
  // Every bounded command passed (or none could start): agent failed for another reason.
216
216
  return null;
@@ -456,6 +456,18 @@ export function detectGateRanNothing(command, stdout, stderr, code) {
456
456
  }
457
457
  return null;
458
458
  }
459
+ /**
460
+ * A declared gate that ran and failed. Carries the command as a field so the verification signal
461
+ * can name the gate without reading it back out of this error's text.
462
+ */
463
+ export class VerificationFailedError extends Error {
464
+ command;
465
+ constructor(command, detail) {
466
+ super(`Agent report: Verification failed (${command}): ${detail}`);
467
+ this.name = "VerificationFailedError";
468
+ this.command = command;
469
+ }
470
+ }
459
471
  /**
460
472
  * A declared gate that ran no tests, or that only printed its own usage text: a misconfigured gate,
461
473
  * not a failed one. Carries the typed fields `execution.ts` needs to emit the T1 signal — the
@@ -520,7 +532,7 @@ export async function ensureTestEvidence(input) {
520
532
  elided: detail.includes("omitted"),
521
533
  ends_with_exit: detail.trimEnd().endsWith(`exit ${result.code}`),
522
534
  }));
523
- throw new Error(`Agent report: Verification failed (${command}): ${detail}`);
535
+ throw new VerificationFailedError(command, detail);
524
536
  }
525
537
  // Empty exit-0 is not proof — a no-op recipe (`@true`) must not clear required_evidence: test.
526
538
  if (stdoutLines.length === 0 && stderrLines.length === 0) {
package/dist/execution.js CHANGED
@@ -26,7 +26,7 @@ import { ensureLandCommit } from "./ensure-land-commit.js";
26
26
  import { AGENT_NO_LAND_COMMIT_PREFIX, AgentNoLandCommitError, agentNoLandCommitMessage, requiresLandCommit } from "./land-contract.js";
27
27
  import { agentClaimsRepositoryWork, claimsVsGitMismatch, isLandTreeEmpty, reportClaimsRepositoryWork, } from "./land-git-reality.js";
28
28
  const execFileAsync = promisify(execFile);
29
- import { boundedTail, captureVerificationFailure, detectGateRanNothing, ensureTestEvidence, needsTestEvidence, runBoundedVerificationCommand, verificationEvidenceCommands, VerificationGateRanNothingError } from "./ensure-test-evidence.js";
29
+ import { boundedTail, captureVerificationFailure, detectGateRanNothing, ensureTestEvidence, needsTestEvidence, runBoundedVerificationCommand, verificationEvidenceCommands, VerificationFailedError, VerificationGateRanNothingError } from "./ensure-test-evidence.js";
30
30
  import { ensureChangeEvidence } from "./ensure-change-evidence.js";
31
31
  import { ensureDemonstrationEvidence } from "./ensure-demonstration-evidence.js";
32
32
  import { ensureNormativeEvidence } from "./ensure-normative-evidence.js";
@@ -472,8 +472,8 @@ export function parseDiagnosticRepairBrief(text) {
472
472
  return parsed.data;
473
473
  }
474
474
  /**
475
- * The gate command inside the witness line Bridge itself wrote. Mirrored as
476
- * VERIFICATION_GATE_PATTERN in src/conductor/repair.ts.
475
+ * The gate command inside the witness line, for the diagnosis prompt. Repair names the gate from
476
+ * the typed signal; this pattern stays until that prompt moves.
477
477
  */
478
478
  const VERIFICATION_GATE_PATTERN = /Verification failed \(([^)]+)\)/;
479
479
  /** Prompt for Option C: IDE-depth understanding in the retained tree, with write authority absent. */
@@ -1484,7 +1484,7 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
1484
1484
  // gate that moved instead of showing two hex strings.
1485
1485
  await client.updateAttempt(taskId, { executionFacts, verificationCommands });
1486
1486
  await client.attemptRequest(taskId, "progress", {
1487
- phase: diagnosis ? "inspecting" : "changing",
1487
+ phase: diagnosis ? "inspecting" : "agent_running",
1488
1488
  execution_facts: executionFacts,
1489
1489
  message: resuming
1490
1490
  ? `Resuming ${driver.name} after Bridge restart with proven session and worktree.`
@@ -1728,15 +1728,22 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
1728
1728
  // transient retry — the one verdict that would run the capped work again at the same price.
1729
1729
  const turnCapReached = result.signal?.kind === "turn_cap";
1730
1730
  const recoverVerification = grants.includes("test_run") && !subscriptionExhausted(agentMessage) && !turnCapReached;
1731
- if (recoverVerification)
1731
+ if (recoverVerification) {
1732
1732
  watchdog.phase("verification");
1733
- const verificationDetail = recoverVerification
1733
+ await client.attemptRequest(taskId, "progress", {
1734
+ phase: "verifying",
1735
+ message: "Bridge is running the project's verification commands to read the failure.",
1736
+ idempotency_key: `bridge:progress:${active.attemptId}:verify-failure`,
1737
+ });
1738
+ }
1739
+ const capturedVerification = recoverVerification
1734
1740
  ? await captureVerificationFailure({
1735
1741
  workspace: attemptWorkspace,
1736
1742
  verificationCommands: attemptBrief?.verification ?? [],
1737
1743
  acceptance: spec.acceptance ?? [],
1738
1744
  })
1739
1745
  : null;
1746
+ const verificationDetail = capturedVerification?.detail ?? null;
1740
1747
  // The vendor named the model as the cause, so the model is what the retry must change. The
1741
1748
  // model rides in the prefix: the control plane cannot read Bridge's selection any other way.
1742
1749
  const diffStat = verificationDetail && attemptWorkspace && startCommit
@@ -1771,8 +1778,8 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
1771
1778
  : subscriptionExhausted(agentMessage)
1772
1779
  ? { phase: "agent", witness: "vendor", kind: "quota", vendor_message: agentMessage.slice(0, 2_000) }
1773
1780
  : result.signal
1774
- ? verificationDetail
1775
- ? { ...result.signal, phase: "verification", witness: "bridge", stderr_tail: undefined, stdout_tail: undefined, ...(diffStat ? { diff_stat: diffStat } : {}) }
1781
+ ? capturedVerification
1782
+ ? { ...result.signal, phase: "verification", witness: "bridge", kind: "verification_failed", command: capturedVerification.command, stderr_tail: undefined, stdout_tail: undefined, ...(diffStat ? { diff_stat: diffStat } : {}) }
1776
1783
  : result.signal
1777
1784
  : undefined;
1778
1785
  const response = await queueTerminal(client, taskId, { action: "fail", body: {
@@ -2144,6 +2151,13 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
2144
2151
  ? await changedPathsSince(attemptWorkspace, deliveryBaseCommit)
2145
2152
  : null;
2146
2153
  // Mechanical test path: agent often forgets verbatim make test / npm test output.
2154
+ if (grants.includes("test_run")) {
2155
+ await client.attemptRequest(taskId, "progress", {
2156
+ phase: "verifying",
2157
+ message: "Bridge is running the project's verification commands.",
2158
+ idempotency_key: `bridge:progress:${active.attemptId}:verify`,
2159
+ });
2160
+ }
2147
2161
  report = await ensureTestEvidence({
2148
2162
  workspace: attemptWorkspace,
2149
2163
  report,
@@ -2221,7 +2235,8 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
2221
2235
  // the same verify-class pre-delivery failure as the driver-failed path above: preserve the
2222
2236
  // exact tree and send the witness marker that arms Conductor diagnosis. Treating it as a
2223
2237
  // delivery-envelope defect would stop before Invariant 23 ever ran.
2224
- const verificationFailed = /^Agent report: Verification failed \(/.test(message);
2238
+ // The command is the field on the throw, never a parse of this message.
2239
+ const verificationFailed = error instanceof VerificationFailedError;
2225
2240
  const diffStat = verificationFailed && attemptWorkspace
2226
2241
  ? await diffStatSince(attemptWorkspace, deliveryBaseCommit)
2227
2242
  .then((stat) => stat?.slice(0, 8_000) ?? null)
@@ -2244,6 +2259,7 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
2244
2259
  phase: "verification",
2245
2260
  witness: "bridge",
2246
2261
  kind: "verification_failed",
2262
+ command: error.command,
2247
2263
  ...(diffStat ? { diff_stat: diffStat } : {}),
2248
2264
  } } : {}),
2249
2265
  // A rework's own PR may already be open (ensureDeliveryPullRequest ran before this
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miraland-labs/conduit-bridge",
3
- "version": "0.16.133",
3
+ "version": "0.16.137",
4
4
  "description": "Conduit Bridge CLI \u2014 join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity / Grok Build agents for a Conduit organization",
5
5
  "type": "module",
6
6
  "bin": {