@sagentlab/navarch-runtime 0.1.146 → 0.1.149

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.
@@ -11,6 +11,22 @@ exports.detectAgentModelUnavailable = detectAgentModelUnavailable;
11
11
  */
12
12
  const NO_ROUTE_PATTERN = /\bprovider\.no-route\b|\bModelUnavailableError\b|\bModel unavailable:/i;
13
13
  const MODEL_NAME_PATTERN = /\bModel unavailable:\s*([^\s"'\\,;)]+)/i;
14
+ const TRANSPORT_PATTERN = /\bprovider\.transport\b|\bConnectionRefused\b|\bECONNREFUSED\b|\bconnection refused\b/i;
15
+ /** A failed connection is a machine problem only before the session does work. */
16
+ function madeModelProgress(result) {
17
+ if (result.reportText?.trim() || (result.tokensIn ?? 0) > 0 ||
18
+ (result.tokensOut ?? 0) > 0 || (result.costUsd ?? 0) > 0)
19
+ return true;
20
+ return result.stdout.split(/\r?\n/).some((line) => {
21
+ try {
22
+ const event = JSON.parse(line);
23
+ return ["step_start", "step_finish", "text", "reasoning", "tool_use", "tool_result", "tool"].includes(event?.type);
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ });
29
+ }
14
30
  function clip(text) {
15
31
  const trimmed = text.trim().replace(/\s+/g, " ");
16
32
  return trimmed.length > 300 ? `${trimmed.slice(0, 299)}…` : trimmed;
@@ -50,7 +66,8 @@ function errorEventMessages(stdout) {
50
66
  }
51
67
  /**
52
68
  * Detects a turn the coding agent CLI could not start because the model it
53
- * was told to use has no route on this machine (OpenCode `provider.no-route`).
69
+ * was told to use has no route on this machine (OpenCode `provider.no-route`),
70
+ * or its provider connection fails before any model progress in this session.
54
71
  * Like a dead login (agent-auth.cts), every task the machine claims then fails
55
72
  * the same way within seconds, so the control plane requeues the task without
56
73
  * spending a retry and pauses the machine until the model is fixed.
@@ -59,19 +76,20 @@ function errorEventMessages(stdout) {
59
76
  * ERROR log lines — and only for a turn that failed or produced no report, so
60
77
  * an agent writing about this very error cannot pause its own machine.
61
78
  */
62
- function detectAgentModelUnavailable(result) {
79
+ function detectAgentModelUnavailable(result, previousAttempts = []) {
63
80
  if (result.killedByLeaseLoss || result.timedOut || result.capacityError || result.authError)
64
81
  return null;
65
82
  if (result.exitCode === 0 && result.reportText)
66
83
  return null;
84
+ const beforeProgress = !madeModelProgress(result) && !previousAttempts.some(madeModelProgress);
67
85
  for (const message of errorEventMessages(result.stdout)) {
68
- if (NO_ROUTE_PATTERN.test(message))
86
+ if (NO_ROUTE_PATTERN.test(message) || (beforeProgress && TRANSPORT_PATTERN.test(message)))
69
87
  return failure(message);
70
88
  }
71
89
  if (result.exitCode === 0)
72
90
  return null;
73
91
  for (const line of result.stderr.split(/\r?\n/)) {
74
- if (/ERROR/.test(line) && NO_ROUTE_PATTERN.test(line)) {
92
+ if (/ERROR/.test(line) && (NO_ROUTE_PATTERN.test(line) || (beforeProgress && TRANSPORT_PATTERN.test(line)))) {
75
93
  const cause = line.match(/Model unavailable:[^"\n]*/i)?.[0];
76
94
  return failure(cause ?? line);
77
95
  }
package/dist/cli.cjs CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.main = main;
7
7
  const config_cjs_1 = require("./config.cjs");
8
8
  const node_crypto_1 = require("node:crypto");
9
+ const model_reference_cjs_1 = require("./model-reference.cjs");
9
10
  const node_os_1 = __importDefault(require("node:os"));
10
11
  const node_path_1 = __importDefault(require("node:path"));
11
12
  const machine_store_cjs_1 = require("./machine-store.cjs");
@@ -126,10 +127,7 @@ function warnUnknownAdapters(config) {
126
127
  /** Saved pins only apply to the adapter they were selected for. */
127
128
  function modelFromFlags(flags, agentType, identity) {
128
129
  const model = flags.model ?? (identity?.agent_type === agentType ? identity.model : undefined);
129
- if (model !== undefined && (!model.trim() || model === "true" || /\s/.test(model))) {
130
- throw new Error("--model requires a non-empty model reference without whitespace.");
131
- }
132
- return model;
130
+ return model === undefined ? undefined : (0, model_reference_cjs_1.normalizeRuntimeModelReference)(model);
133
131
  }
134
132
  function parseArgs(argv) {
135
133
  const [command, ...rest] = argv;
@@ -8,15 +8,19 @@ exports.loadMachineIdentity = loadMachineIdentity;
8
8
  exports.resolveMachineIdentity = resolveMachineIdentity;
9
9
  const node_fs_1 = require("node:fs");
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
+ const model_reference_cjs_1 = require("./model-reference.cjs");
11
12
  const agent_types_cjs_1 = require("./agent-types.cjs");
12
13
  function storePath(configDir) {
13
14
  return node_path_1.default.join(configDir, "machine.json");
14
15
  }
15
16
  /** Persists the machine's long-lived auth token to `<configDir>/machine.json`, mode 0600. */
16
17
  async function saveMachineIdentity(configDir, identity) {
18
+ const normalized = identity.model === undefined
19
+ ? identity
20
+ : { ...identity, model: (0, model_reference_cjs_1.normalizeRuntimeModelReference)(identity.model) };
17
21
  await node_fs_1.promises.mkdir(configDir, { recursive: true, mode: 0o700 });
18
22
  const file = storePath(configDir);
19
- await node_fs_1.promises.writeFile(file, `${JSON.stringify(identity, null, 2)}\n`, { mode: 0o600 });
23
+ await node_fs_1.promises.writeFile(file, `${JSON.stringify(normalized, null, 2)}\n`, { mode: 0o600 });
20
24
  await node_fs_1.promises.chmod(file, 0o600);
21
25
  }
22
26
  async function loadMachineIdentity(configDir) {
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeModelReference = normalizeModelReference;
4
+ exports.normalizeRuntimeModelReference = normalizeRuntimeModelReference;
5
+ /** Mirrors normalizeModelReference in lib/navarch/execution-policy.ts for this standalone package. */
6
+ function normalizeModelReference(value) {
7
+ if (typeof value !== "string")
8
+ return null;
9
+ let model = value.trim();
10
+ const pairs = { '"': '"', "'": "'", "`": "`", "‘": "’", "“": "”" };
11
+ if (model.length >= 2 && pairs[model[0]] === model.at(-1)) {
12
+ model = model.slice(1, -1).trim();
13
+ }
14
+ if (!model || model.length > 500 || /[\u0000-\u001f\u007f'"`‘’“”]/.test(model))
15
+ return null;
16
+ return model;
17
+ }
18
+ /** Validate both new CLI pins and saved identities before use or persistence. */
19
+ function normalizeRuntimeModelReference(value) {
20
+ const model = normalizeModelReference(value);
21
+ if (!model || model === "true" || /\s/.test(model)) {
22
+ throw new Error("--model requires a non-empty model reference without whitespace or embedded quotes.");
23
+ }
24
+ return model;
25
+ }
package/dist/session.cjs CHANGED
@@ -1080,7 +1080,10 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
1080
1080
  }
1081
1081
  // A model the CLI has no route to (OpenCode `provider.no-route`) fails
1082
1082
  // every task the same way, so it gets the same treatment as a dead login.
1083
- const modelUnavailable = capacityLimit || authFailure ? null : (0, agent_model_cjs_1.detectAgentModelUnavailable)(turnResult);
1083
+ // Startup transport failures also qualify, unless this session already
1084
+ // made model progress in this or an earlier turn.
1085
+ const modelUnavailable = capacityLimit || authFailure
1086
+ ? null : (0, agent_model_cjs_1.detectAgentModelUnavailable)(turnResult, attempts.slice(0, -1));
1084
1087
  if (modelUnavailable) {
1085
1088
  turnResult.modelUnavailableError = modelUnavailable.detail;
1086
1089
  log.warn(`session ${leaseId}: coding agent model unavailable: ${modelUnavailable.detail}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagentlab/navarch-runtime",
3
- "version": "0.1.146",
3
+ "version": "0.1.149",
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",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -p tsconfig.json",
42
- "typecheck": "tsc -p tsconfig.json --noEmit",
42
+ "typecheck": "bun check -p tsconfig.json",
43
43
  "test": "vitest run",
44
44
  "test:watch": "vitest",
45
45
  "prepublishOnly": "npm run build && npm test",