@miraland-labs/conduit-bridge 0.9.7 → 0.9.8
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/README.md +13 -1
- package/dist/driver.js +2 -2
- package/dist/execution.js +3 -0
- package/dist/preflight.js +18 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Local Bridge CLI for [Conduit](https://github.com/miralandlabs/conduit). Connects a computer to one organization, claims work, and drives a local agent.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Package version:** `0.9.8` — heartbeat protocol 2 requires a clean workspace plus a ready, versioned driver lane before dispatch. It also includes **ops** helpers for macOS / Linux / Windows, LaunchAgent/systemd **PATH** for `~/.local/bin`, disconnect/disengage, multi-driver lanes, shared slots **1–4**, worktrees, and optional `--ensure-checkout`. Protocol 1 heartbeats remain compatible for presence but cannot receive work.
|
|
6
6
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
|
|
@@ -63,6 +63,18 @@ npx @miraland-labs/conduit-bridge disconnect --yes
|
|
|
63
63
|
|
|
64
64
|
**Capacity:** Connect-approved `lease_capacity` (1–4) is a **shared pool** for all online lanes on that computer.
|
|
65
65
|
|
|
66
|
+
## Readiness and lane status
|
|
67
|
+
|
|
68
|
+
Before agent spend, Bridge checks that the workspace is readable, matches the expected repository,
|
|
69
|
+
and is clean; each online driver must be installed, compatible with its configured fuel, and signed
|
|
70
|
+
in when local fuel is used. The same bounded report is sent on heartbeat so Conduit can place an
|
|
71
|
+
environment failure on Hold without consuming an execution attempt. Fix the named issue and let a
|
|
72
|
+
fresh heartbeat land before choosing **Recheck** in Activity.
|
|
73
|
+
|
|
74
|
+
Conduit labels an exact driver/CLI version **Certified** only after two consecutive real canaries:
|
|
75
|
+
repository delivery with PR/evidence and a successful rework cycle. All other lanes are
|
|
76
|
+
**Experimental**. A lane runs only when its computer operator explicitly brings it online.
|
|
77
|
+
|
|
66
78
|
## Grant enforcement
|
|
67
79
|
|
|
68
80
|
| Driver | Fuel | Mechanism |
|
package/dist/driver.js
CHANGED
|
@@ -345,7 +345,7 @@ export function codexSandboxForGrants(grants) {
|
|
|
345
345
|
}
|
|
346
346
|
/**
|
|
347
347
|
* Codex exec arguments. The sandbox is the enforcement boundary: network stays
|
|
348
|
-
* off inside workspace-write unless the
|
|
348
|
+
* off inside workspace-write unless the canonical contract requires external_network.
|
|
349
349
|
*/
|
|
350
350
|
export function codexExecArgs(input, sandbox) {
|
|
351
351
|
// exec is non-interactive by design (no approval flag), and `exec resume`
|
|
@@ -355,7 +355,7 @@ export function codexExecArgs(input, sandbox) {
|
|
|
355
355
|
: ["exec", "--json", "--sandbox", sandbox];
|
|
356
356
|
if (input.model)
|
|
357
357
|
args.push("-m", input.model);
|
|
358
|
-
if (sandbox === "workspace-write" && input.
|
|
358
|
+
if (sandbox === "workspace-write" && input.capabilities?.includes("external_network")) {
|
|
359
359
|
args.push("-c", "sandbox_workspace_write.network_access=true");
|
|
360
360
|
}
|
|
361
361
|
if (input.resumeSessionId)
|
package/dist/execution.js
CHANGED
|
@@ -45,6 +45,7 @@ const taskDetailSchema = z.object({
|
|
|
45
45
|
const taskSpecSchema = z.object({
|
|
46
46
|
goal: z.string().optional(), scope: z.array(z.string()).optional(), boundaries: z.array(z.string()).optional(),
|
|
47
47
|
acceptance: z.array(z.string()).optional(), required_evidence: z.array(z.enum(evidenceKinds)).optional(),
|
|
48
|
+
required_capabilities: z.array(z.string()).optional(),
|
|
48
49
|
change_scope: z.array(z.string()).optional(), work_role: z.string().optional(),
|
|
49
50
|
repository: z.object({ url: z.string().optional(), base_commit: z.string().optional() }).nullable().optional(),
|
|
50
51
|
risk_level: z.string().optional(),
|
|
@@ -399,6 +400,7 @@ async function runClaimedAssignment(client, config, driver, workspace, brief, ta
|
|
|
399
400
|
prompt,
|
|
400
401
|
workspace: attemptWorkspace,
|
|
401
402
|
grants,
|
|
403
|
+
capabilities: spec.required_capabilities ?? [],
|
|
402
404
|
verificationCommands: liveBrief?.verification ?? [],
|
|
403
405
|
resumeSessionId,
|
|
404
406
|
timeoutMs,
|
|
@@ -436,6 +438,7 @@ async function runClaimedAssignment(client, config, driver, workspace, brief, ta
|
|
|
436
438
|
// Preserve only existing read authority. No write, test, branch, or push
|
|
437
439
|
// capability is available while the agent repairs the response envelope.
|
|
438
440
|
grants: grants.filter((grant) => grant === "repo_read"),
|
|
441
|
+
capabilities: [],
|
|
439
442
|
verificationCommands: [],
|
|
440
443
|
resumeSessionId: result.sessionId ?? undefined,
|
|
441
444
|
timeoutMs,
|
package/dist/preflight.js
CHANGED
|
@@ -3,7 +3,7 @@ import { promisify } from "node:util";
|
|
|
3
3
|
import { buildWorkspaceBrief, normalizeRepositoryUrl } from "./brief.js";
|
|
4
4
|
import { hasAntigravityLogin, hasClaudeLogin, hasOpenAiLogin, hasOpenCodeLogin, resolveCodexExecutable, } from "./driver.js";
|
|
5
5
|
import { localFuelOnlyDriver, onlineDriverIds, resolveDriverFuel } from "./drivers.js";
|
|
6
|
-
export const BRIDGE_PROTOCOL_VERSION =
|
|
6
|
+
export const BRIDGE_PROTOCOL_VERSION = 2;
|
|
7
7
|
const execFileAsync = promisify(execFile);
|
|
8
8
|
async function defaultCommandRunner(command, args, cwd) {
|
|
9
9
|
try {
|
|
@@ -70,22 +70,30 @@ export async function runBridgePreflight(input, deps = {}) {
|
|
|
70
70
|
&& normalizeRepositoryUrl(brief.repository) !== normalizeRepositoryUrl(input.expectedRepository)) {
|
|
71
71
|
issues.push({ code: "workspace_repository_mismatch" });
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
let workspaceClean = false;
|
|
74
|
+
if (brief) {
|
|
75
|
+
const status = await run("git", ["status", "--porcelain", "--untracked-files=normal"], input.workspace);
|
|
76
|
+
workspaceClean = status.code === 0 && status.stdout.trim().length === 0;
|
|
77
|
+
if (!workspaceClean)
|
|
78
|
+
issues.push({ code: status.code === 0 ? "workspace_dirty" : "workspace_unavailable" });
|
|
79
|
+
}
|
|
80
|
+
const driverChecks = await Promise.all(online.map(async (driver) => {
|
|
74
81
|
const fuel = resolveDriverFuel(input.config, driver);
|
|
75
82
|
if (localFuelOnlyDriver(driver) && fuel !== "local") {
|
|
76
|
-
return { code: "driver_fuel_mismatch", driver };
|
|
83
|
+
return { issue: { code: "driver_fuel_mismatch", driver }, snapshot: { id: driver, version: null, ready: false } };
|
|
77
84
|
}
|
|
78
85
|
const version = await run(executableFor(driver), ["--version"], input.workspace);
|
|
86
|
+
const versionText = `${version.stdout}${version.stderr}`.trim().slice(0, 200) || null;
|
|
79
87
|
if (version.code !== 0 || !`${version.stdout}${version.stderr}`.trim()) {
|
|
80
|
-
return { code: "driver_missing", driver };
|
|
88
|
+
return { issue: { code: "driver_missing", driver }, snapshot: { id: driver, version: versionText, ready: false } };
|
|
81
89
|
}
|
|
82
90
|
if (fuel === "local" && !await localAuthenticationReady(driver, input.workspace, run)) {
|
|
83
|
-
return { code: "driver_not_authenticated", driver };
|
|
91
|
+
return { issue: { code: "driver_not_authenticated", driver }, snapshot: { id: driver, version: versionText, ready: false } };
|
|
84
92
|
}
|
|
85
|
-
return null;
|
|
93
|
+
return { issue: null, snapshot: { id: driver, version: versionText, ready: true } };
|
|
86
94
|
}));
|
|
87
|
-
issues.push(...
|
|
88
|
-
return { ready: issues.length === 0, checked_at: new Date().toISOString(), issues };
|
|
95
|
+
issues.push(...driverChecks.map((check) => check.issue).filter((issue) => issue !== null));
|
|
96
|
+
return { ready: issues.length === 0, checked_at: new Date().toISOString(), workspace_clean: workspaceClean, drivers: driverChecks.map((check) => check.snapshot), issues };
|
|
89
97
|
}
|
|
90
98
|
let cached = null;
|
|
91
99
|
/** Keep auth probes off the 15-second heartbeat hot path while still expiring readiness promptly. */
|
|
@@ -108,6 +116,8 @@ export function describePreflightIssue(issue) {
|
|
|
108
116
|
return "Workspace has no origin repository";
|
|
109
117
|
if (issue.code === "workspace_repository_mismatch")
|
|
110
118
|
return "Workspace origin does not match CONDUIT_REPO";
|
|
119
|
+
if (issue.code === "workspace_dirty")
|
|
120
|
+
return "Workspace has uncommitted or untracked changes";
|
|
111
121
|
if (issue.code === "driver_missing")
|
|
112
122
|
return `Agent CLI is missing from PATH${lane}`;
|
|
113
123
|
if (issue.code === "driver_not_authenticated")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.8",
|
|
4
4
|
"description": "Conduit Bridge CLI — join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Kiro / Antigravity agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|