@miraland-labs/conduit-bridge 0.16.100 → 0.16.102
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/execution.js +32 -12
- package/dist/failure-signal.js +2 -0
- package/dist/git-witness.js +14 -5
- package/dist/managed-workspace-path.js +5 -0
- package/dist/on-shift-apply.js +6 -1
- package/package.json +1 -1
package/dist/execution.js
CHANGED
|
@@ -28,7 +28,7 @@ const execFileAsync = promisify(execFile);
|
|
|
28
28
|
import { captureVerificationFailure, detectGateRanNothing, ensureTestEvidence, needsTestEvidence, runBoundedVerificationCommand, verificationEvidenceCommands, VerificationGateRanNothingError } from "./ensure-test-evidence.js";
|
|
29
29
|
import { ensureChangeEvidence } from "./ensure-change-evidence.js";
|
|
30
30
|
import { ensureNormativeEvidence } from "./ensure-normative-evidence.js";
|
|
31
|
-
import { changedPathsSince, compareWorktreeState, worktreeStateFingerprint, worktreeWrittenPaths } from "./git-witness.js";
|
|
31
|
+
import { changedPathsSince, compareWorktreeState, diffStatSince, worktreeStateFingerprint, worktreeWrittenPaths } from "./git-witness.js";
|
|
32
32
|
import { assertNormativeRefsMaterialized, checkNormativeMarkers, materializeNormativeRefs, NORMATIVE_MARKER_PREFIX, NORMATIVE_REF_PREFIX, parseNormativeRefs, } from "./normative-refs.js";
|
|
33
33
|
import { checkIntegrationObligations, INTEGRATION_OBLIGATION_PREFIX, parseIntegrationObligations, } from "./integration-obligations.js";
|
|
34
34
|
/** Keep a Mac awake only while an assignment is active; display sleep remains allowed. */
|
|
@@ -831,14 +831,18 @@ export async function claimNextAssignment(client, config, workspace, brief, driv
|
|
|
831
831
|
for (const released of releasedWorktrees) {
|
|
832
832
|
// T96: resolve the released attempt's own repository under the managed root, not the runner's
|
|
833
833
|
// default workspace, so a retained tree in another managed checkout is actually swept.
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
834
|
+
try {
|
|
835
|
+
const releaseWorkspace = released.repository_fingerprint && options.managedRoot?.trim()
|
|
836
|
+
? managedWorkspacePath(options.managedRoot.trim(), released.repository_fingerprint)
|
|
837
|
+
: workspace;
|
|
838
|
+
const worktree = attemptWorktreePath(releaseWorkspace, released.attempt_id);
|
|
839
|
+
await removeAttemptWorktree(releaseWorkspace, worktree)
|
|
840
|
+
.then((removed) => { if (removed)
|
|
841
|
+
console.log(`Released settled diagnostic worktree ${worktree}`); });
|
|
842
|
+
}
|
|
843
|
+
catch (error) {
|
|
844
|
+
console.error(`Diagnostic worktree release failed: ${redactSecrets(error instanceof Error ? error.message : "unknown")}`);
|
|
845
|
+
}
|
|
842
846
|
}
|
|
843
847
|
const assignment = assignments[0];
|
|
844
848
|
if (!assignment)
|
|
@@ -1713,6 +1717,11 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
1713
1717
|
: null;
|
|
1714
1718
|
// The vendor named the model as the cause, so the model is what the retry must change. The
|
|
1715
1719
|
// model rides in the prefix: the control plane cannot read Bridge's selection any other way.
|
|
1720
|
+
const diffStat = verificationDetail && attemptWorkspace && startCommit
|
|
1721
|
+
? await diffStatSince(attemptWorkspace, startCommit)
|
|
1722
|
+
.then((stat) => stat?.slice(0, 8_000) ?? null)
|
|
1723
|
+
.catch(() => null)
|
|
1724
|
+
: null;
|
|
1716
1725
|
const abortPrefix = agentModelAbortPrefix(selection.model);
|
|
1717
1726
|
const modelAborted = agentModelAborted(`${agentMessage}\n${result.signal?.stderr_tail ?? ""}`);
|
|
1718
1727
|
// T52: the epoch's second abort is not a retryable model swap — the first one already bought
|
|
@@ -1720,7 +1729,7 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
1720
1729
|
// "Agent Looping Detected" runs on the cursor CLI default). Hold on the tier instead.
|
|
1721
1730
|
const bindingRequired = modelAborted && !verificationDetail && modelBindingRequired(selection);
|
|
1722
1731
|
const message = verificationDetail
|
|
1723
|
-
? `Verification failed after the agent run.\n${verificationDetail}`
|
|
1732
|
+
? `Verification failed after the agent run.\n${verificationDetail}${diffStat ? `\n\nChanged files:\n${diffStat}` : ""}`
|
|
1724
1733
|
: bindingRequired
|
|
1725
1734
|
? `${modelBindingRequiredPrefix(selection.tier, driver.name)}${agentMessage}`
|
|
1726
1735
|
: modelAborted ? `${abortPrefix}${agentMessage}` : agentMessage;
|
|
@@ -1740,7 +1749,7 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
1740
1749
|
? { phase: "agent", witness: "vendor", kind: "quota", vendor_message: agentMessage.slice(0, 2_000) }
|
|
1741
1750
|
: result.signal
|
|
1742
1751
|
? verificationDetail
|
|
1743
|
-
? { ...result.signal, phase: "verification", witness: "bridge", stderr_tail: undefined, stdout_tail: undefined }
|
|
1752
|
+
? { ...result.signal, phase: "verification", witness: "bridge", stderr_tail: undefined, stdout_tail: undefined, ...(diffStat ? { diff_stat: diffStat } : {}) }
|
|
1744
1753
|
: result.signal
|
|
1745
1754
|
: undefined;
|
|
1746
1755
|
const response = await queueTerminal(client, taskId, { action: "fail", body: {
|
|
@@ -2168,8 +2177,13 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
2168
2177
|
// exact tree and send the witness marker that arms Conductor diagnosis. Treating it as a
|
|
2169
2178
|
// delivery-envelope defect would stop before Invariant 23 ever ran.
|
|
2170
2179
|
const verificationFailed = /^Agent report: Verification failed \(/.test(message);
|
|
2180
|
+
const diffStat = verificationFailed && attemptWorkspace
|
|
2181
|
+
? await diffStatSince(attemptWorkspace, deliveryBaseCommit)
|
|
2182
|
+
.then((stat) => stat?.slice(0, 8_000) ?? null)
|
|
2183
|
+
.catch(() => null)
|
|
2184
|
+
: null;
|
|
2171
2185
|
const classified = verificationFailed
|
|
2172
|
-
? { retryable: true, error: `Verification failed after the agent run.\n${message}` }
|
|
2186
|
+
? { retryable: true, error: `Verification failed after the agent run.\n${message}${diffStat ? `\n\nChanged files:\n${diffStat}` : ""}` }
|
|
2173
2187
|
// classifyFinalizeFailure: forge transport → retryable; contract/environment/unknown → fail
|
|
2174
2188
|
// closed (unknown prefixed as "Bridge finalize interrupted", not laundered as contract).
|
|
2175
2189
|
: classifyFinalizeFailure(message);
|
|
@@ -2180,6 +2194,12 @@ async function runAttempt(client, config, driver, workspace, brief, taskId, watc
|
|
|
2180
2194
|
body: {
|
|
2181
2195
|
error: classified.error,
|
|
2182
2196
|
retryable: classified.retryable,
|
|
2197
|
+
...(verificationFailed ? { signal: {
|
|
2198
|
+
phase: "verification",
|
|
2199
|
+
witness: "bridge",
|
|
2200
|
+
kind: "verification_failed",
|
|
2201
|
+
...(diffStat ? { diff_stat: diffStat } : {}),
|
|
2202
|
+
} } : {}),
|
|
2183
2203
|
// A rework's own PR may already be open (ensureDeliveryPullRequest ran before this
|
|
2184
2204
|
// gate). Carry the URL so the control plane can close it instead of littering.
|
|
2185
2205
|
...(report.pull_request_url ? { pull_request_url: report.pull_request_url } : {}),
|
package/dist/failure-signal.js
CHANGED
|
@@ -24,6 +24,8 @@ export const failureSignalSchema = z.object({
|
|
|
24
24
|
paths: z.array(z.string().max(300)).max(20).optional(),
|
|
25
25
|
/** The base commit the dry run ran against, when a gate already fails there. */
|
|
26
26
|
base_commit: z.string().max(200).optional(),
|
|
27
|
+
/** Short git diff --stat of files modified in the attempt worktree. */
|
|
28
|
+
diff_stat: z.string().max(8_000).optional(),
|
|
27
29
|
exit_code: z.number().int().nullable().optional(),
|
|
28
30
|
/** The vendor envelope's own status, e.g. agy's "ERROR". */
|
|
29
31
|
vendor_status: z.string().max(200).optional(),
|
package/dist/git-witness.js
CHANGED
|
@@ -123,11 +123,20 @@ export function compareWorktreeState(before, after) {
|
|
|
123
123
|
/** Short stat summary for change evidence artifacts. */
|
|
124
124
|
export async function diffStatSince(worktree, startCommit) {
|
|
125
125
|
try {
|
|
126
|
-
const { stdout }
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
const [{ stdout }, { stdout: untracked }] = await Promise.all([
|
|
127
|
+
execFileAsync("git", ["-C", worktree, "diff", "--stat", startCommit], {
|
|
128
|
+
timeout: 30_000,
|
|
129
|
+
maxBuffer: 2_000_000,
|
|
130
|
+
}),
|
|
131
|
+
execFileAsync("git", ["-C", worktree, "ls-files", "--others", "--exclude-standard", "-z"], {
|
|
132
|
+
timeout: 30_000,
|
|
133
|
+
maxBuffer: 2_000_000,
|
|
134
|
+
encoding: "buffer",
|
|
135
|
+
}),
|
|
136
|
+
]);
|
|
137
|
+
const untrackedLines = untracked.toString("utf8").split("\0").filter(Boolean)
|
|
138
|
+
.map((path) => ` ${path} | untracked`);
|
|
139
|
+
const text = [stdout.trim(), ...untrackedLines].filter(Boolean).join("\n");
|
|
131
140
|
return text || null;
|
|
132
141
|
}
|
|
133
142
|
catch {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { normalizeRepositoryUrl } from "./brief.js";
|
|
2
2
|
/** Where a managed computer keeps `repositoryUrl` — `<root>/<owner>/<repo>`. */
|
|
3
3
|
export function managedWorkspacePath(managedRoot, repositoryUrl) {
|
|
4
|
+
if (repositoryUrl.includes("\\"))
|
|
5
|
+
throw new Error("managed_workspace_repository_invalid");
|
|
4
6
|
const parts = normalizeRepositoryUrl(repositoryUrl.trim()).split("/").filter(Boolean);
|
|
7
|
+
if (parts.length === 0 || parts.some((part) => part === "." || part === "..")) {
|
|
8
|
+
throw new Error("managed_workspace_repository_invalid");
|
|
9
|
+
}
|
|
5
10
|
const name = parts[parts.length - 1] || "workspace";
|
|
6
11
|
const owner = parts.length >= 2 ? parts[parts.length - 2] : null;
|
|
7
12
|
const root = managedRoot.trim().replace(/[/]+$/, "");
|
package/dist/on-shift-apply.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* machine-wide ops.env. The rebound runner's matching heartbeat clears the intent;
|
|
4
4
|
* installation alone is not proof that the new process is actually bound.
|
|
5
5
|
*/
|
|
6
|
-
import { resolve } from "node:path";
|
|
6
|
+
import { relative, resolve } from "node:path";
|
|
7
7
|
import { expandOpsValue } from "./ops.js";
|
|
8
8
|
import { ensureCheckout } from "./checkout.js";
|
|
9
9
|
import { bootstrapManagedWorkspace } from "./workspace-bootstrap.js";
|
|
@@ -21,7 +21,12 @@ function pathsEqual(left, right) {
|
|
|
21
21
|
* gives a managed checkout.
|
|
22
22
|
*/
|
|
23
23
|
export async function switchManagedWorkspace(managedRoot, repositoryUrl, deps = {}) {
|
|
24
|
+
const root = resolve(expandOpsValue(managedRoot));
|
|
24
25
|
const target = resolve(expandOpsValue(managedWorkspacePath(managedRoot, repositoryUrl)));
|
|
26
|
+
const fromRoot = relative(root, target);
|
|
27
|
+
if (!fromRoot || fromRoot === ".." || fromRoot.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || resolve(root, fromRoot) !== target) {
|
|
28
|
+
throw new Error("managed_workspace_path_outside_root");
|
|
29
|
+
}
|
|
25
30
|
const checkout = deps.ensureCheckout ?? ensureCheckout;
|
|
26
31
|
const result = await checkout(target, repositoryUrl);
|
|
27
32
|
if (result === "cloned") {
|
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.102",
|
|
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": {
|