@nathapp/nax 0.42.3 → 0.42.5
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/dist/nax.js +24 -12
- package/package.json +1 -1
- package/src/agents/acp/adapter.ts +8 -3
- package/src/agents/acp/spawn-client.ts +9 -5
- package/src/agents/claude-complete.ts +3 -1
- package/src/agents/types.ts +6 -0
- package/src/cli/plan.ts +2 -1
package/dist/nax.js
CHANGED
|
@@ -3244,7 +3244,10 @@ async function executeComplete(binary, prompt, options) {
|
|
|
3244
3244
|
if (options?.jsonMode) {
|
|
3245
3245
|
cmd.push("--output-format", "json");
|
|
3246
3246
|
}
|
|
3247
|
-
const
|
|
3247
|
+
const spawnOpts = { stdout: "pipe", stderr: "pipe" };
|
|
3248
|
+
if (options?.workdir)
|
|
3249
|
+
spawnOpts.cwd = options.workdir;
|
|
3250
|
+
const proc = _completeDeps.spawn(cmd, spawnOpts);
|
|
3248
3251
|
const exitCode = await proc.exited;
|
|
3249
3252
|
const stdout = await new Response(proc.stdout).text();
|
|
3250
3253
|
const stderr = await new Response(proc.stderr).text();
|
|
@@ -19053,7 +19056,11 @@ class SpawnAcpClient {
|
|
|
19053
19056
|
const parts = cmdStr.split(/\s+/);
|
|
19054
19057
|
const modelIdx = parts.indexOf("--model");
|
|
19055
19058
|
this.model = modelIdx >= 0 && parts[modelIdx + 1] ? parts[modelIdx + 1] : "default";
|
|
19056
|
-
|
|
19059
|
+
const lastToken = parts[parts.length - 1];
|
|
19060
|
+
if (!lastToken || lastToken.startsWith("-")) {
|
|
19061
|
+
throw new Error(`[acp-adapter] Could not parse agentName from cmdStr: "${cmdStr}"`);
|
|
19062
|
+
}
|
|
19063
|
+
this.agentName = lastToken;
|
|
19057
19064
|
this.cwd = cwd || process.cwd();
|
|
19058
19065
|
this.timeoutSeconds = timeoutSeconds || 1800;
|
|
19059
19066
|
this.env = buildAllowedEnv2();
|
|
@@ -19079,15 +19086,15 @@ class SpawnAcpClient {
|
|
|
19079
19086
|
env: this.env
|
|
19080
19087
|
});
|
|
19081
19088
|
}
|
|
19082
|
-
async loadSession(sessionName) {
|
|
19083
|
-
const cmd = ["acpx", "--cwd", this.cwd,
|
|
19089
|
+
async loadSession(sessionName, agentName) {
|
|
19090
|
+
const cmd = ["acpx", "--cwd", this.cwd, agentName, "sessions", "ensure", "--name", sessionName];
|
|
19084
19091
|
const proc = _spawnClientDeps.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
19085
19092
|
const exitCode = await proc.exited;
|
|
19086
19093
|
if (exitCode !== 0) {
|
|
19087
19094
|
return null;
|
|
19088
19095
|
}
|
|
19089
19096
|
return new SpawnAcpSession({
|
|
19090
|
-
agentName
|
|
19097
|
+
agentName,
|
|
19091
19098
|
sessionName,
|
|
19092
19099
|
cwd: this.cwd,
|
|
19093
19100
|
model: this.model,
|
|
@@ -19177,9 +19184,12 @@ function buildSessionName(workdir, featureName, storyId, sessionRole) {
|
|
|
19177
19184
|
return parts.join("-");
|
|
19178
19185
|
}
|
|
19179
19186
|
async function ensureAcpSession(client, sessionName, agentName, permissionMode) {
|
|
19187
|
+
if (!agentName) {
|
|
19188
|
+
throw new Error("[acp-adapter] agentName is required for ensureAcpSession");
|
|
19189
|
+
}
|
|
19180
19190
|
if (client.loadSession) {
|
|
19181
19191
|
try {
|
|
19182
|
-
const existing = await client.loadSession(sessionName);
|
|
19192
|
+
const existing = await client.loadSession(sessionName, agentName);
|
|
19183
19193
|
if (existing) {
|
|
19184
19194
|
getSafeLogger()?.debug("acp-adapter", `Resumed existing session: ${sessionName}`);
|
|
19185
19195
|
return existing;
|
|
@@ -19445,10 +19455,11 @@ class AcpAgentAdapter {
|
|
|
19445
19455
|
const model = _options?.model ?? "default";
|
|
19446
19456
|
const timeoutMs = _options?.timeoutMs ?? 120000;
|
|
19447
19457
|
const permissionMode = _options?.dangerouslySkipPermissions ? "approve-all" : "default";
|
|
19458
|
+
const workdir = _options?.workdir;
|
|
19448
19459
|
let lastError;
|
|
19449
19460
|
for (let attempt = 0;attempt < MAX_RATE_LIMIT_RETRIES; attempt++) {
|
|
19450
19461
|
const cmdStr = `acpx --model ${model} ${this.name}`;
|
|
19451
|
-
const client = _acpAdapterDeps.createClient(cmdStr);
|
|
19462
|
+
const client = _acpAdapterDeps.createClient(cmdStr, workdir);
|
|
19452
19463
|
await client.start();
|
|
19453
19464
|
let session = null;
|
|
19454
19465
|
try {
|
|
@@ -21859,7 +21870,7 @@ var package_default;
|
|
|
21859
21870
|
var init_package = __esm(() => {
|
|
21860
21871
|
package_default = {
|
|
21861
21872
|
name: "@nathapp/nax",
|
|
21862
|
-
version: "0.42.
|
|
21873
|
+
version: "0.42.5",
|
|
21863
21874
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
21864
21875
|
type: "module",
|
|
21865
21876
|
bin: {
|
|
@@ -21932,8 +21943,8 @@ var init_version = __esm(() => {
|
|
|
21932
21943
|
NAX_VERSION = package_default.version;
|
|
21933
21944
|
NAX_COMMIT = (() => {
|
|
21934
21945
|
try {
|
|
21935
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
21936
|
-
return "
|
|
21946
|
+
if (/^[0-9a-f]{6,10}$/.test("7b603fa"))
|
|
21947
|
+
return "7b603fa";
|
|
21937
21948
|
} catch {}
|
|
21938
21949
|
try {
|
|
21939
21950
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -65781,7 +65792,7 @@ async function planCommand(workdir, config2, options) {
|
|
|
65781
65792
|
const cliAdapter = _deps2.getAgent(agentName);
|
|
65782
65793
|
if (!cliAdapter)
|
|
65783
65794
|
throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
65784
|
-
rawResponse = await cliAdapter.complete(prompt, { jsonMode: true });
|
|
65795
|
+
rawResponse = await cliAdapter.complete(prompt, { jsonMode: true, workdir });
|
|
65785
65796
|
try {
|
|
65786
65797
|
const envelope = JSON.parse(rawResponse);
|
|
65787
65798
|
if (envelope?.type === "result" && typeof envelope?.result === "string") {
|
|
@@ -65805,7 +65816,8 @@ async function planCommand(workdir, config2, options) {
|
|
|
65805
65816
|
config: config2,
|
|
65806
65817
|
modelTier: config2?.plan?.model ?? "balanced",
|
|
65807
65818
|
dangerouslySkipPermissions: config2?.execution?.dangerouslySkipPermissions ?? false,
|
|
65808
|
-
maxInteractionTurns: config2?.agent?.maxInteractionTurns
|
|
65819
|
+
maxInteractionTurns: config2?.agent?.maxInteractionTurns,
|
|
65820
|
+
featureName: options.feature
|
|
65809
65821
|
});
|
|
65810
65822
|
} finally {
|
|
65811
65823
|
logger?.info("plan", "Interactive session ended");
|
package/package.json
CHANGED
|
@@ -92,7 +92,7 @@ export interface AcpClient {
|
|
|
92
92
|
start(): Promise<void>;
|
|
93
93
|
createSession(opts: { agentName: string; permissionMode: string; sessionName?: string }): Promise<AcpSession>;
|
|
94
94
|
/** Resume an existing named session. Returns null if the session is not found. */
|
|
95
|
-
loadSession?(sessionName: string): Promise<AcpSession | null>;
|
|
95
|
+
loadSession?(sessionName: string, agentName: string): Promise<AcpSession | null>;
|
|
96
96
|
close(): Promise<void>;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -180,10 +180,14 @@ export async function ensureAcpSession(
|
|
|
180
180
|
agentName: string,
|
|
181
181
|
permissionMode: string,
|
|
182
182
|
): Promise<AcpSession> {
|
|
183
|
+
if (!agentName) {
|
|
184
|
+
throw new Error("[acp-adapter] agentName is required for ensureAcpSession");
|
|
185
|
+
}
|
|
186
|
+
|
|
183
187
|
// Try to resume existing session first
|
|
184
188
|
if (client.loadSession) {
|
|
185
189
|
try {
|
|
186
|
-
const existing = await client.loadSession(sessionName);
|
|
190
|
+
const existing = await client.loadSession(sessionName, agentName);
|
|
187
191
|
if (existing) {
|
|
188
192
|
getSafeLogger()?.debug("acp-adapter", `Resumed existing session: ${sessionName}`);
|
|
189
193
|
return existing;
|
|
@@ -554,12 +558,13 @@ export class AcpAgentAdapter implements AgentAdapter {
|
|
|
554
558
|
const model = _options?.model ?? "default";
|
|
555
559
|
const timeoutMs = _options?.timeoutMs ?? 120_000; // 2-min safety net by default
|
|
556
560
|
const permissionMode = _options?.dangerouslySkipPermissions ? "approve-all" : "default";
|
|
561
|
+
const workdir = _options?.workdir;
|
|
557
562
|
|
|
558
563
|
let lastError: Error | undefined;
|
|
559
564
|
|
|
560
565
|
for (let attempt = 0; attempt < MAX_RATE_LIMIT_RETRIES; attempt++) {
|
|
561
566
|
const cmdStr = `acpx --model ${model} ${this.name}`;
|
|
562
|
-
const client = _acpAdapterDeps.createClient(cmdStr);
|
|
567
|
+
const client = _acpAdapterDeps.createClient(cmdStr, workdir);
|
|
563
568
|
await client.start();
|
|
564
569
|
|
|
565
570
|
let session: AcpSession | null = null;
|
|
@@ -226,8 +226,12 @@ export class SpawnAcpClient implements AcpClient {
|
|
|
226
226
|
const parts = cmdStr.split(/\s+/);
|
|
227
227
|
const modelIdx = parts.indexOf("--model");
|
|
228
228
|
this.model = modelIdx >= 0 && parts[modelIdx + 1] ? parts[modelIdx + 1] : "default";
|
|
229
|
-
// Agent name is the last non-flag token
|
|
230
|
-
|
|
229
|
+
// Agent name is the last non-flag token — must be present and not a flag
|
|
230
|
+
const lastToken = parts[parts.length - 1];
|
|
231
|
+
if (!lastToken || lastToken.startsWith("-")) {
|
|
232
|
+
throw new Error(`[acp-adapter] Could not parse agentName from cmdStr: "${cmdStr}"`);
|
|
233
|
+
}
|
|
234
|
+
this.agentName = lastToken;
|
|
231
235
|
this.cwd = cwd || process.cwd();
|
|
232
236
|
this.timeoutSeconds = timeoutSeconds || 1800;
|
|
233
237
|
this.env = buildAllowedEnv();
|
|
@@ -267,9 +271,9 @@ export class SpawnAcpClient implements AcpClient {
|
|
|
267
271
|
});
|
|
268
272
|
}
|
|
269
273
|
|
|
270
|
-
async loadSession(sessionName: string): Promise<AcpSession | null> {
|
|
274
|
+
async loadSession(sessionName: string, agentName: string): Promise<AcpSession | null> {
|
|
271
275
|
// Try to ensure session exists — if it does, acpx returns success
|
|
272
|
-
const cmd = ["acpx", "--cwd", this.cwd,
|
|
276
|
+
const cmd = ["acpx", "--cwd", this.cwd, agentName, "sessions", "ensure", "--name", sessionName];
|
|
273
277
|
|
|
274
278
|
const proc = _spawnClientDeps.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
275
279
|
const exitCode = await proc.exited;
|
|
@@ -279,7 +283,7 @@ export class SpawnAcpClient implements AcpClient {
|
|
|
279
283
|
}
|
|
280
284
|
|
|
281
285
|
return new SpawnAcpSession({
|
|
282
|
-
agentName
|
|
286
|
+
agentName,
|
|
283
287
|
sessionName,
|
|
284
288
|
cwd: this.cwd,
|
|
285
289
|
model: this.model,
|
|
@@ -51,7 +51,9 @@ export async function executeComplete(binary: string, prompt: string, options?:
|
|
|
51
51
|
cmd.push("--output-format", "json");
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const
|
|
54
|
+
const spawnOpts: { stdout: "pipe"; stderr: "pipe"; cwd?: string } = { stdout: "pipe", stderr: "pipe" };
|
|
55
|
+
if (options?.workdir) spawnOpts.cwd = options.workdir;
|
|
56
|
+
const proc = _completeDeps.spawn(cmd, spawnOpts);
|
|
55
57
|
const exitCode = await proc.exited;
|
|
56
58
|
|
|
57
59
|
const stdout = await new Response(proc.stdout).text();
|
package/src/agents/types.ts
CHANGED
|
@@ -102,6 +102,12 @@ export interface CompleteOptions {
|
|
|
102
102
|
model?: string;
|
|
103
103
|
/** Whether to skip permission prompts (maps to permissionMode in ACP) */
|
|
104
104
|
dangerouslySkipPermissions?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Working directory for the completion call.
|
|
107
|
+
* Used by ACP adapter to set --cwd on the spawned acpx session.
|
|
108
|
+
* CLI adapter uses this as the process cwd when spawning the agent binary.
|
|
109
|
+
*/
|
|
110
|
+
workdir?: string;
|
|
105
111
|
/**
|
|
106
112
|
* Timeout for the completion call in milliseconds.
|
|
107
113
|
* Adapters that support it (e.g. ACP) will enforce this as a hard deadline.
|
package/src/cli/plan.ts
CHANGED
|
@@ -107,7 +107,7 @@ export async function planCommand(workdir: string, config: NaxConfig, options: P
|
|
|
107
107
|
const prompt = buildPlanningPrompt(specContent, codebaseContext);
|
|
108
108
|
const cliAdapter = _deps.getAgent(agentName);
|
|
109
109
|
if (!cliAdapter) throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
110
|
-
rawResponse = await cliAdapter.complete(prompt, { jsonMode: true });
|
|
110
|
+
rawResponse = await cliAdapter.complete(prompt, { jsonMode: true, workdir });
|
|
111
111
|
// CLI adapter returns {"type":"result","result":"..."} envelope — unwrap it
|
|
112
112
|
try {
|
|
113
113
|
const envelope = JSON.parse(rawResponse) as Record<string, unknown>;
|
|
@@ -135,6 +135,7 @@ export async function planCommand(workdir: string, config: NaxConfig, options: P
|
|
|
135
135
|
modelTier: config?.plan?.model ?? "balanced",
|
|
136
136
|
dangerouslySkipPermissions: config?.execution?.dangerouslySkipPermissions ?? false,
|
|
137
137
|
maxInteractionTurns: config?.agent?.maxInteractionTurns,
|
|
138
|
+
featureName: options.feature,
|
|
138
139
|
});
|
|
139
140
|
} finally {
|
|
140
141
|
logger?.info("plan", "Interactive session ended");
|