@nathapp/nax 0.61.0 → 0.61.2-canary.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.
Files changed (2) hide show
  1. package/dist/nax.js +111 -11
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -3657,6 +3657,18 @@ function parseAcpxJsonLine(line, state) {
3657
3657
  };
3658
3658
  }
3659
3659
  }
3660
+ if (event.error && typeof event.error === "object") {
3661
+ const err = event.error;
3662
+ let errorMsg = typeof err.message === "string" ? err.message : JSON.stringify(event.error);
3663
+ if (err.data && typeof err.data === "object") {
3664
+ const data = err.data;
3665
+ const suffix = [data.acpxCode, data.detailCode].filter(Boolean).join("/");
3666
+ if (suffix)
3667
+ errorMsg = `${errorMsg} [${suffix}]`;
3668
+ }
3669
+ if (!state.error)
3670
+ state.error = errorMsg;
3671
+ }
3660
3672
  return;
3661
3673
  }
3662
3674
  if (event.content && typeof event.content === "string")
@@ -3807,12 +3819,15 @@ class SpawnAcpSession {
3807
3819
  Promise.race([stderrPromise, drainB.promise]).finally(() => drainB.cancel())
3808
3820
  ]);
3809
3821
  if (exitCode !== 0) {
3822
+ const parsedOnError = finalizeParseState(parseState);
3823
+ const errorContent = parsedOnError.error || stderr || `Exit code ${exitCode}`;
3810
3824
  getSafeLogger()?.warn("acp-adapter", `Session prompt exited with code ${exitCode}`, {
3811
3825
  exitCode,
3812
- stderr: stderr.slice(0, 500)
3826
+ error: errorContent.slice(0, 500),
3827
+ ...stderr && stderr !== errorContent ? { banner: stderr.trim().slice(0, 200) } : {}
3813
3828
  });
3814
3829
  return {
3815
- messages: [{ role: "assistant", content: stderr || `Exit code ${exitCode}` }],
3830
+ messages: [{ role: "assistant", content: errorContent }],
3816
3831
  stopReason: "error"
3817
3832
  };
3818
3833
  }
@@ -18070,6 +18085,7 @@ function isLegacyFlatModels(val) {
18070
18085
  var TokenPricingSchema, ModelDefSchema, ModelEntrySchema, PerAgentModelMapSchema, ModelMapSchema, ModelTierSchema, TierConfigSchema, AutoModeConfigSchema, RectificationConfigSchema, RegressionGateConfigSchema, SmartTestRunnerConfigSchema, SMART_TEST_RUNNER_DEFAULT, smartTestRunnerFieldSchema, ExecutionConfigSchema, QualityConfigSchema, TddConfigSchema, ConstitutionConfigSchema, AnalyzeConfigSchema, SemanticReviewConfigSchema, ReviewDialogueConfigSchema, ReviewConfigSchema, PlanConfigSchema, AcceptanceFixConfigSchema, AcceptanceConfigSchema, TestCoverageConfigSchema, ContextAutoDetectConfigSchema, ContextConfigSchema, LlmRoutingConfigSchema, RoutingConfigSchema, OptimizerConfigSchema, PluginConfigEntrySchema, HooksConfigSchema, InteractionConfigSchema, StorySizeGateConfigSchema, PromptAuditConfigSchema, AgentConfigSchema, PrecheckConfigSchema, PromptsConfigSchema, ProjectProfileSchema, VALID_AGENT_TYPES, GenerateConfigSchema, DebaterPersonaEnum, DebaterSchema, toObject = (val) => val === undefined || val === null ? {} : val, RESOLVER_TYPES, makeResolverSchema = (defaultType) => exports_external.preprocess(toObject, exports_external.object({
18071
18086
  type: exports_external.enum(RESOLVER_TYPES).default(defaultType),
18072
18087
  agent: exports_external.string().min(1).optional(),
18088
+ model: exports_external.string().min(1).optional(),
18073
18089
  tieBreaker: exports_external.string().min(1).optional(),
18074
18090
  maxPromptTokens: exports_external.number().int().positive().optional()
18075
18091
  })), DebateStageConfigSchema = (defaults) => exports_external.preprocess(toObject, exports_external.object({
@@ -19086,7 +19102,10 @@ class AcpAgentAdapter {
19086
19102
  try {
19087
19103
  const result = await this._runWithClient(options, startTime, currentAgent);
19088
19104
  if (!result.success) {
19089
- getSafeLogger()?.warn("acp-adapter", `Run failed for ${currentAgent}`, { exitCode: result.exitCode });
19105
+ getSafeLogger()?.warn("acp-adapter", `Run failed for ${currentAgent}`, {
19106
+ exitCode: result.exitCode,
19107
+ ...result.output ? { output: result.output.slice(0, 500) } : {}
19108
+ });
19090
19109
  if (result.sessionError && _acpAdapterDeps.shouldRetrySessionError && !sessionErrorRetried) {
19091
19110
  sessionErrorRetried = true;
19092
19111
  getSafeLogger()?.warn("acp-adapter", "Session error \u2014 retrying with fresh session", {
@@ -21694,12 +21713,16 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
21694
21713
  const adapter = _debateSessionDeps.getAgent(agentName, config2);
21695
21714
  if (adapter) {
21696
21715
  const synthesisSessionName = workdir !== undefined ? buildSessionName(workdir, featureName, storyId, "synthesis") : undefined;
21716
+ const resolverDebater = { agent: agentName, model: resolverConfig.model };
21717
+ const resolverTier = resolverConfig.model && MODEL_SHORTHAND_TIERS[resolverConfig.model.toLowerCase()] || modelTierFromDebater(resolverDebater);
21718
+ const resolverModelDef = resolveModelDefForDebater(resolverDebater, resolverTier, config2);
21697
21719
  const resolverResult = await synthesisResolver(proposalOutputs, critiqueOutputs, {
21698
21720
  adapter,
21699
21721
  promptSuffix,
21700
21722
  debaters,
21701
21723
  completeOptions: {
21702
- model: resolveDebaterModel({ agent: agentName }, config2),
21724
+ model: resolverModelDef.model,
21725
+ modelTier: resolverTier,
21703
21726
  config: config2,
21704
21727
  storyId,
21705
21728
  featureName,
@@ -21720,12 +21743,16 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
21720
21743
  if (resolverConfig.type === "custom") {
21721
21744
  const agentName = resolverConfig.agent ?? RESOLVER_FALLBACK_AGENT;
21722
21745
  const judgeSessionName = workdir !== undefined ? buildSessionName(workdir, featureName, storyId, "judge") : undefined;
21746
+ const resolverDebater = { agent: agentName, model: resolverConfig.model };
21747
+ const resolverTier = resolverConfig.model && MODEL_SHORTHAND_TIERS[resolverConfig.model.toLowerCase()] || modelTierFromDebater(resolverDebater);
21748
+ const resolverModelDef = resolveModelDefForDebater(resolverDebater, resolverTier, config2);
21723
21749
  const resolverResult = await judgeResolver(proposalOutputs, critiqueOutputs, resolverConfig, {
21724
21750
  getAgent: (name) => _debateSessionDeps.getAgent(name, config2),
21725
21751
  defaultAgentName: RESOLVER_FALLBACK_AGENT,
21726
21752
  debaters,
21727
21753
  completeOptions: {
21728
- model: resolveDebaterModel({ agent: agentName }, config2),
21754
+ model: resolverModelDef.model,
21755
+ modelTier: resolverTier,
21729
21756
  config: config2,
21730
21757
  storyId,
21731
21758
  featureName,
@@ -22719,7 +22746,8 @@ The spec above is the authoritative source for acceptance criteria.
22719
22746
  - If a debater proposed criteria beyond the spec (observable edge cases, error-path behaviors), place those in a separate \`suggestedCriteria\` array on the same story object. Each element of \`suggestedCriteria\` MUST be a plain string \u2014 never an object or structured value.
22720
22747
  - \`suggestedCriteria\` MUST contain only behavioral acceptance criteria \u2014 observable outputs, return values, state changes, or error conditions a test can assert. DO NOT include: implementation details (imports, internal structure), design suggestions ("consider X"), "not required" notes, or any criterion that cannot be expressed as a test assertion.
22721
22748
  - Never silently merge debater-invented criteria into \`acceptanceCriteria\`. The distinction matters: \`acceptanceCriteria\` drives automated testing; \`suggestedCriteria\` gates a hardening pass.
22722
- - Preserve the spec's AC wording. You may refine for clarity but must not change semantics.` : "";
22749
+ - Preserve the spec's AC wording. You may refine for clarity but must not change semantics.
22750
+ - Preserve each story's \`routing\` object unchanged \u2014 especially \`routing.complexity\` and \`routing.testStrategy\`. These are required by the schema and must not be dropped or modified during synthesis.` : "";
22723
22751
  const planSynthesisSuffix = `IMPORTANT: Your response must be a single valid JSON object in PRD format (with project, feature, branchName, userStories array, etc.). Do NOT wrap it in markdown fences. Output raw JSON only.${specAnchor}`;
22724
22752
  const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, resolverTimeoutMs, opts.workdir, opts.feature, undefined, undefined, planSynthesisSuffix, successful.map((p) => p.debater));
22725
22753
  const winningOutput = outcome.output ?? successful[0].output;
@@ -32371,7 +32399,8 @@ async function runTddSession(role, agent, story, config2, workdir, modelTier, be
32371
32399
  role,
32372
32400
  storyId: story.id,
32373
32401
  durationMs: Date.now() - startTime,
32374
- exitCode: result.exitCode
32402
+ exitCode: result.exitCode,
32403
+ ...result.output ? { output: result.output.slice(0, 500) } : {}
32375
32404
  });
32376
32405
  }
32377
32406
  await _sessionRunnerDeps.autoCommitIfDirty(workdir, "tdd", role, story.id);
@@ -36647,7 +36676,7 @@ var package_default;
36647
36676
  var init_package = __esm(() => {
36648
36677
  package_default = {
36649
36678
  name: "@nathapp/nax",
36650
- version: "0.61.0",
36679
+ version: "0.61.2-canary.1",
36651
36680
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
36652
36681
  type: "module",
36653
36682
  bin: {
@@ -36727,8 +36756,8 @@ var init_version = __esm(() => {
36727
36756
  NAX_VERSION = package_default.version;
36728
36757
  NAX_COMMIT = (() => {
36729
36758
  try {
36730
- if (/^[0-9a-f]{6,10}$/.test("de7efdbe"))
36731
- return "de7efdbe";
36759
+ if (/^[0-9a-f]{6,10}$/.test("7e54444a"))
36760
+ return "7e54444a";
36732
36761
  } catch {}
36733
36762
  try {
36734
36763
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -41071,6 +41100,68 @@ var init_run_initialization = __esm(() => {
41071
41100
  };
41072
41101
  });
41073
41102
 
41103
+ // src/execution/lifecycle/paused-story-prompts.ts
41104
+ var exports_paused_story_prompts = {};
41105
+ __export(exports_paused_story_prompts, {
41106
+ promptForPausedStories: () => promptForPausedStories
41107
+ });
41108
+ async function promptForPausedStories(prd, chain, featureName) {
41109
+ const logger = getSafeLogger();
41110
+ const summary = { resumed: [], skipped: [], kept: [] };
41111
+ const pausedStories = prd.userStories.filter((s) => s.status === "paused");
41112
+ for (const story of pausedStories) {
41113
+ const lastReason = (story.priorErrors?.slice(-1)[0] ?? "no reason recorded").replace(/\n/g, " ").slice(0, 200);
41114
+ logger?.info("run-initialization", "Paused story found \u2014 prompting user", {
41115
+ storyId: story.id,
41116
+ attempts: story.attempts
41117
+ });
41118
+ const response = await chain.prompt({
41119
+ id: `ix-${story.id}-paused-resume`,
41120
+ type: "choose",
41121
+ featureName,
41122
+ storyId: story.id,
41123
+ stage: "pre-flight",
41124
+ summary: `Story ${story.id} is paused \u2014 how to proceed?`,
41125
+ detail: `"${story.title}"
41126
+ Last reason: ${lastReason}
41127
+ Attempts so far: ${story.attempts}`,
41128
+ options: [
41129
+ { key: "resume", label: "Resume", description: "Reset to pending and retry on this run" },
41130
+ { key: "skip", label: "Skip", description: "Mark as skipped, won't be retried" },
41131
+ { key: "keep", label: "Keep paused", description: "Leave paused, skip for this run" }
41132
+ ],
41133
+ timeout: 300000,
41134
+ fallback: "continue",
41135
+ createdAt: Date.now()
41136
+ });
41137
+ const effectiveAction = chain.applyFallback(response, "continue");
41138
+ const resolvedKey = effectiveAction === "approve" ? "keep" : response.action;
41139
+ switch (resolvedKey) {
41140
+ case "resume": {
41141
+ story.status = "pending";
41142
+ summary.resumed.push(story.id);
41143
+ logger?.info("run-initialization", "User resumed paused story", { storyId: story.id });
41144
+ break;
41145
+ }
41146
+ case "skip": {
41147
+ story.status = "skipped";
41148
+ summary.skipped.push(story.id);
41149
+ logger?.info("run-initialization", "User skipped paused story", { storyId: story.id });
41150
+ break;
41151
+ }
41152
+ default: {
41153
+ summary.kept.push(story.id);
41154
+ logger?.info("run-initialization", "Keeping story paused", { storyId: story.id });
41155
+ break;
41156
+ }
41157
+ }
41158
+ }
41159
+ return summary;
41160
+ }
41161
+ var init_paused_story_prompts = __esm(() => {
41162
+ init_logger2();
41163
+ });
41164
+
41074
41165
  // src/execution/lifecycle/run-setup.ts
41075
41166
  var exports_run_setup = {};
41076
41167
  __export(exports_run_setup, {
@@ -41199,7 +41290,16 @@ async function setupRun(options) {
41199
41290
  agentGetFn: options.agentGetFn
41200
41291
  });
41201
41292
  prd = initResult.prd;
41202
- const counts = initResult.storyCounts;
41293
+ statusWriter.setPrd(prd);
41294
+ let counts = initResult.storyCounts;
41295
+ if (counts.paused > 0 && interactionChain !== null) {
41296
+ const { promptForPausedStories: promptForPausedStories2 } = await Promise.resolve().then(() => (init_paused_story_prompts(), exports_paused_story_prompts));
41297
+ const pausedSummary = await promptForPausedStories2(prd, interactionChain, feature);
41298
+ if (pausedSummary.resumed.length > 0 || pausedSummary.skipped.length > 0) {
41299
+ await savePRD(prd, prdPath);
41300
+ counts = countStories(prd);
41301
+ }
41302
+ }
41203
41303
  return {
41204
41304
  statusWriter,
41205
41305
  pidRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.61.0",
3
+ "version": "0.61.2-canary.1",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {