@sagentlab/navarch-runtime 0.1.84 → 0.1.86

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
@@ -677,21 +677,22 @@ not the account's subscription charge or long-context/fast-mode uplifts.
677
677
 
678
678
 
679
679
  The Codex CLI invocation was verified against `codex-cli 0.144.1` on
680
- 2026-07-18. The runtime uses `codex exec "<prompt>" --json` and translates
680
+ 2026-07-18. The runtime uses `codex exec - --json` and translates
681
681
  the existing per-session MCP JSON into one-off `-c mcp_servers.*` overrides.
682
682
  Machine and lease credentials are referenced through environment variables,
683
683
  not placed in argv. The JSONL parser accepts the verified top-level
684
684
  `item.completed` / `turn.completed` shape and retains the older `msg`
685
685
  envelope as a compatibility fallback.
686
686
 
687
- The Gemini adapter uses official headless `--prompt` and `--output-format
688
- stream-json` flags. It resets pre-tool assistant text, retains the final
687
+ The Gemini adapter runs headlessly with `--output-format stream-json` and the
688
+ prompt piped on stdin, which Gemini uses as the prompt when stdin is not a
689
+ TTY. It resets pre-tool assistant text, retains the final
689
690
  post-tool answer as the report, and reads aggregate `input_tokens` and
690
691
  `output_tokens` from the terminal `result` event. Gemini does not report USD
691
692
  cost, so an unknown value remains absent rather than becoming zero.
692
693
 
693
- The OpenCode adapter uses `opencode run "<prompt>" --format json`, closes
694
- stdin immediately, and normalizes `text` and `step_finish` JSONL events into
694
+ The OpenCode adapter uses `opencode run --format json` with the prompt on
695
+ stdin, and normalizes `text` and `step_finish` JSONL events into
695
696
  the shared report/token/cost boundary. It translates the lease MCP document
696
697
  to OpenCode's remote-server config with OAuth disabled and environment-backed
697
698
  headers. Missing accounting fields stay absent rather than becoming a
@@ -716,6 +717,12 @@ ships that server as `dsh --profile acp`. This is intentionally not the
716
717
  REST-based [Agent Communication Protocol](https://agentcommunicationprotocol.dev),
717
718
  which is a different protocol now maintained as part of A2A.
718
719
 
720
+ Every CLI adapter writes the prompt to the agent's stdin, never argv. Linux
721
+ rejects any single argv or environment string over 128 KiB
722
+ (`MAX_ARG_STRLEN`), so an argv prompt made large review sessions fail with
723
+ `spawn E2BIG` before the agent started. Docker-mode sessions forward the same
724
+ stdin through `docker exec -i`.
725
+
719
726
  ## Architecture
720
727
 
721
728
  ```
@@ -733,12 +740,12 @@ cli.cts
733
740
  into Docker when NAVARCH_SANDBOX_MODE=docker
734
741
  4. selectAdapter(config.agentType) (adapters/index.cts) picks one AgentAdapter
735
742
  (adapters/types.cts) by NAVARCH_AGENT, then .run(...):
736
- - claudeCodeAdapter (adapters/claude.cts) — `claude -p <prompt> --mcp-config <path>`
737
- - codexAdapter (adapters/codex.cts) — `codex exec <prompt> --json -c mcp_servers.*=...`
738
- - geminiAdapter (adapters/gemini.cts) — `gemini --prompt <prompt> --output-format stream-json`
739
- - openCodeAdapter (adapters/opencode.cts) — `opencode run <prompt> --format json`
743
+ - claudeCodeAdapter (adapters/claude.cts) — `claude -p --mcp-config <path> < prompt`
744
+ - codexAdapter (adapters/codex.cts) — `codex exec - --json -c mcp_servers.*=... < prompt`
745
+ - geminiAdapter (adapters/gemini.cts) — `gemini --output-format stream-json < prompt`
746
+ - openCodeAdapter (adapters/opencode.cts) — `opencode run --format json < prompt`
740
747
  - acpAdapter (adapters/acp.cts) — ACP v1 JSON-RPC/stdio; defaults to `dsh --profile acp`
741
- - qoderAdapter (adapters/qoder.cts) — `qoder -p <prompt> --output-format json`
748
+ - qoderAdapter (adapters/qoder.cts) — `qoder -p --output-format json < prompt`
742
749
  heartbeating the lease every NAVARCH_LEASE_HEARTBEAT_INTERVAL_MS throughout either;
743
750
  a failed heartbeat aborts the run (kills the process) and marks the outcome as lease-lost
744
751
  5. mapExitCondition (exit-conditions.cts) → redact.cts scrubs the transcript → upload.cts PUTs it
@@ -13,8 +13,10 @@ function hasFlag(args, flag) {
13
13
  }
14
14
  /**
15
15
  * Headless Claude Code adapter (project-plan.md §3.9 / implementation-plan.md
16
- * WP-07): `claude -p "<context bundle>" --mcp-config platform-mcp.json
17
- * --output-format json`. Returns raw stdout/stderr/exit signals (plus
16
+ * WP-07): `claude -p --mcp-config platform-mcp.json --output-format json`
17
+ * with the context bundle on stdin (`claude -p` reads its prompt from stdin
18
+ * when none is given; Linux rejects any single argv string over 128 KiB, so
19
+ * an argv prompt failed `spawn E2BIG` before Claude started). Returns raw stdout/stderr/exit signals (plus
18
20
  * best-effort tokensIn/tokensOut/costUsd parsed from the JSON result -- see
19
21
  * exit-conditions.cts#parseClaudeJsonResult) for exit-conditions.cts to map
20
22
  * onto a complete()/fail() call.
@@ -91,7 +93,7 @@ function addUsage(first, second) {
91
93
  return first === undefined && second === undefined ? undefined : (first ?? 0) + (second ?? 0);
92
94
  }
93
95
  async function runClaudeAttempt(options, automaticFallback) {
94
- const args = ["-p", options.prompt];
96
+ const args = ["-p"];
95
97
  const hasSettingSources = options.extraArgs.some((arg) => arg === "--setting-sources" || arg.startsWith("--setting-sources="));
96
98
  const hasExplicitPermissionMode = options.extraArgs.some((arg) => ["--permission-mode", "--permission-prompt-tool", "--dangerously-skip-permissions"].some((flag) => arg === flag || arg.startsWith(`${flag}=`)));
97
99
  // Runtime sessions must not inherit an operator's personal or project-local
@@ -180,11 +182,11 @@ async function runOnHost(options, args) {
180
182
  cwd: options.cwd,
181
183
  env: { ...process.env, ...options.env },
182
184
  });
183
- // `claude -p` checks stdin for an additional piped prompt. Node opens a
184
- // pipe by default, so close it immediately when the complete prompt was
185
- // supplied as an argument. Leaving it open makes Claude wait and emit a
186
- // "no stdin data received" warning that the runtime treats as failure.
187
- child.stdin?.end();
185
+ // `claude -p` reads the whole prompt from stdin and starts at EOF. A
186
+ // child that exits before reading surfaces through `close`; ignore the
187
+ // resulting EPIPE here.
188
+ child.stdin?.on("error", () => undefined);
189
+ child.stdin?.end(options.prompt);
188
190
  options.onProcessStarted?.(child.pid);
189
191
  const timer = options.timeoutMs > 0 ? setTimeout(() => {
190
192
  timedOut = true;
@@ -234,7 +236,9 @@ async function runViaDocker(options, args) {
234
236
  if (options.signal?.aborted)
235
237
  onAbort();
236
238
  try {
237
- const result = await runner.run("docker", ["exec", containerName, "sh", "-c", command], {
239
+ // `-i` forwards the prompt to `claude -p` on stdin (see runOnHost).
240
+ const result = await runner.run("docker", ["exec", "-i", containerName, "sh", "-c", command], {
241
+ input: options.prompt,
238
242
  timeoutMs: options.timeoutMs,
239
243
  onActivity: options.onActivity,
240
244
  signal: options.signal,
@@ -19,12 +19,15 @@ const kill_agent_cjs_1 = require("../kill-agent.cjs");
19
19
  *
20
20
  * Verified against codex-cli 0.144.1 on 2026-07-18:
21
21
  *
22
- * codex exec "<prompt>" --json [-c mcp_servers.<name>.<key>=<value>] [...extraArgs]
22
+ * codex exec - --json [-c mcp_servers.<name>.<key>=<value>] [...extraArgs] < prompt
23
23
  *
24
24
  * - `exec <prompt>` — ASSUMED to be Codex CLI's non-interactive/headless
25
25
  * subcommand (the `codex exec` "automation mode" analog of `claude -p`):
26
26
  * runs the prompt to completion without the interactive TUI and exits,
27
- * printing its result to stdout.
27
+ * printing its result to stdout. The prompt is `-` and arrives on stdin
28
+ * (through `docker exec -i` in sandbox mode): Linux rejects any single argv string over 128 KiB
29
+ * (MAX_ARG_STRLEN), so a large review prompt failed `spawn E2BIG` before
30
+ * Codex started (tobi-agent-5571, 2026-10-06).
28
31
  * - `--json` emits top-level `thread.started`, `turn.started`,
29
32
  * `item.completed`, and `turn.completed` JSONL events. See
30
33
  * exit-conditions.cts for the tolerant parser.
@@ -45,7 +48,7 @@ const kill_agent_cjs_1 = require("../kill-agent.cjs");
45
48
  * approval-bypass flag) without an adapter code change.
46
49
  */
47
50
  async function runCodexAdapter(options) {
48
- const args = ["exec", options.prompt];
51
+ const args = ["exec", "-"];
49
52
  const env = { ...options.env };
50
53
  if (options.mcpConfigPath)
51
54
  args.push(...(await codexMcpArgs(options.mcpConfigPath, env)));
@@ -173,10 +176,11 @@ async function runOnHost(options, args) {
173
176
  cwd: options.cwd,
174
177
  env: { ...process.env, ...options.env },
175
178
  });
176
- // `codex exec` appends piped stdin to the prompt. The spawned process gets
177
- // a pipe by default, so close it immediately or it can wait forever for
178
- // input even though the full prompt was supplied as an argument.
179
- child.stdin?.end();
179
+ // `codex exec -` reads the whole prompt from stdin and starts at EOF, so
180
+ // end the pipe after writing it. A child that exits before reading
181
+ // surfaces through `close`; ignore the resulting EPIPE here.
182
+ child.stdin?.on("error", () => undefined);
183
+ child.stdin?.end(options.prompt);
180
184
  options.onProcessStarted?.(child.pid);
181
185
  const timer = options.timeoutMs > 0 ? setTimeout(() => {
182
186
  timedOut = true;
@@ -229,7 +233,8 @@ async function runViaDocker(options, args) {
229
233
  // variable names in argv. This keeps generated MCP bearer/header values
230
234
  // out of `ps` while making the host and Docker paths equivalent.
231
235
  const forwardedEnv = Object.keys(options.env).flatMap((name) => ["--env", name]);
232
- const result = await runner.run("docker", ["exec", ...forwardedEnv, containerName, "sh", "-c", command], {
236
+ const result = await runner.run("docker", ["exec", "-i", ...forwardedEnv, containerName, "sh", "-c", command], {
237
+ input: options.prompt,
233
238
  timeoutMs: options.timeoutMs,
234
239
  onActivity: options.onActivity,
235
240
  signal: options.signal,
@@ -14,9 +14,13 @@ const kill_agent_cjs_1 = require("../kill-agent.cjs");
14
14
  * The CLI reads MCP servers from settings rather than accepting Claude's
15
15
  * `--mcp-config` flag. The adapter converts the lease-scoped config into a
16
16
  * temporary system-settings file and restricts the run to those server names.
17
+ *
18
+ * The prompt arrives on stdin rather than `--prompt`: Gemini runs headlessly
19
+ * whenever stdin is not a TTY and uses the piped text as the prompt, and
20
+ * Linux rejects any single argv string over 128 KiB (`spawn E2BIG`).
17
21
  */
18
22
  async function runGeminiAdapter(options) {
19
- const args = ["--prompt", options.prompt];
23
+ const args = [];
20
24
  const env = { ...options.env };
21
25
  if (!hasArg(options.extraArgs, "--output-format", "-o")) {
22
26
  args.push("--output-format", "stream-json");
@@ -157,7 +161,9 @@ async function runOnHost(options, args) {
157
161
  cwd: options.cwd,
158
162
  env: { ...process.env, ...options.env },
159
163
  });
160
- child.stdin?.end();
164
+ // Ignore EPIPE from a child that exits before reading; `close` reports it.
165
+ child.stdin?.on("error", () => undefined);
166
+ child.stdin?.end(options.prompt);
161
167
  options.onProcessStarted?.(child.pid);
162
168
  const timer = options.timeoutMs > 0 ? setTimeout(() => {
163
169
  timedOut = true;
@@ -207,7 +213,8 @@ async function runViaDocker(options, args) {
207
213
  onAbort();
208
214
  try {
209
215
  const forwardedEnv = Object.keys(options.env).flatMap((name) => ["--env", name]);
210
- const result = await runner.run("docker", ["exec", ...forwardedEnv, containerName, "sh", "-c", command], { timeoutMs: options.timeoutMs,
216
+ const result = await runner.run("docker", ["exec", "-i", ...forwardedEnv, containerName, "sh", "-c", command], { input: options.prompt,
217
+ timeoutMs: options.timeoutMs,
211
218
  onActivity: options.onActivity,
212
219
  signal: options.signal,
213
220
  env: { ...process.env, ...options.env },
@@ -16,7 +16,12 @@ const kill_agent_cjs_1 = require("../kill-agent.cjs");
16
16
  * Headless OpenCode adapter, fixture-pinned to the OpenCode JSON/config
17
17
  * contract shared by V1 and V2 (verified against 2.0.6):
18
18
  *
19
- * opencode run "<prompt>" --format json [--model provider/model[#variant]]
19
+ * opencode run --format json [--model provider/model[#variant]] < prompt
20
+ *
21
+ * The prompt goes in on stdin, which `opencode run` reads as the message when
22
+ * none is given (verified against 2.0.17). Linux rejects any single argv
23
+ * string over 128 KiB (`spawn E2BIG`), and an argv prompt also let an
24
+ * agent's `pkill -f <word>` match the CLI itself (#1587).
20
25
  *
21
26
  * OpenCode merges configuration from several machine and project locations,
22
27
  * so each Navarch session gets an isolated XDG/config directory, disables
@@ -56,7 +61,7 @@ async function runOpenCodeAdapter(options) {
56
61
  const binaryKey = `${options.dockerExec ? "docker:" : ""}${options.bin ?? "opencode"}`;
57
62
  const defaultLogLevel = !hasArg(options.extraArgs, "--log-level");
58
63
  const logLevel = defaultLogLevel ? (openCodeLogLevels.get(binaryKey) ?? "ERROR") : null;
59
- const args = ["run", options.prompt];
64
+ const args = ["run"];
60
65
  if (/^\s*--standalone\b/m.test(help.stdout) && !hasArg(options.extraArgs, "--standalone")) {
61
66
  args.push("--standalone");
62
67
  }
@@ -76,7 +81,9 @@ async function runOpenCodeAdapter(options) {
76
81
  // use the model's own default.
77
82
  args.push("--model", options.reasoningEffort ? `${options.model}#${options.reasoningEffort}` : options.model);
78
83
  }
79
- const run = (runArgs) => runOptions.dockerExec ? runViaDocker(runOptions, runArgs) : runOnHost(runOptions, runArgs);
84
+ const run = (runArgs) => runOptions.dockerExec
85
+ ? runViaDocker(runOptions, runArgs, options.prompt)
86
+ : runOnHost(runOptions, runArgs, options.prompt);
80
87
  let raw = await run(args);
81
88
  if (logLevel && raw.exitCode !== 0 && isLogLevelRejection(raw.stderr)) {
82
89
  // The CLI rejected our default before starting any work, so one retry with
@@ -315,7 +322,8 @@ function capacityMonitor(onLimit) {
315
322
  },
316
323
  };
317
324
  }
318
- async function runOnHost(options, args) {
325
+ /** `input` is written to the child's stdin; the `run --help` probe sends none. */
326
+ async function runOnHost(options, args, input = "") {
319
327
  return new Promise((resolve) => {
320
328
  let stdout = "";
321
329
  let stderr = "";
@@ -331,7 +339,9 @@ async function runOnHost(options, args) {
331
339
  capacityError ??= detail;
332
340
  (0, kill_agent_cjs_1.killAgent)(child);
333
341
  });
334
- child.stdin?.end();
342
+ // Ignore EPIPE from a child that exits before reading; `close` reports it.
343
+ child.stdin?.on("error", () => undefined);
344
+ child.stdin?.end(input);
335
345
  options.onProcessStarted?.(child.pid);
336
346
  const timer = options.timeoutMs > 0 ? setTimeout(() => {
337
347
  timedOut = true;
@@ -369,7 +379,7 @@ async function runOnHost(options, args) {
369
379
  });
370
380
  });
371
381
  }
372
- async function runViaDocker(options, args) {
382
+ async function runViaDocker(options, args, input) {
373
383
  const { containerName, runner } = options.dockerExec;
374
384
  const quoted = [options.bin, ...args].map(shellQuote).join(" ");
375
385
  const command = `[ -f /tmp/session.env ] && . /tmp/session.env; cd repo 2>/dev/null; ${quoted}`;
@@ -394,7 +404,8 @@ async function runViaDocker(options, args) {
394
404
  onAbort();
395
405
  try {
396
406
  const forwardedEnv = Object.keys(options.env).flatMap((name) => ["--env", name]);
397
- const result = await runner.run("docker", ["exec", ...forwardedEnv, containerName, "sh", "-c", command], { timeoutMs: options.timeoutMs,
407
+ const result = await runner.run("docker", ["exec", ...(input === undefined ? [] : ["-i"]), ...forwardedEnv, containerName, "sh", "-c", command], { input,
408
+ timeoutMs: options.timeoutMs,
398
409
  onActivity: options.onActivity,
399
410
  signal: controller.signal,
400
411
  onStdout: (chunk) => { streamed.stdout = true; monitor.push("stdout", chunk); },
@@ -14,7 +14,8 @@ async function runLocalPiAdapter(options) {
14
14
  "--model", model.includes("/") ? model : `local/${model}`];
15
15
  if (options.reasoningEffort)
16
16
  args.push("--thinking", options.reasoningEffort === "max" ? "xhigh" : options.reasoningEffort);
17
- args.push("-p", options.prompt);
17
+ // The prompt goes in on stdin (pi.cts runOnHost), never argv.
18
+ args.push("-p");
18
19
  return (0, pi_cjs_1.attachPiOutput)(await (0, pi_cjs_1.runOnHost)(options, args, true));
19
20
  }
20
21
  exports.localPiAdapter = { agentType: "pi-local", run: runLocalPiAdapter };
@@ -19,9 +19,11 @@ const kill_agent_cjs_1 = require("../kill-agent.cjs");
19
19
  /**
20
20
  * Headless Pi coding-agent adapter (#885) — the process-side twin of the
21
21
  * Cloudflare hosted executor (worker/hosted-pi/lifecycle.ts). It invokes
22
- * `pi --mode json -p <prompt>` against DeepSeek V4 through a generated
22
+ * `pi --mode json -p < prompt` against DeepSeek V4 through a generated
23
23
  * OpenAI-compatible custom-provider config and normalizes the JSON event
24
- * stream into the common AdapterResult boundary.
24
+ * stream into the common AdapterResult boundary. `-p` is Pi's print flag and
25
+ * the prompt arrives on stdin, as in the hosted executor (#1587): Linux
26
+ * rejects any single argv string over 128 KiB (`spawn E2BIG`).
25
27
  *
26
28
  * Hosted Pi is control-plane provisioned (docs/navarch/runtime-adapter-contract.md
27
29
  * — "Hosted Pi is not a fifth AgentAdapter" for BYO workers): config.cts
@@ -254,7 +256,7 @@ async function runPiAdapter(options) {
254
256
  // than passing an invalid value that Pi warns about and ignores.
255
257
  args.push("--thinking", options.reasoningEffort === "max" ? "xhigh" : options.reasoningEffort);
256
258
  }
257
- args.push("-p", options.prompt);
259
+ args.push("-p");
258
260
  const raw = options.dockerExec
259
261
  ? await runViaDocker(options, args)
260
262
  : await runOnHost(options, args);
@@ -325,7 +327,9 @@ async function runOnHost(options, args, local = false) {
325
327
  cwd: options.cwd,
326
328
  env: { ...process.env, ...env },
327
329
  });
328
- child.stdin?.end();
330
+ // Ignore EPIPE from a child that exits before reading; `close` reports it.
331
+ child.stdin?.on("error", () => undefined);
332
+ child.stdin?.end(options.prompt);
329
333
  options.onProcessStarted?.(child.pid);
330
334
  const timer = options.timeoutMs > 0 ? setTimeout(() => {
331
335
  timedOut = true;
@@ -400,7 +404,8 @@ async function runViaDocker(options, args) {
400
404
  onAbort();
401
405
  try {
402
406
  const forwardedEnv = Object.keys(options.env).flatMap((name) => ["--env", name]);
403
- const result = await runner.run("docker", ["exec", ...forwardedEnv, containerName, "sh", "-c", command], {
407
+ const result = await runner.run("docker", ["exec", "-i", ...forwardedEnv, containerName, "sh", "-c", command], {
408
+ input: options.prompt,
404
409
  timeoutMs: options.timeoutMs,
405
410
  onActivity: options.onActivity,
406
411
  signal: options.signal,
@@ -9,7 +9,10 @@ function hasFlag(args, flag) {
9
9
  return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
10
10
  }
11
11
  /**
12
- * Headless Qoder CLI adapter: `qoder -p "<prompt>" --output-format json`.
12
+ * Headless Qoder CLI adapter: `qoder -p --output-format json < prompt`.
13
+ * Like `claude -p`, print mode reads the prompt from stdin when no query is
14
+ * given (verified against qodercli 1.1.64), which keeps large prompts clear
15
+ * of Linux's 128 KiB per-argv-string limit (`spawn E2BIG`).
13
16
  * Qoder's print mode emits the same single-JSON result shape as Claude Code
14
17
  * (verified against qodercli 1.1.56), so usage/cost parsing reuses
15
18
  * parseClaudeJsonResult and its result text lands in the ordinary
@@ -24,7 +27,7 @@ async function runQoderAdapter(options) {
24
27
  if (options.signal?.aborted) {
25
28
  return { exitCode: null, timedOut: false, killedByLeaseLoss: true, stdout: "", stderr: "" };
26
29
  }
27
- const args = ["-p", options.prompt];
30
+ const args = ["-p"];
28
31
  // Runtime sessions must not inherit an operator's personal or project-local
29
32
  // Qoder hooks, for the same reason as the Claude adapter: they make
30
33
  // execution machine-dependent and can fail otherwise-good sessions. The
@@ -81,9 +84,10 @@ async function runOnHost(options, args) {
81
84
  cwd: options.cwd,
82
85
  env: { ...process.env, ...options.env },
83
86
  });
84
- // `qoder -p` checks stdin for a piped prompt; close it so the child does
85
- // not wait when the complete prompt was supplied as an argument.
86
- child.stdin?.end();
87
+ // `qoder -p` reads the whole prompt from stdin and starts at EOF. A child
88
+ // that exits before reading surfaces through `close`; ignore the EPIPE.
89
+ child.stdin?.on("error", () => undefined);
90
+ child.stdin?.end(options.prompt);
87
91
  options.onProcessStarted?.(child.pid);
88
92
  const timer = options.timeoutMs > 0 ? setTimeout(() => {
89
93
  timedOut = true;
@@ -130,7 +134,9 @@ async function runViaDocker(options, args) {
130
134
  if (options.signal?.aborted)
131
135
  onAbort();
132
136
  try {
133
- const result = await runner.run("docker", ["exec", containerName, "sh", "-c", command], {
137
+ // `-i` forwards the prompt to `qoder -p` on stdin (see runOnHost).
138
+ const result = await runner.run("docker", ["exec", "-i", containerName, "sh", "-c", command], {
139
+ input: options.prompt,
134
140
  timeoutMs: options.timeoutMs,
135
141
  onActivity: options.onActivity,
136
142
  signal: options.signal,
package/dist/sandbox.cjs CHANGED
@@ -61,6 +61,9 @@ exports.nodeCommandRunner = {
61
61
  cleanup();
62
62
  resolve({ code: code ?? -1, stdout, stderr });
63
63
  });
64
+ // A child that exits before reading all input (e.g. `docker exec -i`
65
+ // carrying an agent prompt) raises EPIPE here; `close` reports the exit.
66
+ child.stdin?.on("error", () => undefined);
64
67
  if (opts.input !== undefined) {
65
68
  child.stdin?.write(opts.input);
66
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagentlab/navarch-runtime",
3
- "version": "0.1.84",
3
+ "version": "0.1.86",
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",