@nathapp/nax 0.45.0 → 0.46.0
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/CHANGELOG.md +19 -0
- package/bin/nax.ts +7 -6
- package/dist/nax.js +181 -124
- package/package.json +1 -1
- package/src/agents/acp/adapter.ts +34 -6
- package/src/agents/acp/index.ts +0 -2
- package/src/agents/acp/parser.ts +57 -104
- package/src/agents/acp/spawn-client.ts +2 -1
- package/src/agents/{claude.ts → claude/adapter.ts} +15 -12
- package/src/agents/{claude-complete.ts → claude/complete.ts} +3 -3
- package/src/agents/{cost.ts → claude/cost.ts} +1 -1
- package/src/agents/{claude-execution.ts → claude/execution.ts} +5 -5
- package/src/agents/claude/index.ts +3 -0
- package/src/agents/{claude-interactive.ts → claude/interactive.ts} +4 -4
- package/src/agents/{claude-plan.ts → claude/plan.ts} +12 -9
- package/src/agents/index.ts +5 -5
- package/src/agents/registry.ts +5 -5
- package/src/agents/{claude-decompose.ts → shared/decompose.ts} +2 -2
- package/src/agents/{model-resolution.ts → shared/model-resolution.ts} +2 -2
- package/src/agents/{types-extended.ts → shared/types-extended.ts} +4 -4
- package/src/agents/{validation.ts → shared/validation.ts} +2 -2
- package/src/agents/{version-detection.ts → shared/version-detection.ts} +3 -3
- package/src/agents/types.ts +8 -4
- package/src/cli/agents.ts +1 -1
- package/src/pipeline/stages/acceptance.ts +5 -8
- package/src/pipeline/stages/regression.ts +2 -0
- package/src/pipeline/stages/verify.ts +5 -10
- package/src/precheck/checks-agents.ts +1 -1
- package/src/utils/log-test-output.ts +25 -0
- /package/src/agents/{adapters/aider.ts → aider/adapter.ts} +0 -0
- /package/src/agents/{adapters/codex.ts → codex/adapter.ts} +0 -0
- /package/src/agents/{adapters/gemini.ts → gemini/adapter.ts} +0 -0
- /package/src/agents/{adapters/opencode.ts → opencode/adapter.ts} +0 -0
|
@@ -15,7 +15,7 @@ import { createHash } from "node:crypto";
|
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { resolvePermissions } from "../../config/permissions";
|
|
17
17
|
import { getSafeLogger } from "../../logger";
|
|
18
|
-
import { buildDecomposePrompt, parseDecomposeOutput } from "../
|
|
18
|
+
import { buildDecomposePrompt, parseDecomposeOutput } from "../shared/decompose";
|
|
19
19
|
import { createSpawnAcpClient } from "./spawn-client";
|
|
20
20
|
|
|
21
21
|
import type {
|
|
@@ -80,7 +80,14 @@ const DEFAULT_ENTRY: AgentRegistryEntry = {
|
|
|
80
80
|
export interface AcpSessionResponse {
|
|
81
81
|
messages: Array<{ role: string; content: string }>;
|
|
82
82
|
stopReason: string;
|
|
83
|
-
cumulative_token_usage?: {
|
|
83
|
+
cumulative_token_usage?: {
|
|
84
|
+
input_tokens: number;
|
|
85
|
+
output_tokens: number;
|
|
86
|
+
cache_read_input_tokens?: number;
|
|
87
|
+
cache_creation_input_tokens?: number;
|
|
88
|
+
};
|
|
89
|
+
/** Exact cost in USD from acpx usage_update event. Preferred over token-based estimation. */
|
|
90
|
+
exactCostUsd?: number;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
export interface AcpSession {
|
|
@@ -555,7 +562,13 @@ export class AcpAgentAdapter implements AgentAdapter {
|
|
|
555
562
|
// Tracks whether the run completed successfully — used by finally to decide
|
|
556
563
|
// whether to close the session (success) or keep it open for retry (failure).
|
|
557
564
|
const runState = { succeeded: false };
|
|
558
|
-
const totalTokenUsage = {
|
|
565
|
+
const totalTokenUsage = {
|
|
566
|
+
input_tokens: 0,
|
|
567
|
+
output_tokens: 0,
|
|
568
|
+
cache_read_input_tokens: 0,
|
|
569
|
+
cache_creation_input_tokens: 0,
|
|
570
|
+
};
|
|
571
|
+
let totalExactCostUsd: number | undefined;
|
|
559
572
|
|
|
560
573
|
try {
|
|
561
574
|
// 5. Multi-turn loop
|
|
@@ -577,10 +590,16 @@ export class AcpAgentAdapter implements AgentAdapter {
|
|
|
577
590
|
lastResponse = turnResult.response;
|
|
578
591
|
if (!lastResponse) break;
|
|
579
592
|
|
|
580
|
-
// Accumulate token usage
|
|
593
|
+
// Accumulate token usage and exact cost
|
|
581
594
|
if (lastResponse.cumulative_token_usage) {
|
|
582
595
|
totalTokenUsage.input_tokens += lastResponse.cumulative_token_usage.input_tokens ?? 0;
|
|
583
596
|
totalTokenUsage.output_tokens += lastResponse.cumulative_token_usage.output_tokens ?? 0;
|
|
597
|
+
totalTokenUsage.cache_read_input_tokens += lastResponse.cumulative_token_usage.cache_read_input_tokens ?? 0;
|
|
598
|
+
totalTokenUsage.cache_creation_input_tokens +=
|
|
599
|
+
lastResponse.cumulative_token_usage.cache_creation_input_tokens ?? 0;
|
|
600
|
+
}
|
|
601
|
+
if (lastResponse.exactCostUsd !== undefined) {
|
|
602
|
+
totalExactCostUsd = (totalExactCostUsd ?? 0) + lastResponse.exactCostUsd;
|
|
584
603
|
}
|
|
585
604
|
|
|
586
605
|
// Check for agent question → route to interaction bridge
|
|
@@ -643,10 +662,12 @@ export class AcpAgentAdapter implements AgentAdapter {
|
|
|
643
662
|
const success = lastResponse?.stopReason === "end_turn";
|
|
644
663
|
const output = extractOutput(lastResponse);
|
|
645
664
|
|
|
665
|
+
// Prefer exact cost from acpx usage_update; fall back to token-based estimation
|
|
646
666
|
const estimatedCost =
|
|
647
|
-
|
|
667
|
+
totalExactCostUsd ??
|
|
668
|
+
(totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0
|
|
648
669
|
? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model)
|
|
649
|
-
: 0;
|
|
670
|
+
: 0);
|
|
650
671
|
|
|
651
672
|
return {
|
|
652
673
|
success,
|
|
@@ -719,6 +740,13 @@ export class AcpAgentAdapter implements AgentAdapter {
|
|
|
719
740
|
throw new CompleteError("complete() returned empty output");
|
|
720
741
|
}
|
|
721
742
|
|
|
743
|
+
if (response.exactCostUsd !== undefined) {
|
|
744
|
+
getSafeLogger()?.info("acp-adapter", "complete() cost", {
|
|
745
|
+
costUsd: response.exactCostUsd,
|
|
746
|
+
model,
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
|
|
722
750
|
return unwrapped;
|
|
723
751
|
} catch (err) {
|
|
724
752
|
const error = err instanceof Error ? err : new Error(String(err));
|
package/src/agents/acp/index.ts
CHANGED
|
@@ -4,6 +4,4 @@
|
|
|
4
4
|
|
|
5
5
|
export { AcpAgentAdapter, _acpAdapterDeps } from "./adapter";
|
|
6
6
|
export { createSpawnAcpClient } from "./spawn-client";
|
|
7
|
-
export { estimateCostFromTokenUsage } from "./cost";
|
|
8
|
-
export type { SessionTokenUsage } from "./cost";
|
|
9
7
|
export type { AgentRegistryEntry } from "./types";
|
package/src/agents/acp/parser.ts
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* ACP adapter — NDJSON and JSON-RPC output parsing helpers.
|
|
3
3
|
*
|
|
4
4
|
* Extracted from adapter.ts to keep that file within the 800-line limit.
|
|
5
|
-
* Used
|
|
5
|
+
* Used by SpawnAcpSession.prompt() to parse acpx stdout.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { AgentRunOptions } from "../types";
|
|
9
|
-
|
|
10
8
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
9
|
// Types
|
|
12
10
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -15,131 +13,86 @@ import type { AgentRunOptions } from "../types";
|
|
|
15
13
|
export interface AcpxTokenUsage {
|
|
16
14
|
input_tokens: number;
|
|
17
15
|
output_tokens: number;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** JSON-RPC message from acpx --format json --json-strict */
|
|
21
|
-
interface JsonRpcMessage {
|
|
22
|
-
jsonrpc: "2.0";
|
|
23
|
-
method?: string;
|
|
24
|
-
params?: {
|
|
25
|
-
sessionId: string;
|
|
26
|
-
update?: {
|
|
27
|
-
sessionUpdate: string;
|
|
28
|
-
content?: { type: string; text?: string };
|
|
29
|
-
used?: number;
|
|
30
|
-
size?: number;
|
|
31
|
-
cost?: { amount: number; currency: string };
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
id?: number | string;
|
|
35
|
-
result?: unknown;
|
|
36
|
-
error?: { code: number; message: string };
|
|
16
|
+
cache_read_input_tokens?: number;
|
|
17
|
+
cache_creation_input_tokens?: number;
|
|
37
18
|
}
|
|
38
19
|
|
|
39
20
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
-
//
|
|
21
|
+
// parseAcpxJsonOutput
|
|
41
22
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
23
|
|
|
43
24
|
/**
|
|
44
|
-
*
|
|
25
|
+
* Parse acpx NDJSON output for assistant text, token usage, and exact cost.
|
|
26
|
+
*
|
|
27
|
+
* Handles the JSON-RPC envelope format emitted by acpx:
|
|
28
|
+
* - session/update agent_message_chunk → text accumulation
|
|
29
|
+
* - session/update usage_update → exact cost (cost.amount) + context size
|
|
30
|
+
* - id/result → token breakdown (inputTokens, outputTokens, cachedWriteTokens, cachedReadTokens)
|
|
31
|
+
*
|
|
32
|
+
* Also handles legacy flat NDJSON format for backward compatibility.
|
|
45
33
|
*/
|
|
46
|
-
export
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
export function parseAcpxJsonOutput(rawOutput: string): {
|
|
35
|
+
text: string;
|
|
36
|
+
tokenUsage?: AcpxTokenUsage;
|
|
37
|
+
exactCostUsd?: number;
|
|
38
|
+
stopReason?: string;
|
|
39
|
+
error?: string;
|
|
40
|
+
} {
|
|
41
|
+
const lines = rawOutput.split("\n").filter((l) => l.trim());
|
|
42
|
+
let text = "";
|
|
52
43
|
let tokenUsage: AcpxTokenUsage | undefined;
|
|
53
|
-
|
|
54
|
-
let
|
|
55
|
-
|
|
56
|
-
const reader = stdout.getReader();
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
while (true) {
|
|
60
|
-
const { done, value } = await reader.read();
|
|
61
|
-
if (done) break;
|
|
62
|
-
|
|
63
|
-
buffer += decoder.decode(value, { stream: true });
|
|
64
|
-
const lines = buffer.split("\n");
|
|
65
|
-
buffer = lines.pop() ?? "";
|
|
66
|
-
|
|
67
|
-
for (const line of lines) {
|
|
68
|
-
if (!line.trim()) continue;
|
|
44
|
+
let exactCostUsd: number | undefined;
|
|
45
|
+
let stopReason: string | undefined;
|
|
46
|
+
let error: string | undefined;
|
|
69
47
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
} catch {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
try {
|
|
50
|
+
const event = JSON.parse(line);
|
|
76
51
|
|
|
77
|
-
|
|
78
|
-
|
|
52
|
+
// ── JSON-RPC envelope format (acpx v0.3+) ──────────────────────────────
|
|
53
|
+
if (event.jsonrpc === "2.0") {
|
|
54
|
+
// session/update events
|
|
55
|
+
if (event.method === "session/update" && event.params?.update) {
|
|
56
|
+
const update = event.params.update;
|
|
79
57
|
|
|
58
|
+
// Text chunks
|
|
80
59
|
if (
|
|
81
60
|
update.sessionUpdate === "agent_message_chunk" &&
|
|
82
61
|
update.content?.type === "text" &&
|
|
83
62
|
update.content.text
|
|
84
63
|
) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (bridge?.detectQuestion && bridge.onQuestionDetected) {
|
|
88
|
-
const isQuestion = await bridge.detectQuestion(accumulatedText);
|
|
89
|
-
if (isQuestion) {
|
|
90
|
-
const response = await bridge.onQuestionDetected(accumulatedText);
|
|
91
|
-
accumulatedText += `\n\n[Human response: ${response}]`;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
64
|
+
text += update.content.text;
|
|
94
65
|
}
|
|
95
66
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
input_tokens: Math.floor(total * 0.3),
|
|
100
|
-
output_tokens: Math.floor(total * 0.7),
|
|
101
|
-
};
|
|
67
|
+
// Exact cost from usage_update
|
|
68
|
+
if (update.sessionUpdate === "usage_update" && typeof update.cost?.amount === "number") {
|
|
69
|
+
exactCostUsd = update.cost.amount;
|
|
102
70
|
}
|
|
103
71
|
}
|
|
104
72
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
accumulatedText += result;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
} finally {
|
|
114
|
-
reader.releaseLock();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return { text: accumulatedText.trim(), tokenUsage };
|
|
118
|
-
}
|
|
73
|
+
// Final result with token breakdown (camelCase from acpx)
|
|
74
|
+
if (event.id !== undefined && event.result && typeof event.result === "object") {
|
|
75
|
+
const result = event.result as Record<string, unknown>;
|
|
119
76
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
77
|
+
if (result.stopReason) stopReason = result.stopReason as string;
|
|
78
|
+
if (result.stop_reason) stopReason = result.stop_reason as string;
|
|
123
79
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
let tokenUsage: AcpxTokenUsage | undefined;
|
|
136
|
-
let stopReason: string | undefined;
|
|
137
|
-
let error: string | undefined;
|
|
80
|
+
if (result.usage && typeof result.usage === "object") {
|
|
81
|
+
const u = result.usage as Record<string, unknown>;
|
|
82
|
+
tokenUsage = {
|
|
83
|
+
input_tokens: (u.inputTokens as number) ?? (u.input_tokens as number) ?? 0,
|
|
84
|
+
output_tokens: (u.outputTokens as number) ?? (u.output_tokens as number) ?? 0,
|
|
85
|
+
cache_read_input_tokens: (u.cachedReadTokens as number) ?? (u.cache_read_input_tokens as number) ?? 0,
|
|
86
|
+
cache_creation_input_tokens:
|
|
87
|
+
(u.cachedWriteTokens as number) ?? (u.cache_creation_input_tokens as number) ?? 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
138
91
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const event = JSON.parse(line);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
142
94
|
|
|
95
|
+
// ── Legacy flat NDJSON format ───────────────────────────────────────────
|
|
143
96
|
if (event.content && typeof event.content === "string") text += event.content;
|
|
144
97
|
if (event.text && typeof event.text === "string") text += event.text;
|
|
145
98
|
if (event.result && typeof event.result === "string") text = event.result;
|
|
@@ -162,5 +115,5 @@ export function parseAcpxJsonOutput(rawOutput: string): {
|
|
|
162
115
|
}
|
|
163
116
|
}
|
|
164
117
|
|
|
165
|
-
return { text: text.trim(), tokenUsage, stopReason, error };
|
|
118
|
+
return { text: text.trim(), tokenUsage, exactCostUsd, stopReason, error };
|
|
166
119
|
}
|
|
@@ -180,8 +180,9 @@ class SpawnAcpSession implements AcpSession {
|
|
|
180
180
|
const parsed = parseAcpxJsonOutput(stdout);
|
|
181
181
|
return {
|
|
182
182
|
messages: [{ role: "assistant", content: parsed.text || "" }],
|
|
183
|
-
stopReason: "end_turn",
|
|
183
|
+
stopReason: parsed.stopReason ?? "end_turn",
|
|
184
184
|
cumulative_token_usage: parsed.tokenUsage,
|
|
185
|
+
exactCostUsd: parsed.exactCostUsd,
|
|
185
186
|
};
|
|
186
187
|
} catch (err) {
|
|
187
188
|
getSafeLogger()?.warn("acp-adapter", "Failed to parse session prompt response", {
|
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
* Main adapter class coordinating execution, completion, decomposition, and interactive modes.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { resolvePermissions } from "
|
|
8
|
-
import { PidRegistry } from "
|
|
9
|
-
import { withProcessTimeout } from "
|
|
10
|
-
import { getLogger } from "
|
|
11
|
-
import {
|
|
12
|
-
import { buildDecomposePrompt, parseDecomposeOutput } from "./claude-decompose";
|
|
13
|
-
import { _runOnceDeps, buildAllowedEnv, buildCommand, executeOnce } from "./claude-execution";
|
|
14
|
-
import { runInteractiveMode } from "./claude-interactive";
|
|
15
|
-
import { runPlan } from "./claude-plan";
|
|
7
|
+
import { resolvePermissions } from "../../config/permissions";
|
|
8
|
+
import { PidRegistry } from "../../execution/pid-registry";
|
|
9
|
+
import { withProcessTimeout } from "../../execution/timeout-handler";
|
|
10
|
+
import { getLogger } from "../../logger";
|
|
11
|
+
import { buildDecomposePrompt, parseDecomposeOutput } from "../shared/decompose";
|
|
16
12
|
import type {
|
|
17
13
|
AgentAdapter,
|
|
18
14
|
AgentCapabilities,
|
|
@@ -25,7 +21,11 @@ import type {
|
|
|
25
21
|
PlanOptions,
|
|
26
22
|
PlanResult,
|
|
27
23
|
PtyHandle,
|
|
28
|
-
} from "
|
|
24
|
+
} from "../types";
|
|
25
|
+
import { _completeDeps, executeComplete } from "./complete";
|
|
26
|
+
import { _runOnceDeps, buildAllowedEnv, buildCommand, executeOnce } from "./execution";
|
|
27
|
+
import { runInteractiveMode } from "./interactive";
|
|
28
|
+
import { runPlan } from "./plan";
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Injectable dependencies for decompose() — allows tests to intercept
|
|
@@ -174,7 +174,7 @@ export class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
async decompose(options: DecomposeOptions): Promise<DecomposeResult> {
|
|
177
|
-
const { resolveBalancedModelDef } = await import("
|
|
177
|
+
const { resolveBalancedModelDef } = await import("../shared/model-resolution");
|
|
178
178
|
|
|
179
179
|
const prompt = buildDecomposePrompt(options);
|
|
180
180
|
|
|
@@ -186,7 +186,10 @@ export class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
186
186
|
modelDef = resolveBalancedModelDef(options.config);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
const { skipPermissions } = resolvePermissions(
|
|
189
|
+
const { skipPermissions } = resolvePermissions(
|
|
190
|
+
options.config as import("../../config").NaxConfig | undefined,
|
|
191
|
+
"run",
|
|
192
|
+
);
|
|
190
193
|
const cmd = [this.binary, "--model", modelDef.model, "-p", prompt];
|
|
191
194
|
if (skipPermissions) {
|
|
192
195
|
cmd.splice(cmd.length - 2, 0, "--dangerously-skip-permissions");
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Standalone completion endpoint for simple prompts.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { resolvePermissions } from "
|
|
8
|
-
import type { CompleteOptions } from "
|
|
9
|
-
import { CompleteError } from "
|
|
7
|
+
import { resolvePermissions } from "../../config/permissions";
|
|
8
|
+
import type { CompleteOptions } from "../types";
|
|
9
|
+
import { CompleteError } from "../types";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Injectable dependencies for complete() — allows tests to intercept
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* Handles building commands, preparing environment, and process execution.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { resolvePermissions } from "
|
|
8
|
-
import type { PidRegistry } from "
|
|
9
|
-
import { withProcessTimeout } from "
|
|
10
|
-
import { getLogger } from "
|
|
7
|
+
import { resolvePermissions } from "../../config/permissions";
|
|
8
|
+
import type { PidRegistry } from "../../execution/pid-registry";
|
|
9
|
+
import { withProcessTimeout } from "../../execution/timeout-handler";
|
|
10
|
+
import { getLogger } from "../../logger";
|
|
11
|
+
import type { AgentResult, AgentRunOptions } from "../types";
|
|
11
12
|
import { estimateCostByDuration, estimateCostFromOutput } from "./cost";
|
|
12
|
-
import type { AgentResult, AgentRunOptions } from "./types";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Maximum characters to capture from agent stdout.
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Handles terminal UI interactions with the Claude agent.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { PidRegistry } from "
|
|
8
|
-
import { getLogger } from "
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
7
|
+
import type { PidRegistry } from "../../execution/pid-registry";
|
|
8
|
+
import { getLogger } from "../../logger";
|
|
9
|
+
import type { AgentRunOptions, InteractiveRunOptions, PtyHandle } from "../types";
|
|
10
|
+
import { buildAllowedEnv } from "./execution";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Run Claude agent in interactive (TTY) mode for TUI output.
|
|
@@ -7,13 +7,13 @@ import { join } from "node:path";
|
|
|
7
7
|
* Extracted from claude.ts: plan(), buildPlanCommand()
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { resolvePermissions } from "
|
|
11
|
-
import type { PidRegistry } from "
|
|
12
|
-
import { withProcessTimeout } from "
|
|
13
|
-
import { getLogger } from "
|
|
14
|
-
import { resolveBalancedModelDef } from "
|
|
15
|
-
import type {
|
|
16
|
-
import type {
|
|
10
|
+
import { resolvePermissions } from "../../config/permissions";
|
|
11
|
+
import type { PidRegistry } from "../../execution/pid-registry";
|
|
12
|
+
import { withProcessTimeout } from "../../execution/timeout-handler";
|
|
13
|
+
import { getLogger } from "../../logger";
|
|
14
|
+
import { resolveBalancedModelDef } from "../shared/model-resolution";
|
|
15
|
+
import type { PlanOptions, PlanResult } from "../shared/types-extended";
|
|
16
|
+
import type { AgentRunOptions } from "../types";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Build the CLI command for plan mode.
|
|
@@ -32,7 +32,10 @@ export function buildPlanCommand(binary: string, options: PlanOptions): string[]
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Resolve permission mode from config
|
|
35
|
-
const { skipPermissions } = resolvePermissions(
|
|
35
|
+
const { skipPermissions } = resolvePermissions(
|
|
36
|
+
options.config as import("../../config").NaxConfig | undefined,
|
|
37
|
+
"plan",
|
|
38
|
+
);
|
|
36
39
|
if (skipPermissions) {
|
|
37
40
|
cmd.push("--dangerously-skip-permissions");
|
|
38
41
|
}
|
|
@@ -75,7 +78,7 @@ export async function runPlan(
|
|
|
75
78
|
pidRegistry: PidRegistry,
|
|
76
79
|
buildAllowedEnv: (options: AgentRunOptions) => Record<string, string | undefined>,
|
|
77
80
|
): Promise<PlanResult> {
|
|
78
|
-
const { resolveBalancedModelDef } = await import("
|
|
81
|
+
const { resolveBalancedModelDef } = await import("../shared/model-resolution");
|
|
79
82
|
|
|
80
83
|
const cmd = buildPlanCommand(binary, options);
|
|
81
84
|
|
package/src/agents/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type { AgentAdapter, AgentCapabilities, AgentResult, AgentRunOptions, Com
|
|
|
2
2
|
export { CompleteError } from "./types";
|
|
3
3
|
export { ClaudeCodeAdapter } from "./claude";
|
|
4
4
|
export { getAllAgentNames, getAgent, getInstalledAgents, checkAgentHealth } from "./registry";
|
|
5
|
-
export type { ModelCostRates, TokenUsage, CostEstimate, TokenUsageWithConfidence } from "./cost";
|
|
5
|
+
export type { ModelCostRates, TokenUsage, CostEstimate, TokenUsageWithConfidence } from "./claude/cost";
|
|
6
6
|
export {
|
|
7
7
|
COST_RATES,
|
|
8
8
|
parseTokenUsage,
|
|
@@ -10,7 +10,7 @@ export {
|
|
|
10
10
|
estimateCostFromOutput,
|
|
11
11
|
estimateCostByDuration,
|
|
12
12
|
formatCostWithConfidence,
|
|
13
|
-
} from "./cost";
|
|
14
|
-
export { validateAgentForTier, validateAgentFeature, describeAgentCapabilities } from "./validation";
|
|
15
|
-
export type { AgentVersionInfo } from "./version-detection";
|
|
16
|
-
export { getAgentVersion, getAgentVersions } from "./version-detection";
|
|
13
|
+
} from "./claude/cost";
|
|
14
|
+
export { validateAgentForTier, validateAgentFeature, describeAgentCapabilities } from "./shared/validation";
|
|
15
|
+
export type { AgentVersionInfo } from "./shared/version-detection";
|
|
16
|
+
export { getAgentVersion, getAgentVersions } from "./shared/version-detection";
|
package/src/agents/registry.ts
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
import type { NaxConfig } from "../config/schema";
|
|
8
8
|
import { getLogger } from "../logger";
|
|
9
9
|
import { AcpAgentAdapter } from "./acp/adapter";
|
|
10
|
-
import { AiderAdapter } from "./
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
10
|
+
import { AiderAdapter } from "./aider/adapter";
|
|
11
|
+
import { ClaudeCodeAdapter } from "./claude/adapter";
|
|
12
|
+
import { CodexAdapter } from "./codex/adapter";
|
|
13
|
+
import { GeminiAdapter } from "./gemini/adapter";
|
|
14
|
+
import { OpenCodeAdapter } from "./opencode/adapter";
|
|
15
15
|
import type { AgentAdapter } from "./types";
|
|
16
16
|
|
|
17
17
|
/** All known agent adapters */
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* parseDecomposeOutput(), validateComplexity()
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { COMPLEXITY_GUIDE, GROUPING_RULES, TEST_STRATEGY_GUIDE, resolveTestStrategy } from "
|
|
9
|
-
import type { DecomposeOptions, DecomposeResult, DecomposedStory } from "
|
|
8
|
+
import { COMPLEXITY_GUIDE, GROUPING_RULES, TEST_STRATEGY_GUIDE, resolveTestStrategy } from "../../config/test-strategy";
|
|
9
|
+
import type { DecomposeOptions, DecomposeResult, DecomposedStory } from "../types";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Build the decompose prompt combining spec content and codebase context.
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Implementation placeholder — logic to be filled in by the implementer.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { resolveModel } from "
|
|
11
|
-
import type { ModelDef, NaxConfig } from "
|
|
10
|
+
import { resolveModel } from "../../config/schema";
|
|
11
|
+
import type { ModelDef, NaxConfig } from "../../config/schema";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Resolve the balanced model definition from config, with optional adapter default fallback.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Separated from core types to keep each file under 400 lines.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { ModelDef, ModelTier, NaxConfig } from "
|
|
8
|
+
import type { ModelDef, ModelTier, NaxConfig } from "../../config/schema";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Configuration options for running an agent in plan mode.
|
|
@@ -56,7 +56,7 @@ export interface PlanOptions {
|
|
|
56
56
|
*/
|
|
57
57
|
onAcpSessionCreated?: (sessionName: string) => Promise<void> | void;
|
|
58
58
|
/** PID registry for tracking spawned agent processes — cleanup on crash/SIGTERM */
|
|
59
|
-
pidRegistry?: import("
|
|
59
|
+
pidRegistry?: import("../../execution/pid-registry").PidRegistry;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -117,7 +117,7 @@ export interface DecomposedStory {
|
|
|
117
117
|
/** Implementation risks */
|
|
118
118
|
risks: string[];
|
|
119
119
|
/** Test strategy recommendation from LLM */
|
|
120
|
-
testStrategy?: import("
|
|
120
|
+
testStrategy?: import("../../config/test-strategy").TestStrategy;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
@@ -161,4 +161,4 @@ export interface InteractiveRunOptions extends AgentRunOptions {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// Re-import for the extends clause
|
|
164
|
-
import type { AgentRunOptions } from "
|
|
164
|
+
import type { AgentRunOptions } from "../types";
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Runtime validation for agent capabilities and tier compatibility.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { ModelTier } from "
|
|
8
|
-
import type { AgentAdapter } from "
|
|
7
|
+
import type { ModelTier } from "../../config/schema";
|
|
8
|
+
import type { AgentAdapter } from "../types";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Check if an agent supports a given model tier.
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* by running `<agent> --version` and parsing the output.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { getInstalledAgents } from "
|
|
9
|
-
import type { AgentAdapter } from "
|
|
8
|
+
import { getInstalledAgents } from "../registry";
|
|
9
|
+
import type { AgentAdapter } from "../types";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Information about an installed agent including its version
|
|
@@ -90,7 +90,7 @@ export async function getAgentVersions(): Promise<AgentVersionInfo[]> {
|
|
|
90
90
|
const agentsByName = new Map(agents.map((a) => [a.name, a]));
|
|
91
91
|
|
|
92
92
|
// Import ALL_AGENTS to include non-installed ones
|
|
93
|
-
const { ALL_AGENTS } = await import("
|
|
93
|
+
const { ALL_AGENTS } = await import("../registry");
|
|
94
94
|
|
|
95
95
|
const versions = await Promise.all(
|
|
96
96
|
ALL_AGENTS.map(async (agent: AgentAdapter): Promise<AgentVersionInfo> => {
|
package/src/agents/types.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type {
|
|
|
18
18
|
DecomposedStory,
|
|
19
19
|
PtyHandle,
|
|
20
20
|
InteractiveRunOptions,
|
|
21
|
-
} from "./types-extended";
|
|
21
|
+
} from "./shared/types-extended";
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Agent execution result returned after running a coding agent.
|
|
@@ -165,10 +165,12 @@ export interface AgentAdapter {
|
|
|
165
165
|
buildCommand(options: AgentRunOptions): string[];
|
|
166
166
|
|
|
167
167
|
/** Run the agent in plan mode to generate a feature specification. */
|
|
168
|
-
plan(options: import("./types-extended").PlanOptions): Promise<import("./types-extended").PlanResult>;
|
|
168
|
+
plan(options: import("./shared/types-extended").PlanOptions): Promise<import("./shared/types-extended").PlanResult>;
|
|
169
169
|
|
|
170
170
|
/** Run the agent in decompose mode to break spec into classified stories. */
|
|
171
|
-
decompose(
|
|
171
|
+
decompose(
|
|
172
|
+
options: import("./shared/types-extended").DecomposeOptions,
|
|
173
|
+
): Promise<import("./shared/types-extended").DecomposeResult>;
|
|
172
174
|
|
|
173
175
|
/**
|
|
174
176
|
* Run a one-shot LLM call and return the plain text response.
|
|
@@ -181,5 +183,7 @@ export interface AgentAdapter {
|
|
|
181
183
|
* This method is optional — only implemented by agents that support
|
|
182
184
|
* interactive terminal sessions (e.g., Claude Code).
|
|
183
185
|
*/
|
|
184
|
-
runInteractive?(
|
|
186
|
+
runInteractive?(
|
|
187
|
+
options: import("./shared/types-extended").InteractiveRunOptions,
|
|
188
|
+
): import("./shared/types-extended").PtyHandle;
|
|
185
189
|
}
|
package/src/cli/agents.ts
CHANGED