@miraland-labs/conduit-bridge 0.16.16 → 0.16.18

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/checkout.js CHANGED
@@ -35,16 +35,39 @@ async function gitOriginUrl(workspace, exec) {
35
35
  }
36
36
  }
37
37
  /** A control-plane repository fingerprint needs a transport before Git can clone it. */
38
- function cloneRepositoryUrl(repositoryUrl) {
38
+ function cloneRepositoryUrls(repositoryUrl) {
39
39
  const value = repositoryUrl.trim();
40
40
  const slash = value.indexOf("/");
41
41
  if (slash <= 0 || value.startsWith(".") || value.includes("\\") || /^[^/@\s]+@[^:]+:/.test(value))
42
- return value;
42
+ return [value];
43
43
  const host = value.slice(0, slash);
44
44
  const path = value.slice(slash + 1);
45
45
  if (!host.includes(".") || /\s/.test(value) || path.split("/").filter(Boolean).length < 2)
46
- return value;
47
- return `https://${value}`;
46
+ return [value];
47
+ return [`https://${value}`, `git@${host}:${path}${path.endsWith(".git") ? "" : ".git"}`];
48
+ }
49
+ async function cloneWorkspace(workspace, repositoryUrls, exec, accessFn, readdirFn) {
50
+ let lastError;
51
+ for (const [index, repositoryUrl] of repositoryUrls.entries()) {
52
+ try {
53
+ await exec("git", ["clone", "--", repositoryUrl, workspace], {
54
+ timeout: 300_000,
55
+ maxBuffer: 2_000_000,
56
+ });
57
+ return;
58
+ }
59
+ catch (error) {
60
+ lastError = error;
61
+ if (index === repositoryUrls.length - 1)
62
+ break;
63
+ const exists = await pathExists(workspace, accessFn);
64
+ const entries = exists ? await readdirFn(workspace).catch(() => null) : [];
65
+ if (entries === null || entries.length > 0)
66
+ break;
67
+ }
68
+ }
69
+ const detail = lastError instanceof Error ? lastError.message : "clone_failed";
70
+ throw new Error(`ensure_checkout_clone_failed:${detail}`);
48
71
  }
49
72
  /**
50
73
  * Ensure `workspace` is a checkout of `repositoryUrl`.
@@ -61,20 +84,11 @@ export async function ensureCheckout(workspace, repositoryUrl, deps = {}) {
61
84
  const wanted = normalizeRepositoryUrl(repositoryUrl);
62
85
  if (!wanted)
63
86
  throw new Error("ensure_checkout_invalid_url");
64
- const cloneUrl = cloneRepositoryUrl(repositoryUrl);
87
+ const cloneUrls = cloneRepositoryUrls(repositoryUrl);
65
88
  const exists = await pathExists(workspace, accessFn);
66
89
  if (!exists) {
67
90
  await mkdirFn(dirname(workspace), { recursive: true });
68
- try {
69
- await exec("git", ["clone", "--", cloneUrl, workspace], {
70
- timeout: 300_000,
71
- maxBuffer: 2_000_000,
72
- });
73
- }
74
- catch (error) {
75
- const detail = error instanceof Error ? error.message : "clone_failed";
76
- throw new Error(`ensure_checkout_clone_failed:${detail}`);
77
- }
91
+ await cloneWorkspace(workspace, cloneUrls, exec, accessFn, readdirFn);
78
92
  const origin = await gitOriginUrl(workspace, exec);
79
93
  if (!origin || normalizeRepositoryUrl(origin) !== wanted) {
80
94
  throw new Error("ensure_checkout_clone_mismatch");
@@ -85,16 +99,7 @@ export async function ensureCheckout(workspace, repositoryUrl, deps = {}) {
85
99
  if (entries === null)
86
100
  throw new Error("ensure_checkout_path_unreadable");
87
101
  if (entries.length === 0) {
88
- try {
89
- await exec("git", ["clone", "--", cloneUrl, workspace], {
90
- timeout: 300_000,
91
- maxBuffer: 2_000_000,
92
- });
93
- }
94
- catch (error) {
95
- const detail = error instanceof Error ? error.message : "clone_failed";
96
- throw new Error(`ensure_checkout_clone_failed:${detail}`);
97
- }
102
+ await cloneWorkspace(workspace, cloneUrls, exec, accessFn, readdirFn);
98
103
  return "cloned";
99
104
  }
100
105
  const origin = await gitOriginUrl(workspace, exec);
package/dist/driver.js CHANGED
@@ -324,16 +324,21 @@ export function parseAgentReport(text, acceptance) {
324
324
  throw new Error(`Agent report is invalid${at}: ${issue?.message ?? "unknown validation error"}`);
325
325
  }
326
326
  const reportedKeys = parsed.data.acceptance_results.map((item) => criterionKey(item.criterion));
327
+ const approvedKeys = new Set(acceptance.map(criterionKey));
327
328
  const duplicated = parsed.data.acceptance_results
328
329
  .filter((item, index) => reportedKeys.indexOf(criterionKey(item.criterion)) !== index)
329
330
  .map((item) => item.criterion);
330
331
  const missing = acceptance.filter((criterion) => !reportedKeys.includes(criterionKey(criterion)));
331
- if (missing.length || duplicated.length) {
332
+ const unapproved = parsed.data.acceptance_results
333
+ .filter((item) => !approvedKeys.has(criterionKey(item.criterion)))
334
+ .map((item) => item.criterion);
335
+ if (missing.length || duplicated.length || unapproved.length) {
332
336
  // Name the criterion. This text becomes the failure detail and the Conductor repair brief, and
333
337
  // "one of them is wrong" gives the next run nothing to act on.
334
338
  const detail = [
335
339
  missing.length ? `missing: ${missing.map(quoteCriterion).join("; ")}` : "",
336
340
  duplicated.length ? `reported more than once: ${duplicated.map(quoteCriterion).join("; ")}` : "",
341
+ unapproved.length ? `not approved: ${unapproved.map(quoteCriterion).join("; ")}` : "",
337
342
  ].filter(Boolean).join(" | ");
338
343
  throw new Error(`Agent report must include every acceptance criterion exactly once (${detail})`);
339
344
  }
package/dist/execution.js CHANGED
@@ -1406,9 +1406,13 @@ async function runClaimedAssignment(client, config, driver, workspace, brief, ta
1406
1406
  return;
1407
1407
  }
1408
1408
  await client.updateAttempt(taskId, { phase: "agent_finished", delivery: { spec, report } });
1409
- await submitFinishedDelivery(client, taskId);
1410
- deliverySubmitted = true;
1411
- console.log(`Assignment ${taskId} delivered for review and acceptance.`);
1409
+ const response = await submitFinishedDelivery(client, taskId);
1410
+ retainAttemptWorktree = response.retain_worktree === true;
1411
+ deliverySubmitted = response.status !== "failed";
1412
+ if (deliverySubmitted)
1413
+ console.log(`Assignment ${taskId} delivered for review and acceptance.`);
1414
+ else
1415
+ console.error(`Assignment ${taskId} Delivery was rejected and routed to Conductor repair.`);
1412
1416
  }
1413
1417
  finally {
1414
1418
  releaseIdleSleep();
@@ -1579,7 +1583,7 @@ async function submitFinishedDelivery(client, taskId) {
1579
1583
  throw new Error("Finished agent run is missing its persisted Delivery data");
1580
1584
  const terminal = await prepareDelivery(client, active.attemptId, taskId, active.delivery.report);
1581
1585
  await client.attemptRequest(taskId, "progress", { phase: "preparing_delivery", message: "Agent finished; submitting the Delivery.", idempotency_key: `bridge:progress:${active.attemptId}:delivery` });
1582
- await queueTerminal(client, taskId, terminal);
1586
+ return queueTerminal(client, taskId, terminal);
1583
1587
  }
1584
1588
  export const modelListCache = new Map();
1585
1589
  const MODEL_LIST_TTL_MS = 6 * 60 * 60 * 1000;
@@ -1869,6 +1873,31 @@ export async function flushTerminal(client, taskId) {
1869
1873
  await client.clearAttempt(taskId);
1870
1874
  return { accepted: false, status: "invalid_lease" };
1871
1875
  }
1876
+ if (error instanceof ConduitRequestError
1877
+ && error.code === "criterion_unapproved"
1878
+ && active.terminal.action === "complete") {
1879
+ const detail = `Delivery report rejected by the Control Plane: ${error.message}`;
1880
+ await client.updateAttempt(taskId, {
1881
+ terminal: {
1882
+ action: "fail",
1883
+ body: {
1884
+ error: detail,
1885
+ retryable: false,
1886
+ failure: {
1887
+ code: "delivery_report_invalid",
1888
+ class: "contract",
1889
+ disposition: "rework",
1890
+ responsible_party: "conduit",
1891
+ message: "The completed delivery report did not match the owner-approved acceptance criteria.",
1892
+ next_action: "Conductor will prepare bounded recovery guidance. You do not need to edit technical constraints.",
1893
+ diagnostic_detail: detail,
1894
+ },
1895
+ idempotency_key: `bridge:delivery-rejected:${active.attemptId}`,
1896
+ },
1897
+ },
1898
+ });
1899
+ return flushTerminal(client, taskId);
1900
+ }
1872
1901
  throw error;
1873
1902
  }
1874
1903
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miraland-labs/conduit-bridge",
3
- "version": "0.16.16",
3
+ "version": "0.16.18",
4
4
  "description": "Conduit Bridge CLI — 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": {