@sagentlab/navarch-runtime 0.1.60 → 0.1.62
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 +11 -5
- package/dist/git-worktree.cjs +39 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,8 +8,9 @@ machine.
|
|
|
8
8
|
|
|
9
9
|
The machine operator selects **Claude Code**, **OpenAI Codex**, **Google
|
|
10
10
|
Gemini CLI**, **OpenCode**, or an **Agent Client Protocol (ACP)** agent when connecting a worker. Any selected agent can
|
|
11
|
-
run any project task; task eligibility depends on capabilities and
|
|
12
|
-
gates
|
|
11
|
+
run any unpinned project task; task eligibility depends on capabilities and
|
|
12
|
+
project gates. A task pinned to a runtime waits for a worker that advertises
|
|
13
|
+
that adapter and runs on it — see "Choosing an agent" below.
|
|
13
14
|
|
|
14
15
|
See [`docs/agent-platform-project-plan.md`](../docs/agent-platform-project-plan.md)
|
|
15
16
|
§3.8/§3.9/§3.11 and [`docs/navarch/implementation-plan.md`](../docs/navarch/implementation-plan.md)
|
|
@@ -369,7 +370,7 @@ unchanged across the deployment.
|
|
|
369
370
|
| `NAVARCH_SANDBOX_EGRESS_NETWORK` | _(unset)_ | Docker network that enforces a profile's egress allowlist. Docker cannot filter by domain itself, so an allowlist profile without this fails closed to `--network=none` and records the denial. |
|
|
370
371
|
| `NAVARCH_DOCKER_IMAGE` | `ghcr.io/sagentlab/navarch-sandbox-agent:0.1.0` | Version-pinned per-session image with Node 20, git, GitHub CLI, ripgrep, jq, SSH, and Claude Code 2.1.218. Override with an image tag or digest you control. |
|
|
371
372
|
| `NAVARCH_AGENT` | saved choice, then `claude-code` | Local choice of agent adapter: `claude-code`, `codex`, `gemini`, `opencode`, `acp`, or `qoder`. Overrides the choice saved by `connect`/`register`; `start --agent` has highest priority. |
|
|
372
|
-
| `NAVARCH_RUNTIMES` | resolved local agent choice | Comma list of installed/authenticated adapters advertised to dispatch.
|
|
373
|
+
| `NAVARCH_RUNTIMES` | resolved local agent choice | Comma list of installed/authenticated adapters advertised to dispatch. Unpinned tasks run on the preferred agent; a task runtime pin selects the matching adapter. |
|
|
373
374
|
| `NAVARCH_UPDATE_CHANNEL` | `stable` | Release channel advertised by the worker (`stable` or `canary`); the server-managed machine channel remains authoritative. |
|
|
374
375
|
| `NAVARCH_AUTO_UPDATE` | on under `supervise` | Set `off`, `false`, or `0` to report releases without staging or activating them. Automatic activation is always off under plain `start`. |
|
|
375
376
|
| `NAVARCH_CLAUDE_BIN` | `claude` | Path/name of the Claude Code CLI binary. |
|
|
@@ -530,8 +531,13 @@ export NAVARCH_RUNTIMES=claude-code,codex,gemini,opencode,acp,qoder
|
|
|
530
531
|
For the legacy single-runtime setting, priority is `start --agent` →
|
|
531
532
|
`NAVARCH_AGENT` → the locally saved choice → `claude-code`.
|
|
532
533
|
`NAVARCH_RUNTIMES` expands what the worker advertises. For normal projects,
|
|
533
|
-
the locally selected `NAVARCH_AGENT` handles every
|
|
534
|
-
|
|
534
|
+
the locally selected `NAVARCH_AGENT` handles every unpinned task. A task with a
|
|
535
|
+
runtime override (for example `gemini`) is claimed only by a worker that
|
|
536
|
+
advertises that adapter, and the claim returns it as the session runtime.
|
|
537
|
+
Sandbox projects remain constrained to Claude Code, so they claim only unpinned
|
|
538
|
+
or `claude-code`-pinned tasks. List only adapters this worker can really run:
|
|
539
|
+
in Docker mode that means CLIs installed in `NAVARCH_DOCKER_IMAGE`, otherwise
|
|
540
|
+
pinned tasks for a missing adapter are claimed and then fail.
|
|
535
541
|
|
|
536
542
|
All five BYO adapters implement the same `AgentAdapter` interface
|
|
537
543
|
(`src/adapters/types.cts`) and run either directly on the host or via
|
package/dist/git-worktree.cjs
CHANGED
|
@@ -37,6 +37,8 @@ class GitWorktree {
|
|
|
37
37
|
sessionTokenMarker;
|
|
38
38
|
hostedCheckpointRef;
|
|
39
39
|
hostedCheckpointLocalRef;
|
|
40
|
+
/** Set when the resumed remote branch had lost its ownership commit and must be re-marked. */
|
|
41
|
+
adoptedUnmarkedBranch = false;
|
|
40
42
|
constructor(options) {
|
|
41
43
|
const projectKey = safePathSegment(options.projectId);
|
|
42
44
|
const sessionKey = safePathSegment(options.sessionId);
|
|
@@ -185,7 +187,9 @@ class GitWorktree {
|
|
|
185
187
|
this.worktreePath,
|
|
186
188
|
startRef,
|
|
187
189
|
], false);
|
|
188
|
-
|
|
190
|
+
// An adopted branch gets its marker back, or the pre-push guard refuses
|
|
191
|
+
// every delivery push and the next retry is stuck again.
|
|
192
|
+
if ((!taskRef && !staleTip) || this.adoptedUnmarkedBranch)
|
|
189
193
|
await this.createTaskOwnershipCommit();
|
|
190
194
|
if (checkpoint)
|
|
191
195
|
await this.applyHostedCheckpoint(checkpoint);
|
|
@@ -293,6 +297,7 @@ class GitWorktree {
|
|
|
293
297
|
if (tip.code !== 0 || !/^[0-9a-f]{40,64}$/i.test(staleSha))
|
|
294
298
|
return null;
|
|
295
299
|
const owned = this.branch.endsWith(this.legacyTaskBranchSuffix) ||
|
|
300
|
+
this.adoptedUnmarkedBranch ||
|
|
296
301
|
(await this.hasTaskOwnershipMarker(localRef));
|
|
297
302
|
if (!owned) {
|
|
298
303
|
throw new Error(`Local branch ${this.branch} already exists in the cached repository but does not ` +
|
|
@@ -360,6 +365,9 @@ class GitWorktree {
|
|
|
360
365
|
const matches = [...ownedShortMatches, ...legacyMatches];
|
|
361
366
|
if (matches.length === 0) {
|
|
362
367
|
if (shortMatches.length > 0) {
|
|
368
|
+
const adopted = await this.adoptUnmarkedOwnBranch(shortMatches);
|
|
369
|
+
if (adopted)
|
|
370
|
+
return adopted;
|
|
363
371
|
throw new Error(`Remote branch ${shortMatches[0]} shares task suffix ${this.taskBranchSuffix} but does not ` +
|
|
364
372
|
`contain ownership marker ${this.taskOwnershipTrailer}; refusing to resume or fork.`);
|
|
365
373
|
}
|
|
@@ -372,6 +380,36 @@ class GitWorktree {
|
|
|
372
380
|
this.branch = remoteRef.replace(/^refs\/remotes\/origin\//, "");
|
|
373
381
|
return remoteRef;
|
|
374
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* An agent that amends, resets, or rebases away the empty claim commit and
|
|
385
|
+
* pushes leaves this task's branch without its marker, and every retry then
|
|
386
|
+
* refused it (carova #10). The branch is still ours when it carries exactly
|
|
387
|
+
* the name this task allocates (slug and suffix) and none of its own commits
|
|
388
|
+
* claim another task; anything else stays refused.
|
|
389
|
+
*/
|
|
390
|
+
async adoptUnmarkedOwnBranch(shortMatches) {
|
|
391
|
+
const ownRef = `refs/remotes/origin/${this.branch}`;
|
|
392
|
+
if (shortMatches.length !== 1 || shortMatches[0] !== ownRef)
|
|
393
|
+
return null;
|
|
394
|
+
const base = await this.resolveStartRef().catch(() => null);
|
|
395
|
+
if (!base)
|
|
396
|
+
return null;
|
|
397
|
+
const log = await this.runner.run("git", [
|
|
398
|
+
"--git-dir",
|
|
399
|
+
this.repositoryPath,
|
|
400
|
+
"log",
|
|
401
|
+
"--format=%B",
|
|
402
|
+
ownRef,
|
|
403
|
+
"--not",
|
|
404
|
+
base,
|
|
405
|
+
]);
|
|
406
|
+
if (log.code !== 0)
|
|
407
|
+
return null;
|
|
408
|
+
if (log.stdout.split("\n").some((line) => /^Navarch-Task-ID:/i.test(line.trim())))
|
|
409
|
+
return null;
|
|
410
|
+
this.adoptedUnmarkedBranch = true;
|
|
411
|
+
return ownRef;
|
|
412
|
+
}
|
|
375
413
|
/** Short branch names are ambiguous without a full task-ID marker in their history. */
|
|
376
414
|
async hasTaskOwnershipMarker(ref) {
|
|
377
415
|
const log = await this.runGit([
|
package/package.json
CHANGED