@nathapp/nax 0.18.5 → 0.18.6

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/.gitlab-ci.yml CHANGED
@@ -17,7 +17,7 @@ test:
17
17
  stage: test
18
18
  image:
19
19
  name: nathapp/node-bun:22.21.0-1.3.9-alpine
20
- pull_policy: if-not-present
20
+ pull_policy: always
21
21
  before_script:
22
22
  - apk add --no-cache git
23
23
  - git config --global safe.directory '*'
@@ -47,7 +47,7 @@ release:
47
47
  stage: release
48
48
  image:
49
49
  name: nathapp/node-bun:22.21.0-1.3.9-alpine
50
- pull_policy: if-not-present
50
+ pull_policy: always
51
51
  cache:
52
52
  key:
53
53
  files:
@@ -86,7 +86,7 @@ notify:
86
86
  stage: notify
87
87
  image:
88
88
  name: registry-intl.cn-hongkong.aliyuncs.com/gkci/node:22.14.0-alpine-ci
89
- pull_policy: if-not-present
89
+ pull_policy: always
90
90
  needs: [release]
91
91
  script:
92
92
  - VERSION=$(node -e "console.log(require('./package.json').version)")
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.18.6] - 2026-03-04
9
+
10
+ ### Fixed
11
+ - **BUG-2:** Infinite PTY respawn loop in `usePty` hook by destructuring object-identity dependencies.
12
+ - **MEM-1 & MEM-3:** Prevented child process hangs on full `stderr` pipes by switching to `stderr: "inherit"`.
13
+ - **BUG-21 & BUG-22:** Added missing error handling and `.catch()` chains to process `stdout` streaming and exit handlers.
14
+
8
15
  ## [0.18.5] - 2026-03-04
9
16
 
10
17
  ### Changed
package/docs/ROADMAP.md CHANGED
@@ -134,7 +134,7 @@
134
134
 
135
135
  ---
136
136
 
137
- ## v0.19.0 — Verification Architecture v2
137
+ ## v0.19.0 — Hardening & Compliance
138
138
 
139
139
  **Theme:** Eliminate duplicate test runs, deferred regression gate, structured escalation context
140
140
  **Status:** 🔲 Planned
@@ -166,6 +166,7 @@
166
166
  | Version | Theme | Date | Details |
167
167
  |:---|:---|:---|:---|
168
168
  | v0.18.1 | Type Safety + CI Pipeline | 2026-03-03 | 60 TS errors + 12 lint errors fixed, GitLab CI green (1952/56/0) |
169
+ | v0.19.0 | Hardening & Compliance | TBD | SEC-1 to SEC-5, BUG-1, Node.js API removal, _deps rollout |
169
170
  | v0.18.5 | Bun PTY Migration | 2026-03-04 | BUN-001: node-pty → Bun.spawn, CI cleanup, flaky test fix |
170
171
  | v0.18.4 | Routing Stability | 2026-03-04 | BUG-031 keyword drift, BUG-033 LLM retry, pre-commit hook |
171
172
  | v0.18.3 | Execution Reliability + Smart Runner | 2026-03-04 | BUG-026/028/029/030/032 + SFC-001/002 + STR-007, all items complete |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.18.5",
3
+ "version": "0.18.6",
4
4
  "description": "AI Coding Agent Orchestrator \u2014 loops until done",
5
5
  "type": "module",
6
6
  "bin": {
@@ -172,7 +172,7 @@ export class ClaudeCodeAdapter implements AgentAdapter {
172
172
  const proc = Bun.spawn(cmd, {
173
173
  cwd: options.workdir,
174
174
  stdout: "pipe",
175
- stderr: "pipe",
175
+ stderr: "inherit", // MEM-3: Inherit stderr to avoid blocking on unread pipe
176
176
  env: this.buildAllowedEnv(options),
177
177
  });
178
178
 
@@ -255,7 +255,7 @@ export class ClaudeCodeAdapter implements AgentAdapter {
255
255
  const proc = Bun.spawn(cmd, {
256
256
  cwd: options.workdir,
257
257
  stdout: "pipe",
258
- stderr: "pipe",
258
+ stderr: "inherit", // MEM-3: Inherit stderr to avoid blocking on unread pipe
259
259
  env: this.buildAllowedEnv({
260
260
  workdir: options.workdir,
261
261
  modelDef: options.modelDef || { provider: "anthropic", model: "claude-sonnet-4-5", env: {} },
@@ -295,7 +295,7 @@ export class ClaudeCodeAdapter implements AgentAdapter {
295
295
  env: { ...this.buildAllowedEnv(options), TERM: "xterm-256color", FORCE_COLOR: "1" },
296
296
  stdin: "pipe",
297
297
  stdout: "pipe",
298
- stderr: "pipe",
298
+ stderr: "inherit", // MEM-3: Inherit stderr to avoid blocking on unread pipe
299
299
  });
300
300
 
301
301
  const pidRegistry = this.getPidRegistry(options.workdir);
@@ -303,16 +303,26 @@ export class ClaudeCodeAdapter implements AgentAdapter {
303
303
 
304
304
  // Stream stdout to onOutput callback
305
305
  (async () => {
306
- for await (const chunk of proc.stdout) {
307
- options.onOutput(Buffer.from(chunk));
306
+ try {
307
+ for await (const chunk of proc.stdout) {
308
+ options.onOutput(Buffer.from(chunk));
309
+ }
310
+ } catch (err) {
311
+ // BUG-21: Handle stream errors to avoid unhandled rejections
312
+ getLogger()?.error("agent", "runInteractive stdout error", { err });
308
313
  }
309
314
  })();
310
315
 
311
316
  // Fire onExit when process completes
312
- proc.exited.then((code) => {
313
- pidRegistry.unregister(proc.pid).catch(() => {});
314
- options.onExit(code ?? 1);
315
- });
317
+ proc.exited
318
+ .then((code) => {
319
+ pidRegistry.unregister(proc.pid).catch(() => {});
320
+ options.onExit(code ?? 1);
321
+ })
322
+ .catch((err) => {
323
+ // BUG-22: Guard against onExit or unregister throws
324
+ getLogger()?.error("agent", "runInteractive exit error", { err });
325
+ });
316
326
 
317
327
  return {
318
328
  write: (data: string) => {
@@ -84,19 +84,25 @@ export function usePty(options: PtySpawnOptions | null): PtyState & { handle: Pt
84
84
  const [ptyProcess, setPtyProcess] = useState<ReturnType<typeof Bun.spawn> | null>(null);
85
85
 
86
86
  // Spawn PTY process
87
+ // BUG-2: Destructure options to prevent infinite respawn loop due to object identity
88
+ const command = options?.command;
89
+ const argsJson = JSON.stringify(options?.args);
90
+ const cwd = options?.cwd;
91
+ const envJson = JSON.stringify(options?.env);
92
+
87
93
  useEffect(() => {
88
- if (!options) {
94
+ if (!command) {
89
95
  return;
90
96
  }
91
97
 
92
98
  // BUN-001: Replaced node-pty with Bun.spawn (piped stdio).
93
99
  // TERM + FORCE_COLOR preserve Claude Code output formatting.
94
- const proc = Bun.spawn([options.command, ...(options.args || [])], {
95
- cwd: options.cwd || process.cwd(),
96
- env: { ...process.env, ...options.env, TERM: "xterm-256color", FORCE_COLOR: "1" },
100
+ const proc = Bun.spawn([command, ...(JSON.parse(argsJson) || [])], {
101
+ cwd: cwd || process.cwd(),
102
+ env: { ...process.env, ...JSON.parse(envJson), TERM: "xterm-256color", FORCE_COLOR: "1" },
97
103
  stdin: "pipe",
98
104
  stdout: "pipe",
99
- stderr: "pipe",
105
+ stderr: "inherit", // MEM-1: Inherit stderr to avoid blocking on unread pipe
100
106
  });
101
107
 
102
108
  setPtyProcess(proc);
@@ -128,9 +134,14 @@ export function usePty(options: PtySpawnOptions | null): PtyState & { handle: Pt
128
134
  })();
129
135
 
130
136
  // Handle exit
131
- proc.exited.then((code) => {
132
- setState((prev) => ({ ...prev, isRunning: false, exitCode: code ?? undefined }));
133
- });
137
+ proc.exited
138
+ .then((code) => {
139
+ setState((prev) => ({ ...prev, isRunning: false, exitCode: code ?? undefined }));
140
+ })
141
+ .catch(() => {
142
+ // BUG-22: Guard against setState throws (e.g. on unmount)
143
+ setState((prev) => ({ ...prev, isRunning: false }));
144
+ });
134
145
 
135
146
  // Create handle
136
147
  const ptyHandle: PtyHandle = {
@@ -152,7 +163,7 @@ export function usePty(options: PtySpawnOptions | null): PtyState & { handle: Pt
152
163
  return () => {
153
164
  proc.kill();
154
165
  };
155
- }, [options]);
166
+ }, [command, argsJson, cwd, envJson]);
156
167
 
157
168
  // Handle terminal resize
158
169
  // resize is a no-op with Bun.spawn (no PTY) — kept for API compatibility