@miraland-labs/conduit-bridge 0.16.171 → 0.16.173

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.
@@ -12,6 +12,20 @@ const execFileAsync = promisify(execFile);
12
12
  export function attemptWorktreePath(sourceWorkspace, attemptId) {
13
13
  return join(sourceWorkspace, ".conduit", "attempts", attemptId);
14
14
  }
15
+ export function rehearsalWorktreePath(sourceWorkspace) {
16
+ return join(sourceWorkspace, ".conduit", "rehearsal");
17
+ }
18
+ export async function createRehearsalWorktree(sourceWorkspace, commit) {
19
+ await ensureConduitExclude(sourceWorkspace);
20
+ const path = rehearsalWorktreePath(sourceWorkspace);
21
+ await removeAttemptWorktree(sourceWorkspace, path);
22
+ await mkdir(join(sourceWorkspace, ".conduit"), { recursive: true });
23
+ await execFileAsync("git", ["-C", sourceWorkspace, "worktree", "add", "--detach", path, commit], {
24
+ timeout: 60_000,
25
+ maxBuffer: 2_000_000,
26
+ });
27
+ return path;
28
+ }
15
29
  /**
16
30
  * The branch each attempt works on. Named by attempt id, which is globally unique, so concurrent slots
17
31
  * (F-07 multi-slot) never collide in the shared ref namespace and a retry never clashes with a prior
package/dist/cli.js CHANGED
@@ -20,7 +20,8 @@ import { BOOTSTRAP_RESULTS, buildSovereignExecutionFacts, workspaceIsClean } fro
20
20
  import { ensureCheckout } from "./checkout.js";
21
21
  import { buildOnShiftIntentProof, managedWorkspacePath, maybeApplyOnShiftIntent } from "./on-shift-apply.js";
22
22
  import { expandOpsValue, loadOpsEnv, OPS_VERBS, runOps } from "./ops.js";
23
- import { discardWorktreeWrites, pumpExecutionSlots, renewLeases } from "./execution.js";
23
+ import { pumpExecutionSlots, renewLeases } from "./execution.js";
24
+ import { createRehearsalWorktree, rehearsalWorktreePath, removeAttemptWorktree } from "./attempt-worktree.js";
24
25
  import { applyRunnerToolPath, installRunnerService, uninstallRunnerService } from "./service.js";
25
26
  import { BRIDGE_PROTOCOL_VERSION, cachedBridgePreflight, gateRehearsalLockHeld, planGateRehearsals, PREFLIGHT_CACHE_TTL_MS, runGateRehearsal, unavailableWorkspacePreflight } from "./preflight.js";
26
27
  import { refreshSourceWorkspace } from "./workspace-refresh.js";
@@ -760,20 +761,21 @@ async function rehearsalCheckouts(workspace, brief, refresh) {
760
761
  return checkouts;
761
762
  }
762
763
  /**
763
- * Run one on-shift repository's gate once in its checkout, off the loop: a ten-minute gate
764
- * must not stop the heartbeat, or the computer reads offline while it measures. The workspace was
765
- * clean when it started, so whatever is written there afterwards is the gate's and is discarded.
764
+ * Run one on-shift repository's gate in a detached worktree at its HEAD, nested under .conduit/ as
765
+ * an attempt's is, and remove only that worktree afterwards. Run off the loop: a ten-minute gate
766
+ * must not stop the heartbeat, or the computer reads offline while it measures.
766
767
  */
767
768
  function startGateRehearsal(workspace, plan) {
768
769
  gateRehearsalRunning = true;
770
+ const rehearsalPath = rehearsalWorktreePath(workspace);
769
771
  console.log(`Rehearsing the gate in ${workspace}: ${plan.commands.join(", ")}`);
770
- void runGateRehearsal(workspace, plan)
772
+ void createRehearsalWorktree(workspace, plan.commit)
773
+ .then((path) => runGateRehearsal(path, plan))
771
774
  .then((rehearsal) => { if (rehearsal === "busy")
772
775
  console.log("Gate rehearsal deferred: another runner on this computer is rehearsing"); })
773
776
  .catch((error) => console.error(`Gate rehearsal failed: ${redactSecrets(error instanceof Error ? error.message : "unknown error")}`))
774
777
  .finally(async () => {
775
- if (!await workspaceIsClean(workspace))
776
- await discardWorktreeWrites(workspace);
778
+ await removeAttemptWorktree(workspace, rehearsalPath);
777
779
  gateRehearsalRunning = false;
778
780
  });
779
781
  }
package/dist/execution.js CHANGED
@@ -2219,11 +2219,11 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
2219
2219
  landContinuationUsed,
2220
2220
  landBaseCommit: deliveryBaseCommit,
2221
2221
  });
2222
- if ("kind" in landed) {
2222
+ if (landed.kind === "failed") {
2223
2223
  failedLandResult = landed.result;
2224
2224
  }
2225
2225
  else {
2226
- report = landed;
2226
+ report = landed.report;
2227
2227
  // Mechanical land path: the agent only pushes; Bridge pushes again if needed and the control
2228
2228
  // plane opens the change request with the organization's forge connection.
2229
2229
  report = await ensureDeliveryPullRequest({
@@ -2499,7 +2499,7 @@ async function runLandContinuationTurn(input) {
2499
2499
  }
2500
2500
  async function finalizeRepositoryLand(input) {
2501
2501
  if (!requiresLandCommit({ grants: input.grants, spec: input.spec }))
2502
- return input.report;
2502
+ return { kind: "landed", report: input.report };
2503
2503
  const applyLand = async (report) => {
2504
2504
  const land = await ensureLandCommit({
2505
2505
  workspace: input.workspace,
@@ -2520,7 +2520,7 @@ async function finalizeRepositoryLand(input) {
2520
2520
  return report;
2521
2521
  };
2522
2522
  try {
2523
- return await applyLand(input.report);
2523
+ return { kind: "landed", report: await applyLand(input.report) };
2524
2524
  }
2525
2525
  catch (error) {
2526
2526
  if (!(error instanceof AgentNoLandCommitError))
@@ -2559,7 +2559,7 @@ async function finalizeRepositoryLand(input) {
2559
2559
  catch {
2560
2560
  // Land may have succeeded even when the continuation envelope is malformed — git is authoritative.
2561
2561
  }
2562
- return applyLand(report);
2562
+ return { kind: "landed", report: await applyLand(report) };
2563
2563
  }
2564
2564
  function buildDeliveryRepairPrompt(issues, previousReply, acceptance, options = {}) {
2565
2565
  const replyTail = redactSecrets(previousReply.slice(-8_000));
@@ -3310,6 +3310,7 @@ export async function flushTerminal(client, taskId) {
3310
3310
  const { failure, signal: _signal, delivery_packet: _packet, repair_brief: _brief, ...rest } = active.terminal.body;
3311
3311
  const refusal = `Terminal report rejected by the Control Plane: ${error.message}\n${JSON.stringify(error.details ?? null)}`;
3312
3312
  const cause = failure && typeof failure === "object" ? failure : null;
3313
+ console.error(`Assignment ${taskId}: the Control Plane refused the ${active.terminal.action} report for attempt ${active.attemptId}: ${redactSecrets(refusal)}`);
3313
3314
  await client.updateAttempt(taskId, {
3314
3315
  terminal: {
3315
3316
  action: "fail",
@@ -3327,7 +3328,7 @@ export async function flushTerminal(client, taskId) {
3327
3328
  next_action: boundedSentence(cause.next_action),
3328
3329
  diagnostic_detail: boundedDetail(`${refusal}\n${typeof cause.diagnostic_detail === "string" ? cause.diagnostic_detail : ""}`),
3329
3330
  }
3330
- : bridgeCause("bridge_finalize_failed", refusal),
3331
+ : bridgeCause("terminal_report_refused", refusal),
3331
3332
  idempotency_key: rejectedKey,
3332
3333
  },
3333
3334
  },
@@ -129,6 +129,7 @@ export const BRIDGE_CAUSES = {
129
129
  verification_failed_after_run: { class: "transient", disposition: "retry", responsible_party: "conduit", message: "The agent run failed its own verification before delivery.", next_action: "Conduit will diagnose the retained failed run and brief the next authoring attempt." },
130
130
  bridge_finalize_failed: { class: "platform", disposition: "stop", responsible_party: "conduit", message: "Bridge stopped while finishing the delivery report.", next_action: "Inspect Bridge logs for the finalize fault. Fix Bridge or open a revision if the plan is wrong, then Retry." },
131
131
  work_package_invalid: { class: "platform", disposition: "stop", responsible_party: "conduit", message: "Bridge refused this work package: this Bridge release does not know its shape.", next_action: "Update Bridge and Conduit to the same release, then Retry. The work package itself needs no change from you." },
132
+ terminal_report_refused: { class: "platform", disposition: "stop", responsible_party: "conduit", message: "Conduit refused Bridge's report for this run: this Bridge release and Conduit disagree on the report shape.", next_action: "Update Bridge and Conduit to the same release, then Retry. The work itself needs no change from you." },
132
133
  bridge_preflight_missing: { class: "environment", disposition: "hold", responsible_party: "computer_operator", message: "This computer has not reported Bridge readiness.", next_action: "Run Bridge Doctor and reinstall or restart the background service, then recheck readiness." },
133
134
  bridge_preflight_stale: { class: "environment", disposition: "hold", responsible_party: "computer_operator", message: "This computer's Bridge readiness report is stale.", next_action: "Wake or restart the Bridge and wait for a fresh heartbeat, then recheck readiness." },
134
135
  hosted_computer: { class: "environment", disposition: "hold", responsible_party: "conduit", message: "This hosted computer is Conduit's responsibility.", next_action: "Conduit fixes the hosted computer; Recheck runs the work package again." },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miraland-labs/conduit-bridge",
3
- "version": "0.16.171",
3
+ "version": "0.16.173",
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": {