@nyxa/nyx-agent 0.7.0 → 0.8.1
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 +19 -8
- package/dist/cli.js +3 -1
- package/dist/commands/init.js +53 -29
- package/dist/config/schema.js +45 -4
- package/dist/runtime/prompts.js +50 -17
- package/dist/runtime/runPipeline.js +580 -121
- package/dist/runtime/schemas.js +108 -22
- package/dist/runtime/selectionConfirmation.js +15 -4
- package/dist/runtime/workItems.js +349 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,11 +7,13 @@ GitHub issue at a time, each phase with fresh context.
|
|
|
7
7
|
|
|
8
8
|
For every run NyxAgent:
|
|
9
9
|
|
|
10
|
-
1. **Selects** open GitHub issues to work on (read-only),
|
|
11
|
-
|
|
10
|
+
1. **Selects** executable open GitHub issues to work on (read-only), grouping
|
|
11
|
+
GitHub sub-issues under non-executable parent PRD/plan issues, then asks the
|
|
12
|
+
user to confirm the proposed checklist.
|
|
12
13
|
2. For each selected issue, in an isolated git **worktree**:
|
|
13
14
|
- **implements** it (the agent — the only customizable prompt),
|
|
14
|
-
- optionally **reviews**
|
|
15
|
+
- optionally **reviews** it in bounded discovery rounds, then revises only
|
|
16
|
+
verified blockers with locked validation,
|
|
15
17
|
- **commits** the change (the engine, deterministically).
|
|
16
18
|
3. Optionally runs a **global review** across the whole run.
|
|
17
19
|
4. **Pushes** the run branch and **opens one pull request** (the engine).
|
|
@@ -19,7 +21,9 @@ For every run NyxAgent:
|
|
|
19
21
|
The agent only implements, reviews, and revises. Every git/gh side effect —
|
|
20
22
|
commit, push, pull request — is performed by the engine, so closing the loop
|
|
21
23
|
never depends on the model. Issues are closed by GitHub when the PR merges
|
|
22
|
-
(`Closes #n` in the PR body);
|
|
24
|
+
(`Closes #n` in the PR body); parent PRD/plan issues are closed only when
|
|
25
|
+
NyxAgent can prove the PR completes their remaining open executable children.
|
|
26
|
+
The human merges the PR.
|
|
23
27
|
|
|
24
28
|
The workflow shape is fixed (not configurable). Only `.nyxagent/prompts/execution.md`
|
|
25
29
|
is editable.
|
|
@@ -45,7 +49,7 @@ nyxagent update # self-update to the latest published version
|
|
|
45
49
|
"model": "gpt-5.5",
|
|
46
50
|
"reasoning_effort": "medium",
|
|
47
51
|
"review": "each",
|
|
48
|
-
"
|
|
52
|
+
"review_rounds": { "each": 1, "global": 1 },
|
|
49
53
|
"tracker": { "type": "github", "repo": "owner/repo" },
|
|
50
54
|
"base_branch": "main",
|
|
51
55
|
"max_iterations": 5
|
|
@@ -54,12 +58,19 @@ nyxagent update # self-update to the latest published version
|
|
|
54
58
|
|
|
55
59
|
- `harness`: `codex` or `claude` (override per run with `--harness`).
|
|
56
60
|
- `review`: `each` (per task), `all` (global only), `both`, or `none`.
|
|
57
|
-
- `
|
|
61
|
+
- `review_rounds.each`: fresh per-task discovery rounds (default 1).
|
|
62
|
+
- `review_rounds.global`: fresh global discovery rounds (default 1).
|
|
63
|
+
- `review_max_attempts`: deprecated; accepted for old configs with a warning, but
|
|
64
|
+
ignored by the review loop.
|
|
65
|
+
- `agents.execution`, `agents.review`, `agents.global_review`, and
|
|
66
|
+
`agents.global_review.roles.<role>` can override `harness`, `model`, and
|
|
67
|
+
`reasoning_effort` for specialized phases. Global review roles are
|
|
68
|
+
`diff-contract`, `integration`, `domain-invariants`, and `tests-validation`.
|
|
58
69
|
- `base_branch`: optional; defaults to the current branch at run time.
|
|
59
70
|
|
|
60
|
-
If a run fails review
|
|
71
|
+
If a run fails review validation but has already produced
|
|
61
72
|
commits, NyxAgent pushes the branch and opens a **draft** pull request with the
|
|
62
|
-
unresolved
|
|
73
|
+
unresolved blockers, so the work is never stranded on an orphaned branch.
|
|
63
74
|
|
|
64
75
|
## Requirements
|
|
65
76
|
|
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,9 @@ program
|
|
|
20
20
|
.option("--model <name>", "model name")
|
|
21
21
|
.option("--reasoning-effort <level>", "reasoning effort (default: medium)")
|
|
22
22
|
.option("--review <mode>", "review strategy: each, all, both, or none")
|
|
23
|
-
.option("--review-
|
|
23
|
+
.option("--review-rounds-each <count>", "per-work-item review discovery rounds (default: 1)")
|
|
24
|
+
.option("--review-rounds-global <count>", "global review discovery rounds (default: 1)")
|
|
25
|
+
.option("--review-attempts <count>", "deprecated alias for both review round counts")
|
|
24
26
|
.option("--repo <owner/repo>", "GitHub repository")
|
|
25
27
|
.option("--base-branch <branch>", "base branch (default: current branch)")
|
|
26
28
|
.option("--max-iterations <count>", "maximum work items per run")
|
package/dist/commands/init.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { input, number as numberPrompt, select } from "@inquirer/prompts";
|
|
4
4
|
import pc from "picocolors";
|
|
5
|
-
import { harnessNames, reviewModes } from "../config/schema.js";
|
|
6
|
-
import { ensureDir, pathExists, readText, writeText } from "../runtime/files.js";
|
|
5
|
+
import { harnessNames, reviewModes, } from "../config/schema.js";
|
|
6
|
+
import { ensureDir, pathExists, readText, writeText, } from "../runtime/files.js";
|
|
7
7
|
import { getNyxDir, relativeToProject } from "../runtime/paths.js";
|
|
8
8
|
import { EXECUTION_PROMPT_FILE } from "../runtime/prompts.js";
|
|
9
9
|
const DEFAULT_CODEX_MODEL = "gpt-5.5";
|
|
@@ -17,7 +17,7 @@ const GITIGNORE_ENTRIES = [
|
|
|
17
17
|
".nyxagent/state.json",
|
|
18
18
|
".nyxagent/config.json",
|
|
19
19
|
".nyxagent/config.toml",
|
|
20
|
-
".nyxagent/prompts/"
|
|
20
|
+
".nyxagent/prompts/",
|
|
21
21
|
];
|
|
22
22
|
export async function initCommand(options, projectRoot = process.cwd()) {
|
|
23
23
|
const root = path.resolve(projectRoot);
|
|
@@ -46,14 +46,14 @@ async function resolveInitOptions(options) {
|
|
|
46
46
|
message: "Default harness",
|
|
47
47
|
choices: [
|
|
48
48
|
{ name: "codex", value: "codex" },
|
|
49
|
-
{ name: "claude", value: "claude" }
|
|
50
|
-
]
|
|
49
|
+
{ name: "claude", value: "claude" },
|
|
50
|
+
],
|
|
51
51
|
});
|
|
52
52
|
const model = options.model ??
|
|
53
53
|
(await input({
|
|
54
54
|
message: "Model",
|
|
55
55
|
default: harness === "codex" ? DEFAULT_CODEX_MODEL : "",
|
|
56
|
-
validate: (value) => value.trim().length > 0 || "Model is required"
|
|
56
|
+
validate: (value) => value.trim().length > 0 || "Model is required",
|
|
57
57
|
}));
|
|
58
58
|
const reasoning_effort = options.reasoningEffort ??
|
|
59
59
|
(await input({ message: "Reasoning effort", default: "medium" }));
|
|
@@ -65,34 +65,27 @@ async function resolveInitOptions(options) {
|
|
|
65
65
|
{ name: "After each task", value: "each" },
|
|
66
66
|
{ name: "After all tasks (global review)", value: "all" },
|
|
67
67
|
{ name: "Both per-task and global", value: "both" },
|
|
68
|
-
{ name: "No review", value: "none" }
|
|
68
|
+
{ name: "No review", value: "none" },
|
|
69
69
|
],
|
|
70
|
-
default: "each"
|
|
70
|
+
default: "each",
|
|
71
71
|
});
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
:
|
|
75
|
-
(await numberPrompt({
|
|
76
|
-
message: "Max review attempts per stage",
|
|
77
|
-
default: 4,
|
|
78
|
-
required: true
|
|
79
|
-
}));
|
|
80
|
-
if (!Number.isInteger(review_max_attempts) || review_max_attempts <= 0) {
|
|
81
|
-
throw new Error("review attempts must be a positive integer");
|
|
82
|
-
}
|
|
83
|
-
const repo = options.repo ?? (await input({ message: "GitHub repository (owner/repo)" }));
|
|
72
|
+
const review_rounds = await resolveReviewRounds(options, review);
|
|
73
|
+
const repo = options.repo ??
|
|
74
|
+
(await input({ message: "GitHub repository (owner/repo)" }));
|
|
84
75
|
validateRepository(repo);
|
|
85
76
|
const baseBranchInput = options.baseBranch ??
|
|
86
77
|
(await input({
|
|
87
78
|
message: "Base branch (blank = current branch at run time)",
|
|
88
|
-
default: ""
|
|
79
|
+
default: "",
|
|
89
80
|
}));
|
|
90
|
-
const base_branch = baseBranchInput.trim()
|
|
81
|
+
const base_branch = baseBranchInput.trim()
|
|
82
|
+
? baseBranchInput.trim()
|
|
83
|
+
: undefined;
|
|
91
84
|
const max_iterations = parseMaxIterations(options.maxIterations) ??
|
|
92
85
|
(await numberPrompt({
|
|
93
86
|
message: "Max work items per run",
|
|
94
87
|
default: 5,
|
|
95
|
-
required: true
|
|
88
|
+
required: true,
|
|
96
89
|
}));
|
|
97
90
|
if (!Number.isInteger(max_iterations) || max_iterations <= 0) {
|
|
98
91
|
throw new Error("max iterations must be a positive integer");
|
|
@@ -102,10 +95,10 @@ async function resolveInitOptions(options) {
|
|
|
102
95
|
model: model.trim(),
|
|
103
96
|
reasoning_effort: reasoning_effort.trim() || "medium",
|
|
104
97
|
review,
|
|
105
|
-
|
|
98
|
+
review_rounds,
|
|
106
99
|
repo,
|
|
107
100
|
base_branch,
|
|
108
|
-
max_iterations
|
|
101
|
+
max_iterations,
|
|
109
102
|
};
|
|
110
103
|
}
|
|
111
104
|
function buildConfig(options) {
|
|
@@ -115,11 +108,11 @@ function buildConfig(options) {
|
|
|
115
108
|
reasoning_effort: options.reasoning_effort,
|
|
116
109
|
review: options.review,
|
|
117
110
|
tracker: { type: "github", repo: options.repo },
|
|
118
|
-
max_iterations: options.max_iterations
|
|
111
|
+
max_iterations: options.max_iterations,
|
|
119
112
|
};
|
|
120
|
-
// No point persisting
|
|
113
|
+
// No point persisting review rounds when reviews are disabled.
|
|
121
114
|
if (options.review !== "none") {
|
|
122
|
-
config.
|
|
115
|
+
config.review_rounds = options.review_rounds;
|
|
123
116
|
}
|
|
124
117
|
if (options.base_branch) {
|
|
125
118
|
config.base_branch = options.base_branch;
|
|
@@ -149,7 +142,38 @@ function parseMaxIterations(value) {
|
|
|
149
142
|
}
|
|
150
143
|
return Number.parseInt(value, 10);
|
|
151
144
|
}
|
|
152
|
-
function
|
|
145
|
+
async function resolveReviewRounds(options, review) {
|
|
146
|
+
if (review === "none") {
|
|
147
|
+
return { each: 1, global: 1 };
|
|
148
|
+
}
|
|
149
|
+
const deprecatedAttempts = parsePositiveInteger(options.reviewAttempts);
|
|
150
|
+
const each = parsePositiveInteger(options.reviewRoundsEach) ??
|
|
151
|
+
deprecatedAttempts ??
|
|
152
|
+
(review === "each" || review === "both"
|
|
153
|
+
? await numberPrompt({
|
|
154
|
+
message: "Review rounds per work item",
|
|
155
|
+
default: 1,
|
|
156
|
+
required: true,
|
|
157
|
+
})
|
|
158
|
+
: 1);
|
|
159
|
+
const global = parsePositiveInteger(options.reviewRoundsGlobal) ??
|
|
160
|
+
deprecatedAttempts ??
|
|
161
|
+
(review === "all" || review === "both"
|
|
162
|
+
? await numberPrompt({
|
|
163
|
+
message: "Global review rounds",
|
|
164
|
+
default: 1,
|
|
165
|
+
required: true,
|
|
166
|
+
})
|
|
167
|
+
: 1);
|
|
168
|
+
if (!Number.isInteger(each) || each <= 0) {
|
|
169
|
+
throw new Error("review_rounds.each must be a positive integer");
|
|
170
|
+
}
|
|
171
|
+
if (!Number.isInteger(global) || global <= 0) {
|
|
172
|
+
throw new Error("review_rounds.global must be a positive integer");
|
|
173
|
+
}
|
|
174
|
+
return { each, global };
|
|
175
|
+
}
|
|
176
|
+
function parsePositiveInteger(value) {
|
|
153
177
|
if (value === undefined) {
|
|
154
178
|
return undefined;
|
|
155
179
|
}
|
package/dist/config/schema.js
CHANGED
|
@@ -7,7 +7,37 @@ import { z } from "zod";
|
|
|
7
7
|
*/
|
|
8
8
|
export const harnessNames = ["codex", "claude"];
|
|
9
9
|
export const reviewModes = ["each", "all", "both", "none"];
|
|
10
|
+
export const globalReviewRoles = [
|
|
11
|
+
"diff-contract",
|
|
12
|
+
"integration",
|
|
13
|
+
"domain-invariants",
|
|
14
|
+
"tests-validation",
|
|
15
|
+
];
|
|
10
16
|
const githubRepositoryPattern = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
17
|
+
const reviewRoundsSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
each: z.number().int().positive().default(1),
|
|
20
|
+
global: z.number().int().positive().default(1),
|
|
21
|
+
})
|
|
22
|
+
.default({ each: 1, global: 1 });
|
|
23
|
+
const agentOverrideSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
harness: z.enum(harnessNames).optional(),
|
|
26
|
+
model: z.string().min(1).optional(),
|
|
27
|
+
reasoning_effort: z.string().min(1).optional(),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
const globalReviewAgentOverrideSchema = agentOverrideSchema.extend({
|
|
31
|
+
roles: z
|
|
32
|
+
.object({
|
|
33
|
+
"diff-contract": agentOverrideSchema.optional(),
|
|
34
|
+
integration: agentOverrideSchema.optional(),
|
|
35
|
+
"domain-invariants": agentOverrideSchema.optional(),
|
|
36
|
+
"tests-validation": agentOverrideSchema.optional(),
|
|
37
|
+
})
|
|
38
|
+
.strict()
|
|
39
|
+
.optional(),
|
|
40
|
+
});
|
|
11
41
|
export const nyxConfigSchema = z
|
|
12
42
|
.object({
|
|
13
43
|
/** Which agent CLI runs each phase. Overridable per run via `run --harness`. */
|
|
@@ -18,18 +48,29 @@ export const nyxConfigSchema = z
|
|
|
18
48
|
reasoning_effort: z.string().min(1).default("medium"),
|
|
19
49
|
/** When the agent reviews its own work. */
|
|
20
50
|
review: z.enum(reviewModes).default("each"),
|
|
21
|
-
/** How many
|
|
22
|
-
|
|
51
|
+
/** How many fresh discovery rounds each review stage may run. */
|
|
52
|
+
review_rounds: reviewRoundsSchema,
|
|
53
|
+
/** Deprecated: accepted for existing configs, but no longer drives reviews. */
|
|
54
|
+
review_max_attempts: z.number().int().positive().optional(),
|
|
55
|
+
/** Optional agent overrides by phase and global-review role. */
|
|
56
|
+
agents: z
|
|
57
|
+
.object({
|
|
58
|
+
execution: agentOverrideSchema.optional(),
|
|
59
|
+
review: agentOverrideSchema.optional(),
|
|
60
|
+
global_review: globalReviewAgentOverrideSchema.optional(),
|
|
61
|
+
})
|
|
62
|
+
.strict()
|
|
63
|
+
.optional(),
|
|
23
64
|
/** Work item tracker. GitHub issues only in this version. */
|
|
24
65
|
tracker: z.object({
|
|
25
66
|
type: z.literal("github"),
|
|
26
67
|
repo: z
|
|
27
68
|
.string()
|
|
28
|
-
.regex(githubRepositoryPattern, 'tracker.repo must be "owner/repo"')
|
|
69
|
+
.regex(githubRepositoryPattern, 'tracker.repo must be "owner/repo"'),
|
|
29
70
|
}),
|
|
30
71
|
/** Base branch the run branch is cut from. Defaults to the current branch. */
|
|
31
72
|
base_branch: z.string().min(1).optional(),
|
|
32
73
|
/** Maximum work items processed in a single run. */
|
|
33
|
-
max_iterations: z.number().int().positive().default(5)
|
|
74
|
+
max_iterations: z.number().int().positive().default(5),
|
|
34
75
|
})
|
|
35
76
|
.strict();
|
package/dist/runtime/prompts.js
CHANGED
|
@@ -23,39 +23,72 @@ test, implement the smallest change that satisfies it, then tidy the result.
|
|
|
23
23
|
|
|
24
24
|
Do not commit and do not touch git — NyxAgent commits your changes for you. Leave
|
|
25
25
|
clear validation evidence (commands run and their results) in your final response.`;
|
|
26
|
-
export const REVIEW_PROMPT = `
|
|
26
|
+
export const REVIEW_PROMPT = `Discover findings in the implementation of the selected work item.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
anything.
|
|
28
|
+
Use the review-context artifact paths in the context above. Inspect the patch file,
|
|
29
|
+
diffstat, changed-files list, and the working directory as needed. Stay read-only
|
|
30
|
+
and do not modify anything.
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
validation evidence, design fit, and
|
|
32
|
+
This is discovery for the current review round only. Assess alignment with the work
|
|
33
|
+
item, correctness and regression risk, test or validation evidence, design fit, and
|
|
34
|
+
security or data-safety concerns.
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
Put only must-fix issues in blockers. Put missing or weak validation in test_gaps,
|
|
37
|
+
non-blocking concerns in advisory_findings, uncertain suspicions in
|
|
38
|
+
uncertain_findings, and explicitly refuted candidates in rejected_findings.`;
|
|
39
|
+
export const REVIEW_CHALLENGE_PROMPT = `Challenge the proposed blockers for the selected work item.
|
|
40
|
+
|
|
41
|
+
Stay read-only. Try to refute each proposed blocker using the current code,
|
|
42
|
+
review-context artifacts, and concrete evidence. Return only blockers that remain
|
|
43
|
+
valid and actionable. Move false positives or already-satisfied findings to
|
|
44
|
+
rejected_findings with evidence. Do not introduce new findings in this phase.`;
|
|
37
45
|
export const REVISION_PROMPT = `Apply the changes requested by the review for the selected work item.
|
|
38
46
|
|
|
39
|
-
The
|
|
40
|
-
the work focused. Do not commit — NyxAgent commits your changes for you.`;
|
|
47
|
+
The verified blockers are listed in the context above. Address exactly those,
|
|
48
|
+
keeping the work focused. Do not commit — NyxAgent commits your changes for you.`;
|
|
49
|
+
export const REVIEW_VALIDATION_PROMPT = `Validate the correction for the previously verified blockers.
|
|
50
|
+
|
|
51
|
+
Stay read-only. Validate only the blockers listed in the context above. Do not run a
|
|
52
|
+
new review and do not introduce unrelated new findings. For each blocker, return one
|
|
53
|
+
status: resolved, unresolved, false_positive, or regression_from_correction.
|
|
54
|
+
|
|
55
|
+
Use regression_from_correction only when the correction itself directly created a
|
|
56
|
+
new blocker and the evidence proves that causal link.`;
|
|
41
57
|
export const GLOBAL_REVIEW_PROMPT = `Review the entire run as a whole, now that every selected work item is implemented
|
|
42
58
|
and committed.
|
|
43
59
|
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
Use the review-context artifact paths in the context above. Inspect the patch file,
|
|
61
|
+
diffstat, changed-files list, commit list, and the working directory as needed. Stay
|
|
62
|
+
read-only and do not modify anything.
|
|
46
63
|
|
|
47
64
|
Focus on cross-cutting concerns a per-item review cannot see: integration between
|
|
48
65
|
items, regressions one item introduced in another, overall design coherence,
|
|
49
66
|
duplication, and gaps versus the issues' intent.
|
|
50
67
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
Return typed findings. Put only must-fix issues in blockers. Put missing or weak
|
|
69
|
+
validation in test_gaps, non-blocking concerns in advisory_findings, uncertain
|
|
70
|
+
suspicions in uncertain_findings, and explicitly refuted candidates in
|
|
71
|
+
rejected_findings.`;
|
|
72
|
+
export const GLOBAL_REVIEW_CHALLENGE_PROMPT = `Challenge the aggregated global-review blockers.
|
|
73
|
+
|
|
74
|
+
Stay read-only. Try to refute each proposed blocker using the current code,
|
|
75
|
+
review-context artifacts, and concrete evidence. Return only blockers that remain
|
|
76
|
+
valid and actionable. Move false positives or already-satisfied findings to
|
|
77
|
+
rejected_findings with evidence. Do not introduce new findings in this phase.`;
|
|
54
78
|
export const GLOBAL_REVISION_PROMPT = `Apply the changes requested by the global review of the whole run.
|
|
55
79
|
|
|
56
|
-
The
|
|
80
|
+
The verified blockers are listed in the context above. Address exactly those, across
|
|
57
81
|
whichever work items are affected. Do not commit — NyxAgent commits your corrections
|
|
58
82
|
for you.`;
|
|
83
|
+
export const GLOBAL_REVIEW_VALIDATION_PROMPT = `Validate the global review correction for the previously verified blockers.
|
|
84
|
+
|
|
85
|
+
Stay read-only. Validate only the blockers listed in the context above. Do not run a
|
|
86
|
+
new global review and do not introduce unrelated new findings. For each blocker,
|
|
87
|
+
return one status: resolved, unresolved, false_positive, or
|
|
88
|
+
regression_from_correction.
|
|
89
|
+
|
|
90
|
+
Use regression_from_correction only when the correction itself directly created a
|
|
91
|
+
new blocker and the evidence proves that causal link.`;
|
|
59
92
|
/** Rendered into .nyxagent/prompts/execution.md at init; the only editable prompt. */
|
|
60
93
|
export const EXECUTION_PROMPT_FILE = `${EXECUTION_PROMPT}
|
|
61
94
|
`;
|
|
@@ -88,7 +121,7 @@ export function buildPhasePrompt(input) {
|
|
|
88
121
|
"",
|
|
89
122
|
"## Instructions",
|
|
90
123
|
"",
|
|
91
|
-
input.guidance.trim()
|
|
124
|
+
input.guidance.trim(),
|
|
92
125
|
];
|
|
93
126
|
if (input.schema) {
|
|
94
127
|
parts.push("", "## Required result", "", "End your response with a single <nyxagent_result> block containing JSON that", "matches this schema. NyxAgent parses the last such block, validates it, and", "ignores everything else for control flow.", "", "```json", JSON.stringify(input.schema, null, 2), "```", "", "<nyxagent_result>", "{ ... }", "</nyxagent_result>");
|