@sagentlab/navarch-runtime 0.1.41 → 0.1.43
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 +14 -8
- package/dist/adapters/opencode.cjs +2 -3
- package/dist/cli.cjs +15 -1
- package/dist/config.cjs +32 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -326,7 +326,7 @@ unchanged across the deployment.
|
|
|
326
326
|
| `NAVARCH_ACP_BIN` | `dsh` | Path/name of an Agent Client Protocol v1 stdio server. DeepSeek Harness is the default implementation. |
|
|
327
327
|
| `NAVARCH_ACP_EXTRA_ARGS` | `--profile,acp` | Comma list of arguments used to start the ACP server. Override this together with `NAVARCH_ACP_BIN` for another ACP-compatible coding agent. |
|
|
328
328
|
| `NAVARCH_MCP_CONFIG_PATH` | — | Path to the platform MCP config passed as `--mcp-config`. |
|
|
329
|
-
| `NAVARCH_WORKTREE_GUARD` | on | Host-mode sessions get an adapter-native per-session worktree boundary guard (see below). Set `off` to disable. |
|
|
329
|
+
| `NAVARCH_WORKTREE_GUARD` | on | Host-mode sessions get an adapter-native per-session worktree boundary guard (see below). Set `off` to disable it for every runtime on the machine — required to run OpenCode on the host. |
|
|
330
330
|
| `NAVARCH_GUARD_EXTRA_ROOTS` | — | `path.delimiter`-separated (`:` on POSIX) extra directories the worktree guard allows beyond the session worktree, shared bare repo, and temp dirs. |
|
|
331
331
|
|
|
332
332
|
## Worktree boundary guard (host mode)
|
|
@@ -378,10 +378,13 @@ as an explicit read-only mount.
|
|
|
378
378
|
Docker-mode sessions disable Gemini's implicit YOLO sandbox to avoid nesting
|
|
379
379
|
it inside Navarch's already isolated session container.
|
|
380
380
|
- **OpenCode:** the CLI cannot currently express a host-side boundary for all
|
|
381
|
-
shell side effects
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
381
|
+
shell side effects, so with the guard enabled it is resolved out of the
|
|
382
|
+
machine's advertised runtimes at startup — and a machine that offers only
|
|
383
|
+
OpenCode there refuses to start rather than claim tasks it can only fail.
|
|
384
|
+
Run OpenCode in Docker with an operator-owned image that contains an
|
|
385
|
+
authenticated `opencode` binary, or set `NAVARCH_WORKTREE_GUARD=off` only
|
|
386
|
+
when the whole machine is already isolated (the opt-out is machine-wide:
|
|
387
|
+
every runtime on it loses the boundary).
|
|
385
388
|
OpenCode receives a private, per-session config, project config discovery is
|
|
386
389
|
disabled, and lease MCP headers are referenced through child-only environment
|
|
387
390
|
variables instead of being copied into its config file or argv.
|
|
@@ -445,15 +448,18 @@ export NAVARCH_AGENT=codex
|
|
|
445
448
|
export NAVARCH_AGENT=gemini
|
|
446
449
|
|
|
447
450
|
# OpenCode — requires an authenticated `opencode` CLI (or
|
|
448
|
-
# NAVARCH_OPENCODE_BIN pointing at it).
|
|
449
|
-
# use a Docker image containing OpenCode for the normal
|
|
451
|
+
# NAVARCH_OPENCODE_BIN pointing at it). A guarded host refuses to start with
|
|
452
|
+
# only this runtime; use a Docker image containing OpenCode for the normal
|
|
453
|
+
# isolated path, or NAVARCH_WORKTREE_GUARD=off on an isolated machine.
|
|
450
454
|
export NAVARCH_AGENT=opencode
|
|
451
455
|
|
|
452
456
|
# Agent Client Protocol — defaults to DeepSeek Harness `dsh --profile acp`.
|
|
453
457
|
# Override NAVARCH_ACP_BIN / NAVARCH_ACP_EXTRA_ARGS for another ACP v1 server.
|
|
454
458
|
export NAVARCH_AGENT=acp
|
|
455
459
|
|
|
456
|
-
# Advanced compatibility mode: advertise every installed adapter.
|
|
460
|
+
# Advanced compatibility mode: advertise every installed adapter. On a guarded
|
|
461
|
+
# host, `opencode` is dropped from this list (see the boundary notes above);
|
|
462
|
+
# the rest are advertised as written.
|
|
457
463
|
export NAVARCH_RUNTIMES=claude-code,codex,gemini,opencode,acp
|
|
458
464
|
```
|
|
459
465
|
|
|
@@ -4,6 +4,7 @@ exports.openCodeAdapter = void 0;
|
|
|
4
4
|
exports.runOpenCodeAdapter = runOpenCodeAdapter;
|
|
5
5
|
const node_child_process_1 = require("node:child_process");
|
|
6
6
|
const node_fs_1 = require("node:fs");
|
|
7
|
+
const config_cjs_1 = require("../config.cjs");
|
|
7
8
|
/**
|
|
8
9
|
* Headless OpenCode adapter, fixture-pinned to the OpenCode 1.18 JSON/config
|
|
9
10
|
* contract:
|
|
@@ -24,9 +25,7 @@ async function runOpenCodeAdapter(options) {
|
|
|
24
25
|
timedOut: false,
|
|
25
26
|
killedByLeaseLoss: false,
|
|
26
27
|
stdout: "",
|
|
27
|
-
stderr:
|
|
28
|
-
"Use NAVARCH_SANDBOX_MODE=docker, or explicitly set NAVARCH_WORKTREE_GUARD=off " +
|
|
29
|
-
"only on an otherwise isolated machine.",
|
|
28
|
+
stderr: config_cjs_1.OPENCODE_HOST_GUARD_MESSAGE,
|
|
30
29
|
};
|
|
31
30
|
}
|
|
32
31
|
const env = { ...options.env };
|
package/dist/cli.cjs
CHANGED
|
@@ -274,7 +274,19 @@ async function superviseCommand(flags) {
|
|
|
274
274
|
process.exitCode = exitCode;
|
|
275
275
|
}
|
|
276
276
|
async function doctorCommand(flags) {
|
|
277
|
-
|
|
277
|
+
// doctor is the command an operator reaches for precisely when the config
|
|
278
|
+
// will not load — an OpenCode-only machine on a guarded host refuses to
|
|
279
|
+
// start (config.cts). Report that as the diagnosis instead of rethrowing it
|
|
280
|
+
// as a bare CLI error.
|
|
281
|
+
let config;
|
|
282
|
+
try {
|
|
283
|
+
config = configFromFlags(flags);
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
console.log(`config: UNUSABLE — ${err instanceof Error ? err.message : String(err)}`);
|
|
287
|
+
process.exitCode = 1;
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
278
290
|
const dockerOk = await (0, sandbox_cjs_1.isDockerAvailable)();
|
|
279
291
|
console.log(`api_base: ${config.apiBase}`);
|
|
280
292
|
console.log(`config_dir: ${config.configDir}`);
|
|
@@ -282,6 +294,8 @@ async function doctorCommand(flags) {
|
|
|
282
294
|
console.log(`max_sessions: ${config.maxSessions}`);
|
|
283
295
|
console.log(`capabilities: ${config.capabilities.join(", ")}`);
|
|
284
296
|
console.log(`sandbox_mode: ${config.sandboxMode}`);
|
|
297
|
+
console.log(`worktree_guard: ${config.worktreeGuard ? "on" : "off"}`);
|
|
298
|
+
console.log(`runtimes: ${config.runtimes?.join(", ") ?? config.agentType}`);
|
|
285
299
|
console.log(`docker: ${dockerOk ? "available" : "NOT AVAILABLE (docker-backed sessions will fail)"}`);
|
|
286
300
|
let identity;
|
|
287
301
|
try {
|
package/dist/config.cjs
CHANGED
|
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_SANDBOX_IMAGE = void 0;
|
|
6
|
+
exports.OPENCODE_HOST_GUARD_MESSAGE = exports.DEFAULT_SANDBOX_IMAGE = void 0;
|
|
7
7
|
exports.isRuntimeAgentType = isRuntimeAgentType;
|
|
8
|
+
exports.opencodeBlockedOnHost = opencodeBlockedOnHost;
|
|
8
9
|
exports.loadRuntimeConfig = loadRuntimeConfig;
|
|
9
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
11
|
const node_os_1 = __importDefault(require("node:os"));
|
|
@@ -44,6 +45,18 @@ function envList(env, name, fallback) {
|
|
|
44
45
|
.map((s) => s.trim())
|
|
45
46
|
.filter(Boolean);
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* OpenCode is the one runtime Navarch cannot fence into a per-session worktree
|
|
50
|
+
* on the host: its CLI has no equivalent of Claude's PreToolUse hook or
|
|
51
|
+
* Codex's native permission profile. Docker mode supplies the boundary
|
|
52
|
+
* instead, and NAVARCH_WORKTREE_GUARD=off is the explicit opt-out.
|
|
53
|
+
*/
|
|
54
|
+
function opencodeBlockedOnHost(sandboxMode, worktreeGuard) {
|
|
55
|
+
return sandboxMode === "host" && worktreeGuard;
|
|
56
|
+
}
|
|
57
|
+
exports.OPENCODE_HOST_GUARD_MESSAGE = "OpenCode cannot enforce Navarch's host worktree boundary for shell side effects. " +
|
|
58
|
+
"Use NAVARCH_SANDBOX_MODE=docker, or explicitly set NAVARCH_WORKTREE_GUARD=off " +
|
|
59
|
+
"only on an otherwise isolated machine.";
|
|
47
60
|
/**
|
|
48
61
|
* Loads runtime config from NAVARCH_* env vars, with sane defaults for a
|
|
49
62
|
* fresh machine. Accepts an explicit env map (defaulting to process.env) so
|
|
@@ -57,8 +70,23 @@ function loadRuntimeConfig(env = process.env) {
|
|
|
57
70
|
// prerequisite for claiming ordinary shell work.
|
|
58
71
|
const sandboxMode = env.NAVARCH_SANDBOX_MODE === "docker" ? "docker" : "host";
|
|
59
72
|
const defaultCapabilities = sandboxMode === "docker" ? ["docker-sandbox", "shell"] : ["shell", "browser-use"];
|
|
60
|
-
const
|
|
61
|
-
|
|
73
|
+
const requestedAgentType = isRuntimeAgentType(env.NAVARCH_AGENT) ? env.NAVARCH_AGENT : "claude-code";
|
|
74
|
+
// Multiple sessions share one machine; keeping each agent inside its own
|
|
75
|
+
// worktree is the safe default, so disabling is the explicit opt-out.
|
|
76
|
+
const worktreeGuard = !["off", "false", "0"].includes(env.NAVARCH_WORKTREE_GUARD ?? "");
|
|
77
|
+
const requestedRuntimes = envList(env, "NAVARCH_RUNTIMES", [requestedAgentType]).filter(isRuntimeAgentType);
|
|
78
|
+
// OpenCode's CLI cannot express the host worktree boundary, so its adapter
|
|
79
|
+
// fails closed there (adapters/opencode.cts). Advertising it anyway meant
|
|
80
|
+
// the machine claimed OpenCode tasks it could only fail, one lease at a
|
|
81
|
+
// time; keep it out of the claim instead of discovering this per session.
|
|
82
|
+
const runnableRuntimes = requestedRuntimes.filter((runtime) => runtime !== "opencode" || !opencodeBlockedOnHost(sandboxMode, worktreeGuard));
|
|
83
|
+
if (requestedRuntimes.includes("opencode") && runnableRuntimes.length === 0) {
|
|
84
|
+
throw new Error(exports.OPENCODE_HOST_GUARD_MESSAGE);
|
|
85
|
+
}
|
|
86
|
+
const agentType = runnableRuntimes.includes(requestedAgentType) || runnableRuntimes.length === 0
|
|
87
|
+
? requestedAgentType
|
|
88
|
+
: (runnableRuntimes[0] ?? requestedAgentType);
|
|
89
|
+
const runtimes = runnableRuntimes;
|
|
62
90
|
return {
|
|
63
91
|
apiBase: env.NAVARCH_API_BASE ?? "http://localhost:3000",
|
|
64
92
|
workspaceRoot: env.NAVARCH_WORKSPACE_ROOT ?? node_path_1.default.join(configDir, "sandboxes"),
|
|
@@ -98,9 +126,7 @@ function loadRuntimeConfig(env = process.env) {
|
|
|
98
126
|
: sandbox_profile_cjs_1.DEFAULT_SANDBOX_PROFILE_ID,
|
|
99
127
|
sandboxEgressNetwork: env.NAVARCH_SANDBOX_EGRESS_NETWORK?.trim() || null,
|
|
100
128
|
dockerImage: env.NAVARCH_DOCKER_IMAGE ?? exports.DEFAULT_SANDBOX_IMAGE,
|
|
101
|
-
|
|
102
|
-
// worktree is the safe default, so disabling is the explicit opt-out.
|
|
103
|
-
worktreeGuard: !["off", "false", "0"].includes(env.NAVARCH_WORKTREE_GUARD ?? ""),
|
|
129
|
+
worktreeGuard,
|
|
104
130
|
guardExtraRoots: (env.NAVARCH_GUARD_EXTRA_ROOTS ?? "")
|
|
105
131
|
.split(node_path_1.default.delimiter)
|
|
106
132
|
.map((s) => s.trim())
|
package/package.json
CHANGED