@sagentlab/navarch-runtime 0.1.49 → 0.1.50

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 CHANGED
@@ -837,3 +837,13 @@ Each invocation rebuilds from Git blobs at the requested commit, avoiding stale
837
837
  persistent indexes. Bounds are 2,000 files, 10 MB total source, 500 KB per file,
838
838
  100 returned nodes and 300 edges. A later persistent index can optimize large
839
839
  repositories without changing the query format.
840
+
841
+ ### Pinning a connection model
842
+
843
+ `connect --agent codex --model gpt-6-astra` saves the model alongside the local
844
+ machine identity. `start` and `supervise` reuse that model, including after worker
845
+ restarts. Their optional `--model` flag overrides the saved choice. The pin wins
846
+ over the project's session model for the selected adapter only; switching agent
847
+ types does not reuse another adapter's pin. Reconnect without `--model` to return
848
+ to project-controlled model selection. Older identities without a model keep
849
+ using the control-plane execution policy.
package/dist/cli.cjs CHANGED
@@ -76,6 +76,14 @@ function agentFromFlag(flags) {
76
76
  }
77
77
  return value;
78
78
  }
79
+ /** Saved pins only apply to the adapter they were selected for. */
80
+ function modelFromFlags(flags, agentType, identity) {
81
+ const model = flags.model ?? (identity?.agent_type === agentType ? identity.model : undefined);
82
+ if (model !== undefined && (!model.trim() || model === "true" || /\s/.test(model))) {
83
+ throw new Error("--model requires a non-empty model reference without whitespace.");
84
+ }
85
+ return model;
86
+ }
79
87
  function parseArgs(argv) {
80
88
  const [command, ...rest] = argv;
81
89
  const flags = {};
@@ -118,6 +126,7 @@ async function registerCommand(flags) {
118
126
  .filter(Boolean);
119
127
  const ownerZone = flags["owner-zone"] ?? config.ownerZone;
120
128
  const agentType = agentFromFlag(flags) ?? config.agentType;
129
+ modelFromFlags(flags, agentType); // Validate before consuming an enrollment token.
121
130
  const client = new api_cjs_1.NavarchApiClient({ baseUrl: apiBase });
122
131
  const result = await client.registerMachine({
123
132
  enrollment_token: enrollmentToken,
@@ -132,6 +141,7 @@ async function registerCommand(flags) {
132
141
  name,
133
142
  api_base: apiBase,
134
143
  agent_type: agentType,
144
+ model: modelFromFlags(flags, agentType),
135
145
  });
136
146
  // Printed exactly once. Never logged or echoed again after this point.
137
147
  console.log("Machine registered.");
@@ -166,6 +176,7 @@ async function connectCommand(flags) {
166
176
  .map((s) => s.trim())
167
177
  .filter(Boolean);
168
178
  const agentType = agentFromFlag(flags) ?? config.agentType;
179
+ modelFromFlags(flags, agentType); // Validate before consuming an enrollment token.
169
180
  const client = new api_cjs_1.NavarchApiClient({ baseUrl: apiBase });
170
181
  const result = await client.connectMachine({
171
182
  enrollment_token: enrollmentToken,
@@ -180,6 +191,7 @@ async function connectCommand(flags) {
180
191
  name,
181
192
  api_base: apiBase,
182
193
  agent_type: agentType,
194
+ model: modelFromFlags(flags, agentType),
183
195
  });
184
196
  // Printed exactly once. Never logged or echoed again after this point.
185
197
  console.log("Machine connected.");
@@ -195,7 +207,7 @@ async function startCommand(flags) {
195
207
  // time, and finally the backwards-compatible Claude Code default.
196
208
  const agentType = agentFromFlag(flags) ??
197
209
  (process.env.NAVARCH_AGENT ? baseConfig.agentType : identity.agent_type ?? baseConfig.agentType);
198
- const config = { ...baseConfig, agentType };
210
+ const config = { ...baseConfig, agentType, model: modelFromFlags(flags, agentType, identity) };
199
211
  const api = new api_cjs_1.NavarchApiClient({ baseUrl: identity.api_base, token: identity.token });
200
212
  const capacity = new capacity_cjs_1.CapacityTracker(config.maxSessions);
201
213
  const activeSessionIds = new Set();
@@ -277,6 +289,9 @@ async function superviseCommand(flags) {
277
289
  const agentType = agentFromFlag(flags) ??
278
290
  (process.env.NAVARCH_AGENT ? config.agentType : identity.agent_type ?? config.agentType);
279
291
  const workerArgs = ["--agent", agentType];
292
+ const model = modelFromFlags(flags, agentType, identity);
293
+ if (model)
294
+ workerArgs.push("--model", model);
280
295
  // Pin the identity for this supervisor's lifetime. Without this snapshot, a
281
296
  // replacement worker rereads machine.json after an automatic update and can
282
297
  // silently become a different machine if another terminal reused the same
@@ -332,11 +347,11 @@ function helpText() {
332
347
 
333
348
  Usage:
334
349
  navarch-runtime register --token <enrollment-token> --name <machine-name> \\
335
- [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp] [--capabilities a,b] [--max-sessions N] [--owner-zone z] [--api-base url]
350
+ [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp] [--model <model>] [--capabilities a,b] [--max-sessions N] [--owner-zone z] [--api-base url]
336
351
  navarch-runtime connect --token <enrollment-token> --name <machine-name> \\
337
- [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp] [--project <project-id>] [--capabilities a,b] [--max-sessions N] [--api-base url]
338
- navarch-runtime start [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp]
339
- navarch-runtime supervise [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp]
352
+ [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp] [--model <model>] [--project <project-id>] [--capabilities a,b] [--max-sessions N] [--api-base url]
353
+ navarch-runtime start [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp] [--model <model>]
354
+ navarch-runtime supervise [--config-dir <path>] [--agent claude-code|codex|gemini|opencode|acp] [--model <model>]
340
355
  navarch-runtime doctor [--config-dir <path>]
341
356
  navarch-runtime code-graph --query <symbol-or-file> [--mode search|callers|callees|impact] [--commit HEAD] [--depth 1..5] [--repo <path>]
342
357
 
package/dist/session.cjs CHANGED
@@ -38,6 +38,21 @@ const COMPLETION_REMEDIATION_RETRIES = 2;
38
38
  */
39
39
  const DELIVERY_CORRECTION_RETRIES = 1;
40
40
  const log = (0, logger_cjs_1.createLogger)("session");
41
+ function resolveSessionExecution(config, claimed) {
42
+ const runtime = claimed.runtime ?? config.agentType;
43
+ const execution = claimed.context_bundle.execution ?? {
44
+ profile: claimed.task.execution_profile ?? "standard",
45
+ model: runtime === "codex" ? "gpt-6-astra"
46
+ : runtime === "gemini" ? "auto"
47
+ : runtime === "opencode" || runtime === "acp" ? "default"
48
+ : "claude-opus-5",
49
+ reasoning_effort: "medium",
50
+ };
51
+ return {
52
+ ...execution,
53
+ ...(runtime === config.agentType && config.model ? { model: config.model } : {}),
54
+ };
55
+ }
41
56
  /**
42
57
  * Runs one claimed task end to end (implementation-plan.md WP-07):
43
58
  * 1. write the prompt file
@@ -70,19 +85,9 @@ async function runSession(deps, claimed, sessionId) {
70
85
  // explicit pre-start signal. Otherwise it waits for expiry and bypasses
71
86
  // the repeat/backoff policy entirely.
72
87
  const { api, config } = deps;
73
- const { lease_id: leaseId, task, context_bundle: bundle } = claimed;
88
+ const { lease_id: leaseId, task } = claimed;
74
89
  const runtime = claimed.runtime ?? config.agentType;
75
- const execution = bundle.execution ?? {
76
- profile: task.execution_profile ?? "standard",
77
- model: runtime === "codex"
78
- ? "gpt-6-astra"
79
- : runtime === "gemini"
80
- ? "auto"
81
- : runtime === "opencode" || runtime === "acp"
82
- ? "default"
83
- : "claude-opus-5",
84
- reasoning_effort: "medium",
85
- };
90
+ const execution = resolveSessionExecution(config, claimed);
86
91
  const crashDetail = describeCompletionError(err).slice(0, 1800);
87
92
  const failureSummary = `Session crashed: ${crashDetail}`;
88
93
  log.error(`session ${leaseId} threw before adapter start: ${crashDetail}`);
@@ -110,17 +115,7 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
110
115
  const { api, config } = deps;
111
116
  const { lease_id: leaseId, task, context_bundle: bundle } = claimed;
112
117
  const runtime = claimed.runtime ?? config.agentType;
113
- const execution = bundle.execution ?? {
114
- profile: task.execution_profile ?? "standard",
115
- model: runtime === "codex"
116
- ? "gpt-6-astra"
117
- : runtime === "gemini"
118
- ? "auto"
119
- : runtime === "opencode" || runtime === "acp"
120
- ? "default"
121
- : "claude-opus-5",
122
- reasoning_effort: "medium",
123
- };
118
+ const execution = resolveSessionExecution(config, claimed);
124
119
  // Isolation posture reported on every completion so `sessions` records what
125
120
  // a run actually executed under (#660). Derived from config alone, so the
126
121
  // pre-start failure paths below can stamp it too. Host mode records
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagentlab/navarch-runtime",
3
- "version": "0.1.49",
3
+ "version": "0.1.50",
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",