@sagentlab/navarch-runtime 0.1.120 → 0.1.122

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.
Files changed (28) hide show
  1. package/README.md +27 -5
  2. package/dist/adapters/claude.cjs +77 -6
  3. package/dist/adapters/codex.cjs +66 -2
  4. package/dist/adapters/opencode-providers.cjs +110 -0
  5. package/dist/adapters/opencode.cjs +9 -2
  6. package/dist/delivery.cjs +16 -2
  7. package/dist/prompt.cjs +44 -7
  8. package/dist/session.cjs +131 -53
  9. package/node_modules/jsonc-parser/CHANGELOG.md +76 -0
  10. package/node_modules/jsonc-parser/LICENSE.md +21 -0
  11. package/node_modules/jsonc-parser/README.md +364 -0
  12. package/node_modules/jsonc-parser/SECURITY.md +41 -0
  13. package/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
  14. package/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
  15. package/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
  16. package/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
  17. package/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
  18. package/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
  19. package/node_modules/jsonc-parser/lib/esm/main.js +178 -0
  20. package/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
  21. package/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
  22. package/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
  23. package/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
  24. package/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
  25. package/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
  26. package/node_modules/jsonc-parser/lib/umd/main.js +194 -0
  27. package/node_modules/jsonc-parser/package.json +37 -0
  28. package/package.json +5 -3
package/dist/session.cjs CHANGED
@@ -529,7 +529,22 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
529
529
  });
530
530
  const agentBudgetStartedAt = node_perf_hooks_1.performance.now();
531
531
  const maxRuntimeMs = task.max_runtime_ms ?? config.sessionTimeoutMs;
532
- let nextPrompt = null;
532
+ // A correction turn in two forms: `full` restates the task brief for a
533
+ // fresh agent; `resumed` is only the correction, for an adapter that
534
+ // continues the previous turn's conversation (AgentAdapter.resumesTurns).
535
+ let nextCorrection = null;
536
+ // The conversation the next turn may resume, and the mid-flight guidance
537
+ // that conversation has already been sent.
538
+ let resumableSessionId;
539
+ let conversationGuidanceIds = new Set();
540
+ // Running usage totals the resumable conversation last reported, for the
541
+ // fields its adapter reports cumulatively (AgentAdapter.cumulativeResumeUsage).
542
+ let conversationUsage = {};
543
+ // Claim-time guidance is part of the brief (task_context's "Human
544
+ // guidance" section, lib/navarch/context.ts), so guidance turns carry only
545
+ // what arrived after the claim.
546
+ const briefGuidanceIds = new Set((bundle.guidance ?? []).map((entry) => entry.id));
547
+ const resumedTask = { id: task.id, summary: task.summary };
533
548
  let completionRemediationRetries = 0;
534
549
  let deliveryCorrectionRetries = 0;
535
550
  let deliveredPullRequest = false;
@@ -540,58 +555,85 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
540
555
  // It is already included in deliveredGuidance, so clear the pending
541
556
  // notification and start the first turn with the corrected prompt.
542
557
  pendingGuidance = [];
543
- activeAbortController = new AbortController();
558
+ const turnAbort = new AbortController();
559
+ activeAbortController = turnAbort;
544
560
  if (leaseLost || deps.shutdownSignal?.aborted)
545
- activeAbortController.abort();
561
+ turnAbort.abort();
546
562
  // The task brief plus any mid-flight guidance. Correction turns build on
547
563
  // this rather than on the previous turn's prompt so they never nest.
548
- const briefPrompt = deliveredGuidance.length > (bundle.guidance?.length ?? 0)
549
- ? (0, prompt_cjs_1.renderGuidanceCorrectionPrompt)(promptText, deliveredGuidance)
564
+ const midFlightGuidance = deliveredGuidance.filter((entry) => !briefGuidanceIds.has(entry.id));
565
+ const briefPrompt = midFlightGuidance.length > 0
566
+ ? (0, prompt_cjs_1.renderGuidanceCorrectionPrompt)(promptText, midFlightGuidance)
550
567
  : promptText;
551
- const runPrompt = nextPrompt ?? briefPrompt;
552
- nextPrompt = null;
553
- await node_fs_1.promises.writeFile(node_path_1.default.join(workDir, "prompt.md"), runPrompt, "utf8");
568
+ const fullPrompt = nextCorrection ? nextCorrection.full(briefPrompt) : briefPrompt;
569
+ // Resume only to deliver something new: a correction, or guidance this
570
+ // conversation has not been sent. The brief is already in it.
571
+ const unsentGuidance = midFlightGuidance.filter((entry) => !conversationGuidanceIds.has(entry.id));
572
+ const resumeSessionId = adapter.resumesTurns && (nextCorrection || unsentGuidance.length > 0)
573
+ ? resumableSessionId
574
+ : undefined;
575
+ const resumedPrompt = resumeSessionId
576
+ ? nextCorrection
577
+ ? [nextCorrection.resumed, ...(unsentGuidance.length > 0 ? [(0, prompt_cjs_1.renderMidFlightGuidanceSection)(unsentGuidance)] : [])].join("\n\n")
578
+ : (0, prompt_cjs_1.renderResumedGuidancePrompt)(resumedTask, unsentGuidance)
579
+ : null;
580
+ nextCorrection = null;
554
581
  adapterStarted = true;
555
582
  adapterRunning = true;
556
583
  agentTurnStartedAt = Date.now();
557
584
  outputReportedAt = agentTurnStartedAt;
558
585
  outputUpdates = 0;
559
586
  reportActivity("running");
560
- const remainingRuntimeMs = maxRuntimeMs > 0
561
- ? maxRuntimeMs - (node_perf_hooks_1.performance.now() - agentBudgetStartedAt)
562
- : 0;
563
- const turnResult = maxRuntimeMs > 0 && remainingRuntimeMs <= 0
564
- ? { exitCode: null, timedOut: true, timeoutReason: "duration", killedByLeaseLoss: false, stdout: "", stderr: "" }
565
- : await (0, adapter_watchdog_cjs_1.runWithActivityWatchdog)(adapter, {
566
- prompt: runPrompt,
567
- mcpConfigPath,
568
- mcpMetadataDir: workDir,
569
- bin,
570
- extraArgs,
571
- model: execution.model,
572
- reasoningEffort: execution.reasoning_effort,
573
- timeoutMs: remainingRuntimeMs,
574
- env: sessionEnv,
575
- settingsPath: claudeSettingsPath,
576
- codexGuardArgs,
577
- antigravityHostGuard: runtime === "antigravity" && config.sandboxMode === "host" && config.worktreeGuard,
578
- antigravityResume,
579
- museHostGuard: config.sandboxMode === "host" && config.worktreeGuard,
580
- resumeSessionId: runtime === "muse" ? museSessionId : undefined,
581
- opencodeHostGuard: runtime === "opencode" && config.sandboxMode === "host" && config.worktreeGuard,
582
- // ACP requires an absolute session cwd. Docker mounts the worktree at
583
- // this same absolute path, while the other Docker adapters ignore cwd.
584
- cwd: gitWorktree.worktreePath,
585
- dockerExec: sandbox ? { containerName: sandbox.name, runner: sandbox_cjs_1.nodeCommandRunner } : undefined,
586
- signal: activeAbortController.signal,
587
- onActivity: () => { outputObservedAt = Date.now(); outputUpdates += 1; },
588
- onOutput: (stream, chunk) => {
589
- if (stream !== "stdout")
590
- return;
591
- liveStdout += chunk;
592
- liveTranscript?.markDirty();
593
- },
594
- }, task.idle_timeout_ms ?? config.sessionIdleTimeoutMs ?? 45 * 60_000, runtime === "opencode" ? config.opencodeStartupTimeoutMs : 0);
587
+ const runTurn = async (prompt, resumeId) => {
588
+ // prompt.md records exactly what this invocation is sent.
589
+ await node_fs_1.promises.writeFile(node_path_1.default.join(workDir, "prompt.md"), prompt, "utf8");
590
+ const remainingRuntimeMs = maxRuntimeMs > 0
591
+ ? maxRuntimeMs - (node_perf_hooks_1.performance.now() - agentBudgetStartedAt)
592
+ : 0;
593
+ return maxRuntimeMs > 0 && remainingRuntimeMs <= 0
594
+ ? { exitCode: null, timedOut: true, timeoutReason: "duration", killedByLeaseLoss: false, stdout: "", stderr: "" }
595
+ : await (0, adapter_watchdog_cjs_1.runWithActivityWatchdog)(adapter, {
596
+ prompt,
597
+ mcpConfigPath,
598
+ mcpMetadataDir: workDir,
599
+ bin,
600
+ extraArgs,
601
+ model: execution.model,
602
+ reasoningEffort: execution.reasoning_effort,
603
+ timeoutMs: remainingRuntimeMs,
604
+ env: sessionEnv,
605
+ settingsPath: claudeSettingsPath,
606
+ codexGuardArgs,
607
+ antigravityHostGuard: runtime === "antigravity" && config.sandboxMode === "host" && config.worktreeGuard,
608
+ antigravityResume,
609
+ museHostGuard: config.sandboxMode === "host" && config.worktreeGuard,
610
+ resumeSessionId: runtime === "muse" ? museSessionId : resumeId,
611
+ opencodeHostGuard: runtime === "opencode" && config.sandboxMode === "host" && config.worktreeGuard,
612
+ // ACP requires an absolute session cwd. Docker mounts the worktree at
613
+ // this same absolute path, while the other Docker adapters ignore cwd.
614
+ cwd: gitWorktree.worktreePath,
615
+ dockerExec: sandbox ? { containerName: sandbox.name, runner: sandbox_cjs_1.nodeCommandRunner } : undefined,
616
+ signal: turnAbort.signal,
617
+ onActivity: () => { outputObservedAt = Date.now(); outputUpdates += 1; },
618
+ onOutput: (stream, chunk) => {
619
+ if (stream !== "stdout")
620
+ return;
621
+ liveStdout += chunk;
622
+ liveTranscript?.markDirty();
623
+ },
624
+ }, task.idle_timeout_ms ?? config.sessionIdleTimeoutMs ?? 45 * 60_000, runtime === "opencode" ? config.opencodeStartupTimeoutMs : 0);
625
+ };
626
+ let turnResult = await runTurn(resumedPrompt ?? fullPrompt, resumeSessionId);
627
+ let resumed = Boolean(resumeSessionId);
628
+ if (resumeSessionId && turnResult.resumeRejected && !leaseLost && !turnAbort.signal.aborted) {
629
+ // The CLI refused the session before any work started (expired or
630
+ // unknown id). Nothing ran, so send the full brief to a fresh agent.
631
+ log.warn(`could not resume the agent conversation for ${leaseId} ` +
632
+ `(${(0, redact_cjs_1.redactText)(turnResult.stderr.trim().slice(0, 300), registry.list())}); re-sending the full task brief.`);
633
+ liveStdout = "";
634
+ turnResult = await runTurn(fullPrompt, undefined);
635
+ resumed = false;
636
+ }
595
637
  activeAbortController = null;
596
638
  adapterRunning = false;
597
639
  if (turnResult.timedOut || leaseLost)
@@ -603,6 +645,34 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
603
645
  antigravityResume = turnResult.antigravityResume;
604
646
  if (runtime === "muse")
605
647
  museSessionId = turnResult.agentSessionId;
648
+ if (adapter.cumulativeResumeUsage) {
649
+ // A resumed run reports the conversation's running total: record only
650
+ // this turn's increase. A fresh conversation starts its own total.
651
+ if (!resumed)
652
+ conversationUsage = {};
653
+ for (const key of adapter.cumulativeResumeUsage) {
654
+ const total = turnResult[key];
655
+ if (total === undefined)
656
+ continue;
657
+ turnResult[key] = Math.max(0, total - (conversationUsage[key] ?? 0));
658
+ conversationUsage[key] = total;
659
+ }
660
+ }
661
+ if (adapter.resumesTurns) {
662
+ resumableSessionId = turnResult.resumeRejected ? undefined : turnResult.agentSessionId;
663
+ if (!resumableSessionId) {
664
+ conversationGuidanceIds = new Set();
665
+ }
666
+ else if (resumed) {
667
+ for (const entry of unsentGuidance)
668
+ conversationGuidanceIds.add(entry.id);
669
+ }
670
+ else {
671
+ // A fresh conversation was sent the full brief, which carries every
672
+ // mid-flight guidance entry received so far.
673
+ conversationGuidanceIds = new Set(midFlightGuidance.map((entry) => entry.id));
674
+ }
675
+ }
606
676
  attempts.push(turnResult);
607
677
  liveStdout = "";
608
678
  liveTranscript.markDirty();
@@ -735,14 +805,18 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
735
805
  deliveryCorrectionRetries += 1;
736
806
  log.warn(`session ${leaseId} finished without delivering (${delivery.reason}); ` +
737
807
  `restarting agent turn ${deliveryCorrectionRetries}/${DELIVERY_CORRECTION_RETRIES} in the same worktree.`);
738
- nextPrompt = (0, delivery_cjs_1.renderDeliveryCorrectionPrompt)({
739
- originalPrompt: briefPrompt,
808
+ const correction = {
740
809
  taskType: task.task_type,
741
- previousReport: previousTurnReport(),
742
810
  reason: delivery.reason,
743
811
  attempt: deliveryCorrectionRetries,
744
812
  maxAttempts: DELIVERY_CORRECTION_RETRIES,
745
- });
813
+ };
814
+ const previousReport = previousTurnReport();
815
+ nextCorrection = {
816
+ full: (brief) => (0, delivery_cjs_1.renderDeliveryCorrectionPrompt)({ ...correction, originalPrompt: brief, previousReport }),
817
+ // The resumed conversation already holds the previous report.
818
+ resumed: (0, delivery_cjs_1.renderResumedDeliveryCorrectionPrompt)({ ...correction, task: resumedTask }),
819
+ };
746
820
  continue;
747
821
  }
748
822
  await checkpointForRecovery(result, "incomplete delivery");
@@ -831,15 +905,19 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
831
905
  if (remediable && completionRemediationRetries < COMPLETION_REMEDIATION_RETRIES) {
832
906
  completionRemediationRetries += 1;
833
907
  log.warn(`completion for ${leaseId} needs more work (${rejectionDetail}); restarting agent turn ${completionRemediationRetries}/${COMPLETION_REMEDIATION_RETRIES} in the same worktree.`);
834
- // A new turn is a fresh agent invocation: the rejection alone would
835
- // reach an agent that has never seen the task.
836
- nextPrompt = (0, prompt_cjs_1.renderCompletionRemediationPrompt)({
837
- originalPrompt: briefPrompt,
908
+ // A fresh agent invocation needs the brief: the rejection alone would
909
+ // reach an agent that has never seen the task (#1978). A resumed
910
+ // conversation already holds the brief and the previous report.
911
+ const correction = {
838
912
  rejection: redactedRejection,
839
- previousReport: previousTurnReport(),
840
913
  attempt: completionRemediationRetries,
841
914
  maxAttempts: COMPLETION_REMEDIATION_RETRIES,
842
- });
915
+ };
916
+ const previousReport = previousTurnReport();
917
+ nextCorrection = {
918
+ full: (brief) => (0, prompt_cjs_1.renderCompletionRemediationPrompt)({ ...correction, originalPrompt: brief, previousReport }),
919
+ resumed: (0, prompt_cjs_1.renderResumedCompletionRemediationPrompt)({ ...correction, task: resumedTask }),
920
+ };
843
921
  continue;
844
922
  }
845
923
  if (remediable) {
@@ -0,0 +1,76 @@
1
+ 3.3.0 2022-06-24
2
+ =================
3
+ - `JSONVisitor.onObjectBegin` and `JSONVisitor.onArrayBegin` can now return `false` to instruct the visitor that no children should be visited.
4
+
5
+
6
+ 3.2.0 2022-08-30
7
+ =================
8
+ - update the version of the bundled Javascript files to `es2020`.
9
+ - include all `const enum` values in the bundled JavaScript files (`ScanError`, `SyntaxKind`, `ParseErrorCode`).
10
+
11
+ 3.1.0 2022-07-07
12
+ ==================
13
+ * added new API `FormattingOptions.keepLines` : It leaves the initial line positions in the formatting.
14
+
15
+ 3.0.0 2020-11-13
16
+ ==================
17
+ * fixed API spec for `parseTree`. Can return `undefine` for empty input.
18
+ * added new API `FormattingOptions.insertFinalNewline`.
19
+
20
+
21
+ 2.3.0 2020-07-03
22
+ ==================
23
+ * new API `ModificationOptions.isArrayInsertion`: If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then `modify` will insert a new item at that location instead of overwriting its contents.
24
+ * `ModificationOptions.formattingOptions` is now optional. If not set, newly inserted content will not be formatted.
25
+
26
+
27
+ 2.2.0 2019-10-25
28
+ ==================
29
+ * added `ParseOptions.allowEmptyContent`. Default is `false`.
30
+ * new API `getNodeType`: Returns the type of a value returned by parse.
31
+ * `parse`: Fix issue with empty property name
32
+
33
+ 2.1.0 2019-03-29
34
+ ==================
35
+ * `JSONScanner` and `JSONVisitor` return lineNumber / character.
36
+
37
+ 2.0.0 2018-04-12
38
+ ==================
39
+ * renamed `Node.columnOffset` to `Node.colonOffset`
40
+ * new API `getNodePath`: Gets the JSON path of the given JSON DOM node
41
+ * new API `findNodeAtOffset`: Finds the most inner node at the given offset. If `includeRightBound` is set, also finds nodes that end at the given offset.
42
+
43
+ 1.0.3 2018-03-07
44
+ ==================
45
+ * provide ems modules
46
+
47
+ 1.0.2 2018-03-05
48
+ ==================
49
+ * added the `visit.onComment` API, reported when comments are allowed.
50
+ * added the `ParseErrorCode.InvalidCommentToken` enum value, reported when comments are disallowed.
51
+
52
+ 1.0.1
53
+ ==================
54
+ * added the `format` API: computes edits to format a JSON document.
55
+ * added the `modify` API: computes edits to insert, remove or replace a property or value in a JSON document.
56
+ * added the `allyEdits` API: applies edits to a document
57
+
58
+ 1.0.0
59
+ ==================
60
+ * remove nls dependency (remove `getParseErrorMessage`)
61
+
62
+ 0.4.2 / 2017-05-05
63
+ ==================
64
+ * added `ParseError.offset` & `ParseError.length`
65
+
66
+ 0.4.1 / 2017-04-02
67
+ ==================
68
+ * added `ParseOptions.allowTrailingComma`
69
+
70
+ 0.4.0 / 2017-02-23
71
+ ==================
72
+ * fix for `getLocation`. Now `getLocation` inside an object will always return a property from inside that property. Can be empty string if the object has no properties or if the offset is before a actual property `{ "a": { | }} will return location ['a', ' ']`
73
+
74
+ 0.3.0 / 2017-01-17
75
+ ==================
76
+ * Updating to typescript 2.0
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Microsoft
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.