@miraland-labs/conduit-bridge 0.16.160 → 0.16.162
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/ensure-land-commit.js +10 -10
- package/dist/ensure-test-evidence.js +15 -2
- package/dist/investigation.js +21 -3
- package/package.json +1 -1
|
@@ -40,19 +40,19 @@ export async function ensureLandCommit(input) {
|
|
|
40
40
|
const listPending = input.listUncommittedPaths ?? ((workspace) => uncommittedPaths(workspace));
|
|
41
41
|
const commitPaths = input.commitPaths ?? commitScopedPaths;
|
|
42
42
|
const ahead = await countAhead(input.workspace, base);
|
|
43
|
-
if (ahead !== null && ahead > 0) {
|
|
44
|
-
return { kind: "already_landed", headCommit: await readHead(input.workspace) };
|
|
45
|
-
}
|
|
46
43
|
const pending = filterPathsInScope(await listPending(input.workspace), input.spec.change_scope ?? []);
|
|
47
|
-
if (
|
|
48
|
-
|
|
44
|
+
if (pending.length) {
|
|
45
|
+
await commitPaths(input.workspace, pending);
|
|
46
|
+
const afterAhead = await countAhead(input.workspace, base);
|
|
47
|
+
if (afterAhead === 0) {
|
|
48
|
+
throw new AgentNoLandCommitError(base);
|
|
49
|
+
}
|
|
50
|
+
return { kind: "committed", headCommit: await readHead(input.workspace), paths: pending };
|
|
49
51
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (afterAhead === 0) {
|
|
53
|
-
throw new AgentNoLandCommitError(base);
|
|
52
|
+
if (ahead !== null && ahead > 0) {
|
|
53
|
+
return { kind: "already_landed", headCommit: await readHead(input.workspace) };
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
throw new AgentNoLandCommitError(base);
|
|
56
56
|
}
|
|
57
57
|
async function currentBranch(workspace) {
|
|
58
58
|
const { stdout } = await execFileAsync("git", ["-C", workspace, "branch", "--show-current"], {
|
|
@@ -240,20 +240,33 @@ export function boundedDetailLines(lines, maxLines, identifiers = []) {
|
|
|
240
240
|
// is proved by one line in the middle of a long suite, and a reviewer must not infer it from the
|
|
241
241
|
// summary. So the lines that name an identifier the criteria name are kept too, in run order. The
|
|
242
242
|
// cap keeps a broad identifier that matches every line from storing the whole run.
|
|
243
|
+
// A run of several suites prints one summary per suite, and only the last one sits in the tail:
|
|
244
|
+
// cargo's "test result: ok. 614 passed" for the cli binary says nothing about the core binary. A
|
|
245
|
+
// criterion settled by the totals (a test count delta) needs every summary, so they are kept too.
|
|
246
|
+
const summaries = middle.filter((line) => SUITE_SUMMARY.some((form) => form.test(line))).slice(0, NAMED_DETAIL_LINES_MAX);
|
|
243
247
|
const named = identifiers.length === 0
|
|
244
248
|
? []
|
|
245
|
-
: [...new Set(middle.filter((line) => identifiers.some((id) => line.includes(id))))]
|
|
249
|
+
: [...new Set(middle.filter((line) => !summaries.includes(line) && identifiers.some((id) => line.includes(id))))]
|
|
246
250
|
.slice(0, NAMED_DETAIL_LINES_MAX)
|
|
247
251
|
.map((line) => (line.length > 500 ? `${line.slice(0, 500)}…` : line));
|
|
248
|
-
const dropped = middle.length - named.length;
|
|
252
|
+
const dropped = middle.length - summaries.length - named.length;
|
|
249
253
|
return [
|
|
250
254
|
...lines.slice(0, head),
|
|
251
255
|
`… ${dropped} line${dropped === 1 ? "" : "s"} omitted …`,
|
|
256
|
+
...summaries,
|
|
252
257
|
...named,
|
|
253
258
|
...lines.slice(lines.length - tail),
|
|
254
259
|
];
|
|
255
260
|
}
|
|
256
261
|
const NAMED_DETAIL_LINES_MAX = 40;
|
|
262
|
+
/** One suite's totals line: cargo, vitest, unittest, pytest, go test. */
|
|
263
|
+
const SUITE_SUMMARY = [
|
|
264
|
+
/^test result: /,
|
|
265
|
+
/^\s*(Test Files|Tests)\s+\d/,
|
|
266
|
+
/^Ran \d+ tests?\b/,
|
|
267
|
+
/^=+ .*\b(passed|failed)\b.* in [\d.]+s =+$/,
|
|
268
|
+
/^(ok|FAIL)\s+\S+\s+[\d.]+s$/,
|
|
269
|
+
];
|
|
257
270
|
/**
|
|
258
271
|
* The test and symbol names the acceptance criteria name: every backticked string, and every
|
|
259
272
|
* snake_case, camelCase, dotted or `::` token of at least 8 characters. An ordinary English word has
|
package/dist/investigation.js
CHANGED
|
@@ -11,6 +11,7 @@ import { boundedTail, runBoundedVerificationCommand } from "./ensure-test-eviden
|
|
|
11
11
|
import { createAttemptWorktree, removeAttemptWorktree } from "./attempt-worktree.js";
|
|
12
12
|
import { buildWorkspaceBrief, ensureCommitAvailable, normalizeRepositoryUrl } from "./brief.js";
|
|
13
13
|
import { headCommitOrNull } from "./git.js";
|
|
14
|
+
import { ConduitRequestError } from "./client.js";
|
|
14
15
|
import { learnDriverFuel, resolveAssignmentModel, changedPathsSince, runDeliveryVerificationGates, discardWorktreeWrites } from "./execution.js";
|
|
15
16
|
import { DRIVERS, execute, extractAgentReportJsonText, parseJsonObjectCandidate } from "./driver.js";
|
|
16
17
|
import { pickDriverForClaim, resolveDriverFuel, supportsReadOnlyDiagnosis } from "./drivers.js";
|
|
@@ -773,13 +774,24 @@ export async function executeNextInvestigation(client, config, workspace, brief,
|
|
|
773
774
|
const fingerprintBefore = await workspaceFingerprint(workspace);
|
|
774
775
|
let attemptWorkspace = null;
|
|
775
776
|
const runId = investigationRunId(assignment.id);
|
|
776
|
-
// Renew inside the lease for as long as the driver runs
|
|
777
|
-
// means the
|
|
777
|
+
// Renew inside the lease for as long as the driver runs. A terminal refusal (`invalid_lease`)
|
|
778
|
+
// means the control plane already took the run back — abort the driver and do not settle.
|
|
779
|
+
const runAbort = new AbortController();
|
|
780
|
+
let leaseTakenBack = false;
|
|
778
781
|
const renewTimer = setInterval(() => {
|
|
779
782
|
void client.request(`/runner/v1/investigations/${assignment.id}/renew`, {
|
|
780
783
|
method: "POST",
|
|
781
784
|
body: JSON.stringify({ lease_token: leaseToken }),
|
|
782
|
-
}).catch((error) =>
|
|
785
|
+
}).catch((error) => {
|
|
786
|
+
if (error instanceof ConduitRequestError && error.code === "invalid_lease") {
|
|
787
|
+
leaseTakenBack = true;
|
|
788
|
+
clearInterval(renewTimer);
|
|
789
|
+
runAbort.abort();
|
|
790
|
+
console.error(`Investigation ${assignment.id} stopped: lease is no longer valid`);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
console.error(`Investigation ${assignment.id} lease renewal failed: ${redactSecrets(error instanceof Error ? error.message : "unknown")}`);
|
|
794
|
+
});
|
|
783
795
|
}, options.renewIntervalMs ?? INVESTIGATION_RENEW_INTERVAL_MS);
|
|
784
796
|
renewTimer.unref?.();
|
|
785
797
|
let heartbeatRunning = false;
|
|
@@ -853,7 +865,10 @@ export async function executeNextInvestigation(client, config, workspace, brief,
|
|
|
853
865
|
fuel,
|
|
854
866
|
timeoutMs: Math.min(timeoutMs ?? assignment.budget.max_duration_ms, assignment.budget.max_duration_ms),
|
|
855
867
|
maxTurns: config.maxTurns,
|
|
868
|
+
signal: runAbort.signal,
|
|
856
869
|
});
|
|
870
|
+
if (leaseTakenBack || runAbort.signal.aborted)
|
|
871
|
+
return true;
|
|
857
872
|
await learnDriverFuel(config, driver.name, investigationFuel, result);
|
|
858
873
|
if (result.status === "failed") {
|
|
859
874
|
const message = result.error ?? "Investigation run failed";
|
|
@@ -927,6 +942,9 @@ export async function executeNextInvestigation(client, config, workspace, brief,
|
|
|
927
942
|
console.log(`Investigation ${assignment.id} settled with a bounded brief.`);
|
|
928
943
|
}
|
|
929
944
|
catch (error) {
|
|
945
|
+
if (leaseTakenBack || runAbort.signal.aborted) {
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
930
948
|
const message = error instanceof Error ? error.message : "investigation_failed";
|
|
931
949
|
await settle(client, assignment.id, {
|
|
932
950
|
lease_token: leaseToken,
|
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.162",
|
|
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": {
|