@sagentlab/navarch-runtime 0.1.66 → 0.1.68

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/api.cjs CHANGED
@@ -142,8 +142,8 @@ class NavarchApiClient {
142
142
  return result;
143
143
  }
144
144
  /** Best-effort, lease-scoped activity signal for connected machines. */
145
- async reportActivity(leaseId, phase) {
146
- await this.request("POST", `/api/dispatch/${encodeURIComponent(leaseId)}/activity`, { phase }, { allowEmpty: true });
145
+ async reportActivity(leaseId, phase, detail) {
146
+ await this.request("POST", `/api/dispatch/${encodeURIComponent(leaseId)}/activity`, { phase, ...(detail ? { detail } : {}) }, { allowEmpty: true });
147
147
  }
148
148
  /** schema-design.md §7 — `POST /api/dispatch/:leaseId/complete {status, report, evidence_urls, cost}`. */
149
149
  async completeLease(leaseId, req) {
package/dist/delivery.cjs CHANGED
@@ -110,6 +110,13 @@ function evaluateDelivery(probe) {
110
110
  }
111
111
  return { status: "no_changes" };
112
112
  }
113
+ // A failed GitHub lookup also means we cannot distinguish a genuinely
114
+ // unpushed commit from a successful push whose remote-tracking ref was not
115
+ // fetched after the agent turn. Do not fail and delete a delivered worktree
116
+ // based only on the cache in that state.
117
+ if (probe.pullRequestLookupFailed) {
118
+ return { status: "unknown", reason: "the pull request lookup could not run, so delivery cannot be confirmed" };
119
+ }
113
120
  if (probe.unpushedCommits > 0) {
114
121
  const plural = probe.unpushedCommits === 1 ? "commit" : "commits";
115
122
  return {
@@ -118,10 +125,7 @@ function evaluateDelivery(probe) {
118
125
  "were never pushed, so the work exists only in this session's worktree",
119
126
  };
120
127
  }
121
- // Pushed, but nothing carries it. Only gate when GitHub actually answered.
122
- if (probe.pullRequestLookupFailed) {
123
- return { status: "unknown", reason: "the pull request lookup could not run, so delivery cannot be confirmed" };
124
- }
128
+ // Pushed, but nothing carries it. GitHub answered the lookup above.
125
129
  const plural = probe.commitsAhead === 1 ? "commit" : "commits";
126
130
  return {
127
131
  status: "incomplete",
@@ -31,6 +31,7 @@ class GitWorktree {
31
31
  cloneUrl;
32
32
  githubToken;
33
33
  repoLocalGithubToken;
34
+ prContinuation;
34
35
  taskBranchSuffix;
35
36
  legacyTaskBranchSuffix;
36
37
  taskOwnershipTrailer;
@@ -57,11 +58,16 @@ class GitWorktree {
57
58
  this.legacyTaskBranchSuffix = `-${normalizedTaskId}`;
58
59
  this.taskOwnershipTrailer = `Navarch-Task-ID: ${normalizedTaskId}`;
59
60
  this.sessionTokenMarker = `navarch-session-github-token:${sessionKey}`;
60
- this.branch = `navarch/${branchSlug(options)}${this.taskBranchSuffix}`;
61
+ // A continuation delivers on the reviewed PR's existing branch. Its
62
+ // initial worktree branch is disposable and must not collide with a
63
+ // previous attempt's temporary branch in the bare cache.
64
+ this.branch = `navarch/${branchSlug(options)}${this.taskBranchSuffix}` +
65
+ (options.prContinuation ? `-${sessionKey}` : "");
61
66
  this.runner = options.runner ?? sandbox_cjs_1.nodeCommandRunner;
62
67
  this.cloneUrl = options.cloneUrl;
63
68
  this.githubToken = options.githubToken;
64
69
  this.repoLocalGithubToken = options.repoLocalGithubToken;
70
+ this.prContinuation = options.prContinuation ?? false;
65
71
  this.hostedCheckpointRef = options.restoreHostedCheckpoint ? hostedCheckpointRef(options.taskId) : null;
66
72
  this.hostedCheckpointLocalRef = `refs/navarch/restored-checkpoints/${taskKey}`;
67
73
  }
@@ -349,6 +355,12 @@ class GitWorktree {
349
355
  }
350
356
  /** Returns the fetched task-owned remote branch when an earlier attempt pushed it. */
351
357
  async resolveTaskBranchRef() {
358
+ // A follow-up must check out the reviewed PR's head, not adopt a branch
359
+ // bearing this task's short suffix. An earlier attempt may have pushed its
360
+ // temporary branch after dropping the claim commit; that branch must not
361
+ // prevent later attempts from reaching the actual PR.
362
+ if (this.prContinuation)
363
+ return null;
352
364
  const refs = await this.runGit(["--git-dir", this.repositoryPath, "for-each-ref", "--format=%(refname)", "refs/remotes/origin/navarch"], false);
353
365
  const suffixMatches = refs.stdout
354
366
  .split("\n")
package/dist/session.cjs CHANGED
@@ -199,6 +199,7 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
199
199
  taskId: task.id,
200
200
  taskSummary: task.summary,
201
201
  taskType: task.task_type,
202
+ prContinuation: allowBranchRename,
202
203
  sessionId,
203
204
  cloneUrl,
204
205
  githubToken,
@@ -400,16 +401,22 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
400
401
  let activityPending = Promise.resolve();
401
402
  let outputObservedAt = 0;
402
403
  let outputReportedAt = 0;
404
+ let outputUpdates = 0;
405
+ let agentTurnStartedAt = 0;
403
406
  let adapterRunning = false;
404
- const reportActivity = (phase) => {
405
- activityPending = activityPending.then(() => api.reportActivity(leaseId, phase)).catch((error) => {
407
+ const reportActivity = (phase, detail) => {
408
+ activityPending = activityPending.then(() => api.reportActivity(leaseId, phase, detail)).catch((error) => {
406
409
  log.warn(`activity update failed for ${leaseId}: ${(0, redact_cjs_1.redactText)(String(error), registry.list())}`);
407
410
  });
408
411
  };
409
412
  const activityTimer = setInterval(() => {
410
413
  if (adapterRunning && outputObservedAt > outputReportedAt && Date.now() - outputReportedAt >= 60_000) {
411
414
  outputReportedAt = Date.now();
412
- reportActivity("output");
415
+ reportActivity("output", {
416
+ outputUpdates,
417
+ elapsedSeconds: Math.floor((outputReportedAt - agentTurnStartedAt) / 1000),
418
+ });
419
+ outputUpdates = 0;
413
420
  }
414
421
  }, 15_000);
415
422
  const heartbeatGuard = new lease_heartbeat_cjs_1.LeaseHeartbeatGuard(claimed.expires_at, () => {
@@ -484,6 +491,9 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
484
491
  await node_fs_1.promises.writeFile(node_path_1.default.join(workDir, "prompt.md"), runPrompt, "utf8");
485
492
  adapterStarted = true;
486
493
  adapterRunning = true;
494
+ agentTurnStartedAt = Date.now();
495
+ outputReportedAt = agentTurnStartedAt;
496
+ outputUpdates = 0;
487
497
  reportActivity("running");
488
498
  const remainingRuntimeMs = maxRuntimeMs > 0
489
499
  ? maxRuntimeMs - (node_perf_hooks_1.performance.now() - agentBudgetStartedAt)
@@ -508,7 +518,7 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
508
518
  cwd: gitWorktree.worktreePath,
509
519
  dockerExec: sandbox ? { containerName: sandbox.name, runner: sandbox_cjs_1.nodeCommandRunner } : undefined,
510
520
  signal: activeAbortController.signal,
511
- onActivity: () => { outputObservedAt = Date.now(); },
521
+ onActivity: () => { outputObservedAt = Date.now(); outputUpdates += 1; },
512
522
  }, task.idle_timeout_ms ?? config.sessionIdleTimeoutMs ?? 45 * 60_000, runtime === "opencode" ? config.opencodeStartupTimeoutMs : 0);
513
523
  activeAbortController = null;
514
524
  adapterRunning = false;
@@ -622,7 +632,8 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
622
632
  deliveryFailureSummary =
623
633
  `Session ended without delivering the task: ${delivery.reason}. ` +
624
634
  `${deliveryCorrectionRetries} correction turn(s) did not recover it. ` +
625
- "The session worktree is deleted at teardown, so the work is gone; the task needs another attempt.";
635
+ "The session worktree is deleted at teardown. Pushed commits remain on origin; " +
636
+ "uncommitted or unpushed work may be lost. The task needs another attempt.";
626
637
  log.warn(`failing ${leaseId}: ${deliveryFailureSummary}`);
627
638
  mapping.leaseOutcome = "failed";
628
639
  mapping.exitStatus = "failed";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagentlab/navarch-runtime",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "description": "Navarch machine-side session manager: claims delivery tasks and runs them through local coding agents.",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",