@miraland-labs/conduit-bridge 0.16.127 → 0.16.131

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/driver.js CHANGED
@@ -696,7 +696,7 @@ export function claudeUsage(message) {
696
696
  */
697
697
  export const CLAUDE_CODE_KNOWN_MODELS = [
698
698
  "fable", "opus", "sonnet", "haiku",
699
- "claude-fable-5-1", "claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5",
699
+ "claude-fable-5-1", "claude-opus-5-5", "claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5",
700
700
  ];
701
701
  export const claudeCodeDriver = {
702
702
  name: "claude-code",
@@ -960,7 +960,8 @@ export function cursorRunArgs(input) {
960
960
  args.push(...modelArgs(cursorDriver, input.model));
961
961
  if (input.resumeSessionId)
962
962
  args.push("--resume", input.resumeSessionId);
963
- args.push(input.prompt);
963
+ // The prompt goes on stdin, as it does for codex: Linux caps one argument at 128 KiB
964
+ // (MAX_ARG_STRLEN) and an assignment contract exceeds that, which spawned `E2BIG`.
964
965
  return args;
965
966
  }
966
967
  /** Parse `agent --list-models` output: one "id - Label" line per model. */
@@ -999,7 +1000,9 @@ export const cursorDriver = {
999
1000
  vendor: "cursor",
1000
1001
  // Cursor's own first-party families only. `--list-models` also lists other vendors' models
1001
1002
  // Cursor proxies at a much higher price (the "Other Models" allowance) — those are not owned.
1002
- owns: (id) => id.startsWith("cursor-grok-") || id.startsWith("composer-"),
1003
+ // Grok 4.7 dropped the `cursor-` prefix (`grok-4.7-high`, 2026-09-26) but Cursor still lists it
1004
+ // under "Cursor Models", beside Grok 4.6 and Composer.
1005
+ owns: (id) => id.startsWith("cursor-grok-") || id.startsWith("grok-") || id.startsWith("composer-"),
1003
1006
  },
1004
1007
  async run(input) {
1005
1008
  const fuelSource = input.fuelSource === "local" ? "local" : "conduit";
@@ -1047,7 +1050,7 @@ export const cursorDriver = {
1047
1050
  capabilities: input.capabilities ?? [],
1048
1051
  diagnosis: input.workRole === "diagnose",
1049
1052
  });
1050
- const configured = await withCursorPermissions(input.workspace, { allow: projected.allow, deny: projected.deny }, () => execute(executable, cursorRunArgs({ ...input, trustWorkspace, executionClass, force: projected.force }), input.workspace, agentTurnTimeoutMs(input), undefined, "local", undefined, input.signal));
1053
+ const configured = await withCursorPermissions(input.workspace, { allow: projected.allow, deny: projected.deny }, () => execute(executable, cursorRunArgs({ ...input, trustWorkspace, executionClass, force: projected.force }), input.workspace, agentTurnTimeoutMs(input), undefined, "local", input.prompt, input.signal));
1051
1054
  const { code, stdout, stderr } = configured;
1052
1055
  const parsed = parseCursorOutput(stdout);
1053
1056
  // Cursor prints no token or cost figure, so the run reports the one fact it can prove: it ran.
@@ -101,7 +101,7 @@ export function verificationCommandsMentioned(text, offered = []) {
101
101
  const source = text.slice(0, 20_000);
102
102
  const verbatim = [...new Set(offered.map((command) => command.trim()))]
103
103
  .filter((command) => isBoundedVerificationCommand(command) && textNamesCommand(source, command));
104
- const pattern = /(?:^|[\s("'`])((?:npm run [A-Za-z0-9][A-Za-z0-9._:-]*|pnpm [A-Za-z0-9][A-Za-z0-9._:-]*|yarn [A-Za-z0-9][A-Za-z0-9._:-]*|cargo (?:test|check)|go test(?: \.\/\.\.\.)?|make [A-Za-z0-9][A-Za-z0-9._-]*|python3? -m [A-Za-z0-9_][\w.]*(?: [\w./=:-]*[./=-][\w./=:-]*)*|pytest(?: [\w./=-]*[./=-][\w./=-]*)?|python3? [\w./-]+\.py(?: [\w./=:-]*[./=-][\w./=:-]*)*|(?:\.\/gradlew|gradle|\.\/mvnw|mvn) test|Rscript tests\/testthat\.R|Rscript -e testthat::test_local\(\)))(?=$|[\s"'`),.;:])/g;
104
+ const pattern = /(?:^|[\s("'`])((?:npm run [A-Za-z0-9][A-Za-z0-9._:-]*|pnpm [A-Za-z0-9][A-Za-z0-9._:-]*|yarn [A-Za-z0-9][A-Za-z0-9._:-]*|cargo (?:test|check|clippy)|go test(?: \.\/\.\.\.)?|make [A-Za-z0-9][A-Za-z0-9._-]*|python3? -m [A-Za-z0-9_][\w.]*(?: [\w./=:-]*[./=-][\w./=:-]*)*|pytest(?: [\w./=-]*[./=-][\w./=-]*)?|python3? [\w./-]+\.py(?: [\w./=:-]*[./=-][\w./=:-]*)*|(?:\.\/gradlew|gradle|\.\/mvnw|mvn) test|Rscript tests\/testthat\.R|Rscript -e testthat::test_local\(\)))(?=$|[\s"'`),.;:])/g;
105
105
  const mentions = [...new Set([...source.matchAll(pattern)].map((match) => match[1]))]
106
106
  .filter((mention) => isBoundedVerificationCommand(mention)
107
107
  && !verbatim.some((command) => command === mention || command.startsWith(`${mention} `)));
@@ -13,7 +13,10 @@ export function isBoundedVerificationCommand(command) {
13
13
  // A `*` is a legal character in a Python argument (`-p test_*.py` is how a repository spells its
14
14
  // own gate). Bridge starts a gate with execFile and no shell, so the star stays literal and no
15
15
  // expansion can happen. Refusing it only dropped the declaration, with no reason given.
16
- return /^(npm run [A-Za-z0-9][A-Za-z0-9._:-]*|pnpm [A-Za-z0-9][A-Za-z0-9._:-]*|yarn [A-Za-z0-9][A-Za-z0-9._:-]*|cargo (test|check)|go test(?: \.\/\.\.\.)?|make [A-Za-z0-9][A-Za-z0-9._-]*|python3? -m [A-Za-z0-9_][\w.]*(?: [\w./=:*-]+)*|pytest(?: [\w./=*-]+)?|python3? [\w./-]+\.py(?: [\w./=:-]+)*|(?:\.\/gradlew|gradle|\.\/mvnw|mvn) test|Rscript tests\/testthat\.R|Rscript -e testthat::test_local\(\))$/.test(command);
16
+ // Cargo takes flags the same way: acme-sim registered `cargo clippy --all-targets -- -D warnings`
17
+ // on 2026-09-25, the rule admitted only a bare `cargo test|check`, and five deliveries shipped
18
+ // clippy failures because the gate was dropped before any attempt could run it.
19
+ return /^(npm run [A-Za-z0-9][A-Za-z0-9._:-]*|pnpm [A-Za-z0-9][A-Za-z0-9._:-]*|yarn [A-Za-z0-9][A-Za-z0-9._:-]*|cargo (test|check|clippy)(?: [\w./=:-]+)*|go test(?: \.\/\.\.\.)?|make [A-Za-z0-9][A-Za-z0-9._-]*|python3? -m [A-Za-z0-9_][\w.]*(?: [\w./=:*-]+)*|pytest(?: [\w./=*-]+)?|python3? [\w./-]+\.py(?: [\w./=:-]+)*|(?:\.\/gradlew|gradle|\.\/mvnw|mvn) test|Rscript tests\/testthat\.R|Rscript -e testthat::test_local\(\))$/.test(command);
17
20
  }
18
21
  /** Shell commands each grant authorizes — mapped per driver so they cannot drift. */
19
22
  export const branchCreateCommands = [
package/dist/execution.js CHANGED
@@ -1180,7 +1180,11 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
1180
1180
  // diffing from the requested base would report commits that predate the agent as its work.
1181
1181
  // T138: a rework carries the first revision's resolved start, because its worktree starts at the
1182
1182
  // previous delivery's head. That lets a prose-only rework witness the original pack while still
1183
- // including any new rework changes. Older contracts fall back to the T136 rule.
1183
+ // including any new rework changes. A conflict rework is different: it is pinned to the live base
1184
+ // tip and starts fresh there, so the control plane sends no carried base and the requested base --
1185
+ // the pinned tip -- is the delivery base. Diffing from the first revision's start would report
1186
+ // every commit the base gained in between as the agent's own work. Older contracts fall back to
1187
+ // the T136 rule.
1184
1188
  const rework = task.delivery_state === "changes_requested" || Boolean(executionContract.prior_head_commit);
1185
1189
  const deliveryBaseCommit = executionContract.delivery_base_commit
1186
1190
  ?? (rework
@@ -38,8 +38,9 @@ const budgetSchema = z.object({
38
38
  export const investigationAssignmentSchema = z.object({
39
39
  id: z.string().uuid(),
40
40
  project_id: z.string().uuid(),
41
- kind: z.literal("repository"),
42
- question: z.string().min(1).max(4_000),
41
+ /** `conduct` (protocol 10): a Conductor seat turn — the question is the whole packet Conduit wrote. */
42
+ kind: z.enum(["repository", "conduct"]),
43
+ question: z.string().min(1).max(64_000),
43
44
  grants: z.array(z.string()).default([]),
44
45
  budget: budgetSchema.optional().default({}),
45
46
  repository_fingerprint: z.string().nullable().optional().default(null),
@@ -294,6 +295,24 @@ export function fitBriefToContract(candidate) {
294
295
  * lane wrote prose, a truncated fence, or a field the schema refuses. The failure now names the
295
296
  * reason and carries a bounded, redacted tail of what came back.
296
297
  */
298
+ /**
299
+ * A Conductor seat turn's report: its summary and the actions it asks for. Bridge only carries them;
300
+ * Conduit checks and applies each one when the turn settles.
301
+ */
302
+ export function parseConductReport(text) {
303
+ let candidate;
304
+ try {
305
+ candidate = parseJsonObjectCandidate(extractAgentReportJsonText(text));
306
+ }
307
+ catch {
308
+ throw new Error(`${UNUSABLE_BRIEF}: no JSON object in the reply. Reply tail: ${redactSecrets(boundedTail(text, UNUSABLE_BRIEF_TAIL_CHARS))}`);
309
+ }
310
+ const report = candidate;
311
+ if (typeof report.summary !== "string" || !report.summary.trim()) {
312
+ throw new Error(`${UNUSABLE_BRIEF}: the turn report has no summary. Reply tail: ${redactSecrets(boundedTail(text, UNUSABLE_BRIEF_TAIL_CHARS))}`);
313
+ }
314
+ return { summary: report.summary.trim().slice(0, 4_000), actions: Array.isArray(report.actions) ? report.actions.slice(0, 12) : [] };
315
+ }
297
316
  export function parseInvestigationBrief(text) {
298
317
  let candidate;
299
318
  try {
@@ -815,10 +834,12 @@ export async function executeNextInvestigation(client, config, workspace, brief,
815
834
  // T154: the lane resolves its model the way the authoring lane does. Without it Claude Code ran
816
835
  // at its CLI default, which Conduit's /v1 rejects as an unknown alias (hosted-1, 2026-09-10:
817
836
  // every verification of the first hosted delivery failed "Unknown model alias: claude-opus-5").
818
- // A verification reads and runs commands: the light tier.
819
- const selection = await resolveAssignmentModel(driver, config, { risk_level: "low" }, assignment.id, attemptWorkspace, fuel);
837
+ // A verification reads and runs commands: the light tier. A seat turn plans, writes briefs and
838
+ // judges deliveries: the deep tier.
839
+ const conduct = assignment.kind === "conduct";
840
+ const selection = await resolveAssignmentModel(driver, config, { risk_level: conduct ? "high" : "low" }, assignment.id, attemptWorkspace, fuel);
820
841
  const result = await driver.run({
821
- prompt: buildInvestigationPrompt(assignment, commit, context),
842
+ prompt: conduct ? assignment.question : buildInvestigationPrompt(assignment, commit, context),
822
843
  workspace: attemptWorkspace,
823
844
  ...(selection.model ? { model: selection.model } : {}),
824
845
  grants: assignment.grants,
@@ -839,6 +860,20 @@ export async function executeNextInvestigation(client, config, workspace, brief,
839
860
  console.error(`Investigation ${assignment.id} failed: ${redactSecrets(message)}`);
840
861
  return true;
841
862
  }
863
+ if (conduct) {
864
+ const report = parseConductReport(result.resultText ?? "");
865
+ const turnCheck = await verifyObservationOnly({ attemptWorkspace, workspace, commit, fingerprintBefore });
866
+ if (!turnCheck.ok) {
867
+ await settle(client, assignment.id, {
868
+ lease_token: leaseToken, status: "failed", error: boundedTail(`${turnCheck.error}: ${turnCheck.detail}`, 2_000), retryable: true,
869
+ });
870
+ console.error(`Seat turn ${assignment.id} discarded — ${turnCheck.error}: ${turnCheck.detail}`);
871
+ return true;
872
+ }
873
+ await settle(client, assignment.id, { lease_token: leaseToken, status: "completed", result: report });
874
+ console.log(`Seat turn ${assignment.id} settled with ${report.actions.length} action(s).`);
875
+ return true;
876
+ }
842
877
  const observed = parseInvestigationBrief(result.resultText ?? "");
843
878
  const check = await verifyObservationOnly({ attemptWorkspace, workspace, commit, fingerprintBefore });
844
879
  if (!check.ok) {
@@ -8,6 +8,7 @@ import { expandOpsValue } from "./ops.js";
8
8
  import { ensureCheckout } from "./checkout.js";
9
9
  import { bootstrapManagedWorkspace } from "./workspace-bootstrap.js";
10
10
  import { managedWorkspacePath } from "./managed-workspace-path.js";
11
+ import { refreshSourceWorkspace } from "./workspace-refresh.js";
11
12
  export { managedWorkspacePath } from "./managed-workspace-path.js";
12
13
  function pathsEqual(left, right) {
13
14
  return resolve(expandOpsValue(left)) === resolve(expandOpsValue(right));
@@ -32,6 +33,21 @@ export async function switchManagedWorkspace(managedRoot, repositoryUrl, deps =
32
33
  if (result === "cloned") {
33
34
  console.log(`Cloned ${repositoryUrl} into ${target}`);
34
35
  (deps.bootstrapWorkspace ?? bootstrapManagedWorkspace)(target);
36
+ return target;
37
+ }
38
+ // The runner's own refresh follows only the workspace it was started on. A managed checkout it
39
+ // switches into was never refreshed: acme-sim stayed shallow at a five-day-old main on
40
+ // 2026-09-25 while every claim read it. Follow origin here, before the claim reads the checkout.
41
+ // A dirty or diverged checkout is not moved; the attempt worktree refuses a dirty source.
42
+ const refresh = await refreshSourceWorkspace(target).catch(() => null);
43
+ if (refresh?.state === "fast_forwarded") {
44
+ console.log(`Managed workspace ${target} fast-forwarded to origin/${refresh.branch}.`);
45
+ }
46
+ else if (refresh?.state === "diverged") {
47
+ console.error(`Managed workspace ${target} cannot fast-forward: ${refresh.local_commits} local-only commit(s) on ${refresh.branch}. Park or push them.`);
48
+ }
49
+ else if (refresh?.state !== "up_to_date") {
50
+ console.error(`Managed workspace ${target} could not follow origin (dirty tree, detached HEAD, or fetch failed).`);
35
51
  }
36
52
  return target;
37
53
  }
package/dist/preflight.js CHANGED
@@ -30,7 +30,7 @@ function modelsFingerprint(config) {
30
30
  return createHash("sha256").update(parts.join(";")).digest("hex").slice(0, 16);
31
31
  }
32
32
  // Protocol 9 adds the Bridge-observed driver interface cohort to every preflight lane.
33
- export const BRIDGE_PROTOCOL_VERSION = 9;
33
+ export const BRIDGE_PROTOCOL_VERSION = 10;
34
34
  /** Keep the version identity within the same bound accepted by the heartbeat schema. */
35
35
  export const MAX_DRIVER_VERSION_LENGTH = 200;
36
36
  /** True for a single, bounded line with no control characters -- a version, not a notice. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miraland-labs/conduit-bridge",
3
- "version": "0.16.127",
3
+ "version": "0.16.131",
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": {