@miraland-labs/conduit-bridge 0.16.208 → 0.16.210
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 +26 -6
- package/dist/ensure-land-commit.js +5 -4
- package/dist/execution.js +16 -9
- package/package.json +1 -1
package/dist/driver.js
CHANGED
|
@@ -244,6 +244,12 @@ export function buildAssignmentPrompt(context) {
|
|
|
244
244
|
if (packageContext?.decisions?.length) {
|
|
245
245
|
lines.push("", "PROJECT DECISIONS (explicit human decisions — treat as binding context)", ...packageContext.decisions.map((item) => `- Q: ${item.question}\n A: ${item.decision}`));
|
|
246
246
|
}
|
|
247
|
+
// The earlier work comes before the brief: the brief names a fix to that work.
|
|
248
|
+
if (context.resumeHead) {
|
|
249
|
+
lines.push("", context.repairMode === "briefed"
|
|
250
|
+
? `${RESUME_PROMPT_HEADING} — an earlier run of this same package failed its verification before delivery.`
|
|
251
|
+
: `${RESUME_PROMPT_HEADING} — an earlier run of this same package ran out of time before it finished.`, `Its work is already on this branch at commit ${context.resumeHead}. Continue from that work: read what is there, keep what is correct, and finish the package. Do not start over.`);
|
|
252
|
+
}
|
|
247
253
|
if (rework) {
|
|
248
254
|
// T55 row 9: a briefed repair never delivered, so "an independent review returned this
|
|
249
255
|
// delivery" is false for it — the brief's own first line already says the opposite. Head it by
|
|
@@ -253,9 +259,6 @@ export function buildAssignmentPrompt(context) {
|
|
|
253
259
|
: "REWORK FEEDBACK — an independent review returned this delivery; address every point";
|
|
254
260
|
lines.push("", `${heading}\n${rework}`);
|
|
255
261
|
}
|
|
256
|
-
if (context.resumeHead) {
|
|
257
|
-
lines.push("", `${RESUME_PROMPT_HEADING} — an earlier run of this same package ran out of time before it finished.`, `Its work is already on this branch at commit ${context.resumeHead}. Continue from that work: read what is there, keep what is correct, and finish the package. Do not start over.`);
|
|
258
|
-
}
|
|
259
262
|
const mustCommit = context.grants.includes("repo_write") && context.grants.includes("branch_create");
|
|
260
263
|
const mustOpenPr = mustCommit && context.grants.includes("pr_create");
|
|
261
264
|
const executionClass = context.executionClass
|
|
@@ -728,7 +731,7 @@ export function claudeCodeFuelEnv(fuelSource, model) {
|
|
|
728
731
|
return undefined;
|
|
729
732
|
return { ANTHROPIC_MODEL: model, ANTHROPIC_SMALL_FAST_MODEL: model, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
|
|
730
733
|
}
|
|
731
|
-
export function codexSandboxFacts(projection) {
|
|
734
|
+
export function codexSandboxFacts(projection, writableRoots = []) {
|
|
732
735
|
return [
|
|
733
736
|
"SANDBOX FACTS",
|
|
734
737
|
`Sandbox mode: ${projection.sandbox}.`,
|
|
@@ -736,7 +739,9 @@ export function codexSandboxFacts(projection) {
|
|
|
736
739
|
? "Network is on."
|
|
737
740
|
: "Network is off, including loopback listen.",
|
|
738
741
|
projection.sandbox === "workspace-write"
|
|
739
|
-
?
|
|
742
|
+
? writableRoots.length
|
|
743
|
+
? `Writes are allowed only in the workspace, TMPDIR and ${writableRoots.join(", ")}. HOME and the rest of the filesystem are read-only.`
|
|
744
|
+
: "Writes are allowed only in the workspace and TMPDIR. HOME and the rest of the filesystem are read-only."
|
|
740
745
|
: "No writes are allowed. The filesystem is read-only.",
|
|
741
746
|
"Child processes inherit these limits.",
|
|
742
747
|
"A registered gate that fails only with EPERM, EROFS, or a refused listen does not test the change.",
|
|
@@ -765,11 +770,22 @@ export function codexExecArgs(input, sandbox) {
|
|
|
765
770
|
if (sandbox === "workspace-write" && networkAccess) {
|
|
766
771
|
args.push("-c", "sandbox_workspace_write.network_access=true");
|
|
767
772
|
}
|
|
773
|
+
if (sandbox === "workspace-write" && input.writableRoots?.length) {
|
|
774
|
+
// `exec resume` takes no --add-dir, so both forms set the roots as a config override.
|
|
775
|
+
args.push("-c", `sandbox_workspace_write.writable_roots=${JSON.stringify(input.writableRoots)}`);
|
|
776
|
+
}
|
|
768
777
|
if (input.resumeSessionId)
|
|
769
778
|
args.push(input.resumeSessionId);
|
|
770
779
|
args.push("-");
|
|
771
780
|
return args;
|
|
772
781
|
}
|
|
782
|
+
/** The machine's Cargo cache and build directory that exist, for a Codex agent to write. */
|
|
783
|
+
export function codexCargoRoots(env) {
|
|
784
|
+
return ["CARGO_HOME", "CARGO_TARGET_DIR"].flatMap((name) => {
|
|
785
|
+
const path = env[name];
|
|
786
|
+
return path && existsSync(path) ? [path] : [];
|
|
787
|
+
});
|
|
788
|
+
}
|
|
773
789
|
/**
|
|
774
790
|
* Since the July 2026 desktop merge, macOS machines often carry the codex CLI
|
|
775
791
|
* only inside the ChatGPT app bundle rather than on PATH.
|
|
@@ -866,8 +882,12 @@ export const codexDriver = {
|
|
|
866
882
|
capabilities: input.capabilities,
|
|
867
883
|
diagnosis: input.workRole === "diagnose",
|
|
868
884
|
});
|
|
885
|
+
// The agent shares the gate's Cargo cache and build directory (boundedEnvironment). Codex
|
|
886
|
+
// workspace-write made them read-only, so no cargo command the agent started could run
|
|
887
|
+
// (acme-sim FH17a, 2026-11-04: EROFS on .cargo-lock); the gate sandbox already writes them.
|
|
888
|
+
const writableRoots = projected.sandbox === "workspace-write" ? codexCargoRoots(process.env) : [];
|
|
869
889
|
// Prompt via stdin avoids ARG_MAX limits on large assignment contracts.
|
|
870
|
-
const { code, stdout, stderr } = await execute(driverExecutable(codexDriver.name, input.executable), codexExecArgs({ ...input, networkAccess: projected.networkAccess }, projected.sandbox), input.workspace, agentTurnTimeoutMs(input), fuelSource === "conduit" ? input.fuel : undefined, fuelSource, `${input.prompt}\n\n${codexSandboxFacts(projected)}`, input.signal);
|
|
890
|
+
const { code, stdout, stderr } = await execute(driverExecutable(codexDriver.name, input.executable), codexExecArgs({ ...input, networkAccess: projected.networkAccess, writableRoots }, projected.sandbox), input.workspace, agentTurnTimeoutMs(input), fuelSource === "conduit" ? input.fuel : undefined, fuelSource, `${input.prompt}\n\n${codexSandboxFacts(projected, writableRoots)}`, input.signal);
|
|
871
891
|
const parsed = parseCodexJsonl(stdout);
|
|
872
892
|
const resultText = parsed.resultText ?? (stdout || null);
|
|
873
893
|
if (code !== 0) {
|
|
@@ -63,16 +63,17 @@ async function currentBranch(workspace) {
|
|
|
63
63
|
return stdout.trim();
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
* A run that spent Conduit's package budget keeps its work for the
|
|
67
|
-
* agent left uncommitted, push the attempt branch where the grants allow
|
|
68
|
-
* Null when the attempt has nothing after base — the
|
|
66
|
+
* A run that spent Conduit's package budget or failed its gate keeps its work for the next attempt:
|
|
67
|
+
* commit the scoped files the agent left uncommitted, push the attempt branch where the grants allow
|
|
68
|
+
* a push, and name the head. Null when the attempt has nothing after base — the next attempt then
|
|
69
|
+
* starts fresh.
|
|
69
70
|
*/
|
|
70
71
|
export async function checkpointWorkInProgress(input) {
|
|
71
72
|
if (!requiresLandCommit({ grants: input.grants, spec: input.spec }))
|
|
72
73
|
return null;
|
|
73
74
|
const pending = filterPathsInScope(await uncommittedPaths(input.workspace), input.spec.change_scope ?? []);
|
|
74
75
|
if (pending.length) {
|
|
75
|
-
await commitScopedPaths(input.workspace, pending, "Conduit: work in progress
|
|
76
|
+
await commitScopedPaths(input.workspace, pending, "Conduit: work in progress for the next attempt");
|
|
76
77
|
}
|
|
77
78
|
const ahead = await commitsAheadOfBase(input.workspace, input.baseCommit);
|
|
78
79
|
if (!ahead)
|
package/dist/execution.js
CHANGED
|
@@ -1734,16 +1734,17 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
1734
1734
|
clearInterval(heartbeatTimer);
|
|
1735
1735
|
};
|
|
1736
1736
|
attemptDisposers.set(taskId, disposeTimers);
|
|
1737
|
-
// A run that spent Conduit's package budget keeps its work: the
|
|
1738
|
-
//
|
|
1739
|
-
//
|
|
1737
|
+
// A run that spent Conduit's package budget or failed its gate keeps its work: the next attempt
|
|
1738
|
+
// resumes from the head reported here instead of starting over at the base. The tree stays on this
|
|
1739
|
+
// computer, where that head is always reachable.
|
|
1740
|
+
const checkpointForNextAttempt = () => diagnosis ? Promise.resolve(null) : checkpointWorkInProgress({
|
|
1741
|
+
workspace: attemptWorkspace, spec, grants, baseCommit: deliveryBaseCommit,
|
|
1742
|
+
}).catch((error) => {
|
|
1743
|
+
console.error(`Work in progress was not committed: ${redactSecrets(error instanceof Error ? error.message : "unknown")}`);
|
|
1744
|
+
return null;
|
|
1745
|
+
});
|
|
1740
1746
|
const reportTurnTimeout = async (message) => {
|
|
1741
|
-
const resume =
|
|
1742
|
-
workspace: attemptWorkspace, spec, grants, baseCommit: deliveryBaseCommit,
|
|
1743
|
-
}).catch((error) => {
|
|
1744
|
-
console.error(`Work in progress was not committed: ${redactSecrets(error instanceof Error ? error.message : "unknown")}`);
|
|
1745
|
-
return null;
|
|
1746
|
-
});
|
|
1747
|
+
const resume = machineBoundTimeout(agentTimeout) ? null : await checkpointForNextAttempt();
|
|
1747
1748
|
retainAttemptWorktree = true;
|
|
1748
1749
|
const response = await queueTerminal(client, taskId, {
|
|
1749
1750
|
action: "fail",
|
|
@@ -1829,9 +1830,12 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
1829
1830
|
? { ...result.signal, phase: "verification", witness: "bridge", kind: "verification_failed", command: capturedVerification.command, stderr_tail: undefined, stdout_tail: undefined, ...(diffStat ? { diff_stat: diffStat } : {}) }
|
|
1830
1831
|
: result.signal
|
|
1831
1832
|
: undefined;
|
|
1833
|
+
// A repair brief describes a fix to this work, so the briefed attempt must start from it.
|
|
1834
|
+
const resume = verificationDetail ? await checkpointForNextAttempt() : null;
|
|
1832
1835
|
retainAttemptWorktree = true;
|
|
1833
1836
|
const response = await queueTerminal(client, taskId, { action: "fail", body: {
|
|
1834
1837
|
error: message,
|
|
1838
|
+
...(resume ? { resume } : {}),
|
|
1835
1839
|
retryable: bindingRequired ? false : retryableAgentFailure(agentMessage),
|
|
1836
1840
|
// Bridge's own readings of the vendor's words name the cause, in the order the control plane
|
|
1837
1841
|
// always applied: a computer fact (sign-in, incompatible or missing CLI), then a model abort,
|
|
@@ -2490,12 +2494,15 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
2490
2494
|
// classifyFinalizeFailure: forge transport → retryable; contract/environment/unknown → fail
|
|
2491
2495
|
// closed (unknown prefixed as "Bridge finalize interrupted", not laundered as contract).
|
|
2492
2496
|
: classifyFinalizeFailure(message);
|
|
2497
|
+
// A repair brief describes a fix to this work, so the briefed attempt must start from it.
|
|
2498
|
+
const resume = verificationFailed ? await checkpointForNextAttempt() : null;
|
|
2493
2499
|
retainAttemptWorktree = true;
|
|
2494
2500
|
const response = await queueTerminal(client, taskId, {
|
|
2495
2501
|
action: "fail",
|
|
2496
2502
|
body: {
|
|
2497
2503
|
error: classified.error,
|
|
2498
2504
|
retryable: classified.retryable,
|
|
2505
|
+
...(resume ? { resume } : {}),
|
|
2499
2506
|
...(!verificationFailed && classified.failure ? { failure: classified.failure } : {}),
|
|
2500
2507
|
...(verificationFailed ? { signal: {
|
|
2501
2508
|
phase: "verification",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.210",
|
|
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": {
|