@nathapp/nax 0.82.0 → 0.82.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 (3) hide show
  1. package/README.md +4 -3
  2. package/dist/nax.js +352 -88
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -145,7 +145,7 @@ For full flag details, see the [CLI Reference](docs/guides/cli-reference.md).
145
145
 
146
146
  `execution.commandInterceptor` rewrites the `Git` tool's argv through `rtk` so `log` and `diff` output reaches the model compressed. It is confined to the Git site: user-authored `quality.commands` and `acceptance.command` are never wrapped. It fails open — if the `rtk` binary is missing the call runs as plain git.
147
147
 
148
- **Both features are native-agent only.** An ACP agent (`claude`, `codex`, `opencode`, `gemini`) brings its own tools, so nax's `Git` tool is never invoked and no MCP tool is advertised. A project on `"protocol": "acp"` can hold a complete, valid config for both and get zero effect, with no error. Set `agent.protocol: "hybrid"` and `agent.default: "native"` first.
148
+ **Both features are native-agent only.** An ACP agent (`claude`, `codex`, `opencode`, `gemini`) brings its own tools, so nax's `Git` tool is never invoked and no MCP tool is advertised. A project on `"protocol": "acp"` can hold a complete, valid config for both and get zero effect, with no error. The built-in defaults (`agent.protocol: "hybrid"`, `agent.default: "native"`) enable both; a config that switches to an acpx agent does not.
149
149
 
150
150
  See [MCP & Command Interception](docs/guides/mcp-and-interception.md) for setup, verification and troubleshooting, and the [Configuration Guide](docs/guides/configuration.md) for the full schema.
151
151
 
@@ -209,11 +209,12 @@ See [Plugins Guide](docs/guides/agents.md#plugins).
209
209
 
210
210
  ## Agents
211
211
 
212
- nax communicates with all coding agents via [ACP](https://github.com/openclaw/acpx) (Agent Client Protocol) — a JSON-RPC protocol that provides persistent sessions, exact token/cost reporting, and multi-turn session continuity.
212
+ The default agent is `native`: nax drives the model in-process over `@nathapp/nax-ai` (no CLI binary), using the built-in `models.native` Anthropic tier map, so it needs Anthropic credentials (`nax auth` or the provider's environment variable) unless you override the map. Every other agent is reached via [ACP](https://github.com/openclaw/acpx) (Agent Client Protocol) — a JSON-RPC protocol that provides persistent sessions, exact token/cost reporting, and multi-turn session continuity.
213
213
 
214
214
  | Agent | Binary | Notes |
215
215
  |:------|:-------|:------|
216
- | Claude Code | `claude` | Default. Set `agent.default: "claude"` |
216
+ | Native (nax-ai) | — (in-process) | Default. `agent.default: "native"` |
217
+ | Claude Code | `claude` | Set `agent.default: "claude"` |
217
218
  | OpenCode | `opencode` | Set `agent.default: "opencode"` |
218
219
  | Codex | `codex` | Set `agent.default: "codex"` |
219
220
  | Gemini CLI | `gemini` | Set `agent.default: "gemini"` |
package/dist/nax.js CHANGED
@@ -2668,7 +2668,7 @@ var package_default;
2668
2668
  var init_package = __esm(() => {
2669
2669
  package_default = {
2670
2670
  name: "@nathapp/nax",
2671
- version: "0.82.0",
2671
+ version: "0.82.1",
2672
2672
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
2673
2673
  type: "module",
2674
2674
  bin: {
@@ -4047,6 +4047,26 @@ var init_types2 = __esm(() => {
4047
4047
  };
4048
4048
  });
4049
4049
 
4050
+ // src/config/agent-defaults.ts
4051
+ function isBuiltInModelMap(agent, entry) {
4052
+ if (!Object.hasOwn(DEFAULT_MODEL_MAPS, agent) || typeof entry !== "object" || entry === null)
4053
+ return false;
4054
+ const builtIn = DEFAULT_MODEL_MAPS[agent];
4055
+ const tiers = Object.entries(entry);
4056
+ return tiers.length === Object.keys(builtIn).length && tiers.every(([tier, value]) => Object.hasOwn(builtIn, tier) && builtIn[tier] === value);
4057
+ }
4058
+ var DEFAULT_AGENT_PROTOCOL = "hybrid", DEFAULT_AGENT_NAME = "native", NATIVE_AGENT_NAME = "native", DEFAULT_MODEL_MAPS;
4059
+ var init_agent_defaults = __esm(() => {
4060
+ DEFAULT_MODEL_MAPS = {
4061
+ claude: { fast: "haiku", balanced: "sonnet", powerful: "opus" },
4062
+ native: {
4063
+ fast: "anthropic/claude-haiku-4-5",
4064
+ balanced: "anthropic/claude-sonnet-5",
4065
+ powerful: "anthropic/claude-opus-5-5"
4066
+ }
4067
+ };
4068
+ });
4069
+
4050
4070
  // node_modules/zod/v4/core/core.js
4051
4071
  function $constructor(name, initializer, params) {
4052
4072
  function init(inst, def) {
@@ -17936,7 +17956,7 @@ var init_schemas_sandbox = __esm(() => {
17936
17956
  SANDBOX_GLOB_CHARS = /[*?[\]{}]/;
17937
17957
  literalPath = exports_external.string().refine((p) => !SANDBOX_GLOB_CHARS.test(p), "sandbox paths must be literal: no * ? [ ] { } (spec F1)");
17938
17958
  SandboxConfigSchema = exports_external.object({
17939
- enabled: exports_external.boolean().default(false),
17959
+ enabled: exports_external.boolean().default(true),
17940
17960
  backend: exports_external.enum(["srt"]).default("srt"),
17941
17961
  filesystem: exports_external.object({
17942
17962
  allowWrite: exports_external.array(literalPath).default([]),
@@ -18184,7 +18204,7 @@ async function runArgv(options) {
18184
18204
  stdoutController.abort();
18185
18205
  stderrController.abort();
18186
18206
  };
18187
- const timerId = setTimeout(() => {
18207
+ const timerId = _argvExecDeps.setTimeout(() => {
18188
18208
  timedOut = true;
18189
18209
  killGroup();
18190
18210
  stopReaders();
@@ -18201,7 +18221,7 @@ async function runArgv(options) {
18201
18221
  const graceMs = _argvExecDeps.drainGraceMs;
18202
18222
  let graceTimerId;
18203
18223
  const gracePromise = new Promise((resolve3) => {
18204
- graceTimerId = setTimeout(() => resolve3("expired"), graceMs);
18224
+ graceTimerId = _argvExecDeps.setTimeout(() => resolve3("expired"), graceMs);
18205
18225
  });
18206
18226
  const stdoutSettled = await Promise.race([
18207
18227
  stdoutPromise,
@@ -18212,7 +18232,7 @@ async function runArgv(options) {
18212
18232
  gracePromise.then(() => "expired")
18213
18233
  ]);
18214
18234
  if (graceTimerId !== undefined)
18215
- clearTimeout(graceTimerId);
18235
+ _argvExecDeps.clearTimeout(graceTimerId);
18216
18236
  const stdoutClosed = stdoutSettled !== "expired";
18217
18237
  const stderrClosed = stderrSettled !== "expired";
18218
18238
  if (!stdoutClosed || !stderrClosed) {
@@ -18225,7 +18245,7 @@ async function runArgv(options) {
18225
18245
  }
18226
18246
  const stdoutFinal = stdoutClosed ? stdoutSettled : await stdoutPromise;
18227
18247
  const stderrFinal = stderrClosed ? stderrSettled : await stderrPromise;
18228
- clearTimeout(timerId);
18248
+ _argvExecDeps.clearTimeout(timerId);
18229
18249
  signal?.removeEventListener("abort", onAbort);
18230
18250
  return {
18231
18251
  exitCode,
@@ -18242,7 +18262,9 @@ var init_argv_exec = __esm(() => {
18242
18262
  _argvExecDeps = {
18243
18263
  spawn,
18244
18264
  killProcessGroup,
18245
- drainGraceMs: DRAIN_GRACE_MS
18265
+ drainGraceMs: DRAIN_GRACE_MS,
18266
+ setTimeout: (fn, ms) => setTimeout(fn, ms),
18267
+ clearTimeout: (id) => clearTimeout(id)
18246
18268
  };
18247
18269
  });
18248
18270
 
@@ -19638,7 +19660,8 @@ ${launched.stderr}`;
19638
19660
  isError: launched.timedOut || launched.exitCode !== 0 || launched.aborted === true,
19639
19661
  audit: {
19640
19662
  executed: launched.executed,
19641
- ...launched.sandbox !== undefined ? { sandbox: launched.sandbox } : {}
19663
+ ...launched.sandbox !== undefined ? { sandbox: launched.sandbox } : {},
19664
+ ...!launched.timedOut && launched.aborted !== true ? { exitCode: launched.exitCode } : {}
19642
19665
  },
19643
19666
  resultBytesPreTruncation: Buffer.byteLength(body, "utf8")
19644
19667
  };
@@ -23563,15 +23586,36 @@ var init_types3 = __esm(() => {
23563
23586
  });
23564
23587
 
23565
23588
  // src/command-safety/rule-scorer.ts
23566
- function scoreRules(command) {
23589
+ import { posix } from "path";
23590
+ function escapeRegExp(text) {
23591
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
23592
+ }
23593
+ function usableRoot(root) {
23594
+ if (root === undefined || !root.startsWith("/"))
23595
+ return;
23596
+ const normalized = posix.normalize(root).replace(/\/+$/, "");
23597
+ return normalized.split("/").filter(Boolean).length >= MIN_ROOT_SEGMENTS ? normalized : undefined;
23598
+ }
23599
+ function maskProjectRoot(command, root) {
23600
+ const usable = usableRoot(root);
23601
+ if (usable === undefined)
23602
+ return command;
23603
+ const underRoot = new RegExp(`(?<=^|[${PATH_END}=:])${escapeRegExp(usable)}((?:/[^${PATH_END}]*)?)(?=$|[${PATH_END}])`, "g");
23604
+ return command.replace(underRoot, (match, rest, offset) => {
23605
+ CLEAN_TAIL.lastIndex = offset + match.length;
23606
+ return /\.\.|\\|,/.test(rest) || !CLEAN_TAIL.test(command) ? match : `.${rest}`;
23607
+ });
23608
+ }
23609
+ function scoreRules(command, context = {}) {
23567
23610
  try {
23568
- const hits = Object.fromEntries(QUESTION_IDS.map((id) => [id, RULES[id].some((re) => re.test(command))]));
23611
+ const masked = maskProjectRoot(command, context.root);
23612
+ const hits = Object.fromEntries(QUESTION_IDS.map((id) => [id, RULES[id].some((re) => re.test(id === "outside_project" ? masked : command))]));
23569
23613
  return { version: RULE_SET_VERSION, hits };
23570
23614
  } catch (err) {
23571
23615
  return { version: RULE_SET_VERSION, hits: NO_HITS, error: errorMessage(err) };
23572
23616
  }
23573
23617
  }
23574
- var RULE_SET_VERSION = 1, RULES, NO_HITS;
23618
+ var RULE_SET_VERSION = 2, RULES, NO_HITS, PATH_END, CLEAN_TAIL, MIN_ROOT_SEGMENTS = 2;
23575
23619
  var init_rule_scorer = __esm(() => {
23576
23620
  init_types3();
23577
23621
  RULES = {
@@ -23617,6 +23661,8 @@ var init_rule_scorer = __esm(() => {
23617
23661
  privilege: [/(?:^|[\s;&|(])(?:sudo|doas)\s/, /\b(?:chmod|chown|chgrp)\b/]
23618
23662
  };
23619
23663
  NO_HITS = Object.freeze(Object.fromEntries(QUESTION_IDS.map((id) => [id, false])));
23664
+ PATH_END = String.raw`\s'"\`;&|<>()`;
23665
+ CLEAN_TAIL = /['")]*(?=$|[\s;&|<>])/y;
23620
23666
  });
23621
23667
 
23622
23668
  // src/command-safety/shadow.ts
@@ -23664,6 +23710,7 @@ function createCommandShadow(opts) {
23664
23710
  const { obs } = entry;
23665
23711
  const r = model.result;
23666
23712
  const cwd = entry.run?.cwd ?? obs.cwd;
23713
+ const rules = cwd !== obs.cwd ? scoreRules(obs.command, { root: cwd }) : entry.rules;
23667
23714
  return {
23668
23715
  at: _commandShadowDeps.now(),
23669
23716
  runId: opts.runId,
@@ -23676,7 +23723,7 @@ function createCommandShadow(opts) {
23676
23723
  ...cwd !== undefined ? { cwd } : {},
23677
23724
  mechanical: obs.mechanical,
23678
23725
  outcome,
23679
- rules: entry.rules,
23726
+ rules,
23680
23727
  ...callIdentifiers(obs),
23681
23728
  model: {
23682
23729
  status: model.cached && r.status === "answered" ? "cached" : r.status,
@@ -23694,7 +23741,7 @@ function createCommandShadow(opts) {
23694
23741
  try {
23695
23742
  if (entries.has(key))
23696
23743
  return;
23697
- const entry = { obs, rules: scoreRules(obs.command), written: false };
23744
+ const entry = { obs, rules: scoreRules(obs.command, { root: obs.cwd }), written: false };
23698
23745
  entries.set(key, entry);
23699
23746
  const { promise: promise2, cached: cached2 } = classifyCached(obs.command);
23700
23747
  track(inFlight2, promise2.then((result) => {
@@ -24524,6 +24571,7 @@ function createCodingToolRuntime(opts) {
24524
24571
  ...audit?.target !== undefined ? { target: audit.target } : {},
24525
24572
  ...audit?.approval !== undefined ? { approval: audit.approval } : {},
24526
24573
  ...audit?.sandbox !== undefined ? { sandbox: audit.sandbox } : {},
24574
+ ...audit?.exitCode !== undefined ? { exitCode: audit.exitCode } : {},
24527
24575
  ...provider !== undefined ? { provider } : {},
24528
24576
  ...resultBytesPreTruncation !== undefined ? { resultBytesPreTruncation } : {},
24529
24577
  ...opts.callId !== undefined ? { callId: opts.callId } : {},
@@ -26802,6 +26850,7 @@ var init_schemas_execution = __esm(() => {
26802
26850
  var PlanConfigSchema, AcceptanceFixConfigSchema, AcceptanceConfigSchema, LlmRoutingConfigSchema, AgentRoutingProfileSchema, AgentRoutingConfigSchema, RoutingConfigSchema, OptimizerConfigSchema, PluginConfigEntrySchema, HooksConfigSchema, InteractionConfigSchema, StorySizeGateConfigSchema, PromptAuditConfigSchema, UsageAuditConfigSchema, FallbackTargetSchema, AgentFallbackConfigSchema, DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG, AgentIdleWatchdogConfigSchema, DEFAULT_AGENT_SPIN_BREAKER_CONFIG, AgentSpinBreakerConfigSchema, AgentAcpConfigSchema, AgentNativeTransportRetryConfigSchema, AgentNativeConfigSchema, AgentTimeoutRetryConfigSchema, DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG, AgentConfigSchema, PrecheckConfigSchema, PromptsConfigSchema, ProjectProfileSchema, VALID_AGENT_TYPES, GenerateConfigSchema, CuratorThresholdsSchema, CuratorRetentionConfigSchema, CuratorConfigSchema;
26803
26851
  var init_schemas_infra = __esm(() => {
26804
26852
  init_zod();
26853
+ init_agent_defaults();
26805
26854
  init_schemas_model();
26806
26855
  PlanConfigSchema = exports_external.object({
26807
26856
  model: ConfiguredModelSchema,
@@ -27021,8 +27070,8 @@ var init_schemas_infra = __esm(() => {
27021
27070
  budgetMultiplier: 0.5
27022
27071
  };
27023
27072
  AgentConfigSchema = exports_external.object({
27024
- protocol: exports_external.enum(["acp", "native", "hybrid"]).default("acp"),
27025
- default: exports_external.string().trim().min(1, "agent.default must be non-empty").default("claude"),
27073
+ protocol: exports_external.enum(["acp", "native", "hybrid"]).default(DEFAULT_AGENT_PROTOCOL),
27074
+ default: exports_external.string().trim().min(1, "agent.default must be non-empty").default(DEFAULT_AGENT_NAME),
27026
27075
  maxInteractionTurns: exports_external.number().int().min(1).max(100).default(20),
27027
27076
  promptAudit: PromptAuditConfigSchema.default({ enabled: false }),
27028
27077
  usageAudit: UsageAuditConfigSchema.default({ enabled: false }),
@@ -27101,19 +27150,29 @@ var init_schemas_infra = __esm(() => {
27101
27150
  function asModelDef(entry) {
27102
27151
  return typeof entry === "object" && entry !== null ? entry : null;
27103
27152
  }
27153
+ function declaredModelAgents(models) {
27154
+ return Object.entries(models ?? {}).filter(([agent, entry]) => !isBuiltInModelMap(agent, entry)).map(([agent]) => agent);
27155
+ }
27104
27156
  function validateProtocolGate(data, ctx) {
27105
- const protocol = data.agent?.protocol ?? DEFAULT_PROTOCOL;
27106
- const modelAgents = Object.keys(data.models ?? {});
27107
- if (protocol === DEFAULT_PROTOCOL && modelAgents.includes(NATIVE)) {
27157
+ const protocol = data.agent?.protocol ?? DEFAULT_AGENT_PROTOCOL;
27158
+ const modelAgents = declaredModelAgents(data.models);
27159
+ if (protocol === "acp" && (data.agent?.default ?? DEFAULT_AGENT_NAME) === NATIVE_AGENT_NAME) {
27160
+ ctx.addIssue({
27161
+ code: "custom",
27162
+ path: ["agent", "default"],
27163
+ message: 'agent.protocol "acp" cannot reach agent.default "native", which is also the built-in default when agent.default is unset. Set agent.default to an acpx agent such as "claude", or use agent.protocol "hybrid".'
27164
+ });
27165
+ }
27166
+ if (protocol === "acp" && modelAgents.includes(NATIVE_AGENT_NAME)) {
27108
27167
  ctx.addIssue({
27109
27168
  code: "custom",
27110
- path: ["models", NATIVE],
27169
+ path: ["models", NATIVE_AGENT_NAME],
27111
27170
  message: 'models.native requires agent.protocol "hybrid" or "native" (it is "acp"). Set agent.protocol, or remove the native entry.'
27112
27171
  });
27113
27172
  }
27114
- if (protocol === NATIVE) {
27173
+ if (protocol === NATIVE_AGENT_NAME) {
27115
27174
  for (const agent of modelAgents) {
27116
- if (agent === NATIVE)
27175
+ if (agent === NATIVE_AGENT_NAME)
27117
27176
  continue;
27118
27177
  ctx.addIssue({
27119
27178
  code: "custom",
@@ -27121,7 +27180,7 @@ function validateProtocolGate(data, ctx) {
27121
27180
  message: `agent.protocol "native" permits only models.native; "${agent}" is an acpx agent. Use "hybrid" to run both.`
27122
27181
  });
27123
27182
  }
27124
- if ((data.agent?.default ?? DEFAULT_AGENT) !== NATIVE) {
27183
+ if ((data.agent?.default ?? DEFAULT_AGENT_NAME) !== NATIVE_AGENT_NAME) {
27125
27184
  ctx.addIssue({
27126
27185
  code: "custom",
27127
27186
  path: ["agent", "default"],
@@ -27133,7 +27192,7 @@ function validateProtocolGate(data, ctx) {
27133
27192
  validateFallbackLadderAgents(data, ctx);
27134
27193
  }
27135
27194
  function validateNativeModelIds(data, ctx) {
27136
- for (const [tier, entry] of Object.entries(data.models?.[NATIVE] ?? {})) {
27195
+ for (const [tier, entry] of Object.entries(data.models?.[NATIVE_AGENT_NAME] ?? {})) {
27137
27196
  if (entry === undefined)
27138
27197
  continue;
27139
27198
  const def = asModelDef(entry);
@@ -27145,8 +27204,8 @@ function validateNativeModelIds(data, ctx) {
27145
27204
  const sibling = def && typeof def.provider === "string" ? def.provider.trim() : "";
27146
27205
  ctx.addIssue({
27147
27206
  code: "custom",
27148
- path: ["models", NATIVE, tier, ...def ? ["model"] : []],
27149
- message: sibling.length > 0 ? `models.${NATIVE}.${tier}.model "${modelId}" must be written "provider/model". The sibling "provider" field is not used on the native path \u2014 put it in the model id: "${sibling}/${modelId}".` : `models.${NATIVE}.${tier} "${modelId}" must be written "provider/model" (e.g. "openai/gpt-5.4-mini"). There is no default provider.`
27207
+ path: ["models", NATIVE_AGENT_NAME, tier, ...def ? ["model"] : []],
27208
+ message: sibling.length > 0 ? `models.${NATIVE_AGENT_NAME}.${tier}.model "${modelId}" must be written "provider/model". The sibling "provider" field is not used on the native path \u2014 put it in the model id: "${sibling}/${modelId}".` : `models.${NATIVE_AGENT_NAME}.${tier} "${modelId}" must be written "provider/model" (e.g. "openai/gpt-5.4-mini"). There is no default provider.`
27150
27209
  });
27151
27210
  }
27152
27211
  }
@@ -27161,12 +27220,12 @@ function rungAgent(value) {
27161
27220
  return;
27162
27221
  }
27163
27222
  function validateFallbackLadderAgents(data, ctx) {
27164
- const protocol = data.agent?.protocol ?? DEFAULT_PROTOCOL;
27223
+ const protocol = data.agent?.protocol ?? DEFAULT_AGENT_PROTOCOL;
27165
27224
  if (protocol === "hybrid")
27166
27225
  return;
27167
27226
  const map2 = data.agent?.fallback?.map ?? {};
27168
27227
  const reject = (agent, path) => {
27169
- const permitted = protocol === NATIVE ? `only "${NATIVE}"` : `no native agent`;
27228
+ const permitted = protocol === NATIVE_AGENT_NAME ? `only "${NATIVE_AGENT_NAME}"` : `no native agent`;
27170
27229
  ctx.addIssue({
27171
27230
  code: "custom",
27172
27231
  path,
@@ -27174,7 +27233,7 @@ function validateFallbackLadderAgents(data, ctx) {
27174
27233
  });
27175
27234
  };
27176
27235
  for (const [from, rungs] of Object.entries(map2)) {
27177
- const offending = (agent) => protocol === NATIVE ? agent !== NATIVE : agent === NATIVE;
27236
+ const offending = (agent) => protocol === NATIVE_AGENT_NAME ? agent !== NATIVE_AGENT_NAME : agent === NATIVE_AGENT_NAME;
27178
27237
  if (offending(from))
27179
27238
  reject(from, ["agent", "fallback", "map", from]);
27180
27239
  (rungs ?? []).forEach((rung, index) => {
@@ -27184,9 +27243,9 @@ function validateFallbackLadderAgents(data, ctx) {
27184
27243
  });
27185
27244
  }
27186
27245
  }
27187
- var NATIVE = "native", DEFAULT_PROTOCOL = "acp", DEFAULT_AGENT = "claude";
27188
27246
  var init_schemas_protocol_gate = __esm(() => {
27189
27247
  init_model_spec();
27248
+ init_agent_defaults();
27190
27249
  });
27191
27250
 
27192
27251
  // src/config/schemas-reporters.ts
@@ -27350,6 +27409,7 @@ var init_schemas_review = __esm(() => {
27350
27409
  var NaxConfigSchema;
27351
27410
  var init_schemas3 = __esm(() => {
27352
27411
  init_zod();
27412
+ init_agent_defaults();
27353
27413
  init_bash_approval();
27354
27414
  init_schema_types();
27355
27415
  init_schemas_context();
@@ -27379,13 +27439,7 @@ var init_schemas3 = __esm(() => {
27379
27439
  message: "outputDir must be absolute or start with ~/"
27380
27440
  }),
27381
27441
  version: exports_external.number().default(1),
27382
- models: ModelMapSchema.default({
27383
- claude: {
27384
- fast: "haiku",
27385
- balanced: "sonnet",
27386
- powerful: "opus"
27387
- }
27388
- }),
27442
+ models: ModelMapSchema.default(structuredClone(DEFAULT_MODEL_MAPS)),
27389
27443
  autoMode: AutoModeConfigSchema.default({
27390
27444
  enabled: true,
27391
27445
  complexityRouting: {
@@ -27596,8 +27650,8 @@ var init_schemas3 = __esm(() => {
27596
27650
  }
27597
27651
  }),
27598
27652
  agent: AgentConfigSchema.optional().default({
27599
- protocol: "acp",
27600
- default: "claude",
27653
+ protocol: DEFAULT_AGENT_PROTOCOL,
27654
+ default: DEFAULT_AGENT_NAME,
27601
27655
  maxInteractionTurns: 20,
27602
27656
  promptAudit: { enabled: false },
27603
27657
  usageAudit: { enabled: false },
@@ -27699,7 +27753,7 @@ var init_schemas3 = __esm(() => {
27699
27753
  for (const [pi, profile] of profiles.entries()) {
27700
27754
  const { agent: pAgent, model: pModel } = profile.target;
27701
27755
  const targetTier = MODEL_SHORTHAND_TIERS[pModel.toLowerCase()] ?? pModel;
27702
- const namesTier = resolveTierMembership(data.models ?? {}, pAgent, targetTier, data.agent?.default ?? "claude").isTier;
27756
+ const namesTier = resolveTierMembership(data.models ?? {}, pAgent, targetTier, data.agent?.default ?? DEFAULT_AGENT_NAME).isTier;
27703
27757
  const hasMatchingRung = tierOrder.some((r) => r.tier === targetTier && r.agent === pAgent);
27704
27758
  if (namesTier && !hasMatchingRung) {
27705
27759
  ctx.addIssue({
@@ -28312,6 +28366,57 @@ function trackedSpawnDeadlines(config2) {
28312
28366
  };
28313
28367
  }
28314
28368
 
28369
+ // src/config/unreferenced-agent-models.ts
28370
+ function isPin(value) {
28371
+ return typeof value.agent === "string" && (("model" in value) || ("tier" in value));
28372
+ }
28373
+ function pinAgents(value, found) {
28374
+ if (Array.isArray(value)) {
28375
+ for (const item of value)
28376
+ pinAgents(item, found);
28377
+ return;
28378
+ }
28379
+ if (typeof value !== "object" || value === null)
28380
+ return;
28381
+ const record3 = value;
28382
+ if (isPin(record3))
28383
+ found.add(record3.agent);
28384
+ for (const child of Object.values(record3))
28385
+ pinAgents(child, found);
28386
+ }
28387
+ function fallbackAgents(config2) {
28388
+ const fallback = config2.agent?.fallback;
28389
+ if (fallback?.enabled !== true)
28390
+ return [];
28391
+ return Object.values(fallback.map ?? {}).flatMap((rungs) => (rungs ?? []).map((rung) => typeof rung === "string" ? rung : rung.agent));
28392
+ }
28393
+ function findUnreferencedAgentModels(config2, storyAgents = []) {
28394
+ const reached = new Set([
28395
+ config2.agent?.default ?? DEFAULT_AGENT_NAME,
28396
+ ...fallbackAgents(config2),
28397
+ ...storyAgents
28398
+ ]);
28399
+ for (const [key, value] of Object.entries(config2)) {
28400
+ if (!SKIPPED_ROOT_KEYS.has(key))
28401
+ pinAgents(value, reached);
28402
+ }
28403
+ return Object.entries(config2.models ?? {}).filter(([agent, map2]) => agent !== NATIVE_AGENT_NAME && !isBuiltInModelMap(agent, map2) && !reached.has(agent)).map(([agent]) => agent);
28404
+ }
28405
+ function describeUnreferencedAgentModels(agents, config2) {
28406
+ const maps = agents.map((agent) => `models.${agent}`).join(", ");
28407
+ const head = `${maps} is declared but nothing dispatches to it: no agent.default, enabled fallback rung, pin, escalation rung, ` + `complexity route, routing profile or PRD story names ${agents.join(", ")}.`;
28408
+ if (config2.agent?.protocol === "native") {
28409
+ return `${head} Under agent.protocol "native" acpx agents cannot run; remove the map or use protocol "hybrid".`;
28410
+ }
28411
+ const defaultAgent = config2.agent?.default ?? DEFAULT_AGENT_NAME;
28412
+ return `${head} Unassigned work runs on agent.default "${defaultAgent}". ` + `Set agent.default "${agents[0]}" to run it, or reference it from a pin or fallback rung.`;
28413
+ }
28414
+ var SKIPPED_ROOT_KEYS;
28415
+ var init_unreferenced_agent_models = __esm(() => {
28416
+ init_agent_defaults();
28417
+ SKIPPED_ROOT_KEYS = new Set(["models", "agent"]);
28418
+ });
28419
+
28315
28420
  // src/config/validate.ts
28316
28421
  function validateConfig(config2) {
28317
28422
  const errors3 = [];
@@ -28322,7 +28427,7 @@ function validateConfig(config2) {
28322
28427
  if (!config2.models) {
28323
28428
  errors3.push("models mapping is required");
28324
28429
  } else {
28325
- const defaultAgent = config2.agent?.default ?? "claude";
28430
+ const defaultAgent = config2.agent?.default ?? DEFAULT_AGENT_NAME;
28326
28431
  const agentModels = config2.models[defaultAgent];
28327
28432
  if (!agentModels) {
28328
28433
  errors3.push(`models.${defaultAgent} is required (default agent has no model map)`);
@@ -28370,13 +28475,13 @@ function validateConfig(config2) {
28370
28475
  }
28371
28476
  if (config2.models && config2.agent?.fallback?.map) {
28372
28477
  const modelKeys = Object.keys(config2.models);
28373
- const fallbackAgents = new Set;
28478
+ const fallbackAgents2 = new Set;
28374
28479
  for (const [primary, candidates] of Object.entries(config2.agent.fallback.map)) {
28375
- fallbackAgents.add(primary);
28480
+ fallbackAgents2.add(primary);
28376
28481
  for (const c of candidates)
28377
- fallbackAgents.add(typeof c === "string" ? c : c.agent);
28482
+ fallbackAgents2.add(typeof c === "string" ? c : c.agent);
28378
28483
  }
28379
- for (const agent of fallbackAgents) {
28484
+ for (const agent of fallbackAgents2) {
28380
28485
  if (!modelKeys.includes(agent)) {
28381
28486
  errors3.push(`agent.fallback.map: agent "${agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
28382
28487
  } else {
@@ -28395,7 +28500,7 @@ function validateConfig(config2) {
28395
28500
  errors3.push(`autoMode.escalation.tierOrder: tier "${tc.tier}" agent "${tc.agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
28396
28501
  }
28397
28502
  if (tc.agent === undefined) {
28398
- const owner = config2.agent?.default ?? "claude";
28503
+ const owner = config2.agent?.default ?? DEFAULT_AGENT_NAME;
28399
28504
  const ownerMap = config2.models[owner];
28400
28505
  if (ownerMap && ownerMap[tc.tier] === undefined) {
28401
28506
  errors3.push(`autoMode.escalation.tierOrder: tier "${tc.tier}" does not resolve under agent "${owner}" (the default agent)`);
@@ -28403,7 +28508,7 @@ function validateConfig(config2) {
28403
28508
  }
28404
28509
  }
28405
28510
  }
28406
- const defaultAgentKey = config2.agent?.default ?? "claude";
28511
+ const defaultAgentKey = config2.agent?.default ?? DEFAULT_AGENT_NAME;
28407
28512
  const complexities = ["simple", "medium", "complex", "expert"];
28408
28513
  for (const complexity of complexities) {
28409
28514
  const entry = config2.autoMode.complexityRouting[complexity];
@@ -28430,6 +28535,9 @@ function validateConfig(config2) {
28430
28535
  errors: errors3
28431
28536
  };
28432
28537
  }
28538
+ var init_validate = __esm(() => {
28539
+ init_agent_defaults();
28540
+ });
28433
28541
 
28434
28542
  // src/config/index.ts
28435
28543
  var exports_config = {};
@@ -28448,6 +28556,8 @@ __export(exports_config, {
28448
28556
  ContextV2ConfigSchema: () => ContextV2ConfigSchema,
28449
28557
  CuratorRetentionConfigSchema: () => CuratorRetentionConfigSchema,
28450
28558
  DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG: () => DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG,
28559
+ DEFAULT_AGENT_NAME: () => DEFAULT_AGENT_NAME,
28560
+ DEFAULT_AGENT_PROTOCOL: () => DEFAULT_AGENT_PROTOCOL,
28451
28561
  DEFAULT_AGENT_SPIN_BREAKER_CONFIG: () => DEFAULT_AGENT_SPIN_BREAKER_CONFIG,
28452
28562
  DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG: () => DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG,
28453
28563
  DEFAULT_CONFIG: () => DEFAULT_CONFIG,
@@ -28460,6 +28570,7 @@ __export(exports_config, {
28460
28570
  MODEL_SHORTHAND_TIERS: () => MODEL_SHORTHAND_TIERS,
28461
28571
  McpConfigSchema: () => McpConfigSchema,
28462
28572
  ModelTierSchema: () => ModelTierSchema,
28573
+ NATIVE_AGENT_NAME: () => NATIVE_AGENT_NAME,
28463
28574
  NaxConfigSchema: () => NaxConfigSchema,
28464
28575
  PROJECT_FEATURES_DIR: () => PROJECT_FEATURES_DIR,
28465
28576
  PROJECT_NAX_DIR: () => PROJECT_NAX_DIR,
@@ -28489,17 +28600,20 @@ __export(exports_config, {
28489
28600
  createConfigLoader: () => createConfigLoader,
28490
28601
  decomposeConfigSelector: () => decomposeConfigSelector,
28491
28602
  deepMergeConfig: () => deepMergeConfig,
28603
+ describeUnreferencedAgentModels: () => describeUnreferencedAgentModels,
28492
28604
  executionGatesConfigSelector: () => executionGatesConfigSelector,
28493
28605
  featureDir: () => featureDir,
28494
28606
  featuresDir: () => featuresDir,
28495
28607
  findInertBashStages: () => findInertBashStages,
28496
28608
  findProjectDir: () => findProjectDir,
28609
+ findUnreferencedAgentModels: () => findUnreferencedAgentModels,
28497
28610
  finishConfigSelector: () => finishConfigSelector,
28498
28611
  getAcQualityRules: () => getAcQualityRules,
28499
28612
  getProjectKey: () => getProjectKey,
28500
28613
  globalConfigDir: () => globalConfigDir,
28501
28614
  globalConfigPath: () => globalConfigPath,
28502
28615
  interactionConfigSelector: () => interactionConfigSelector,
28616
+ isBuiltInModelMap: () => isBuiltInModelMap,
28503
28617
  isSingleSessionTestOwningStrategy: () => isSingleSessionTestOwningStrategy,
28504
28618
  isThreeSessionStrategy: () => isThreeSessionStrategy,
28505
28619
  isUnrecognizedLiteralModel: () => isUnrecognizedLiteralModel,
@@ -28549,6 +28663,7 @@ __export(exports_config, {
28549
28663
  verifyConfigSelector: () => verifyConfigSelector
28550
28664
  });
28551
28665
  var init_config = __esm(() => {
28666
+ init_agent_defaults();
28552
28667
  init_bash_approval();
28553
28668
  init_inert_bash_stages();
28554
28669
  init_loader();
@@ -28570,6 +28685,8 @@ var init_config = __esm(() => {
28570
28685
  init_schemas_sandbox();
28571
28686
  init_selectors();
28572
28687
  init_test_strategy();
28688
+ init_unreferenced_agent_models();
28689
+ init_validate();
28573
28690
  });
28574
28691
 
28575
28692
  // src/agents/native/credentials.ts
@@ -28744,6 +28861,32 @@ async function ambientShadows(providerIds) {
28744
28861
  }));
28745
28862
  return checked.filter((id) => id !== undefined);
28746
28863
  }
28864
+ async function providersWithoutCredentials(providerIds) {
28865
+ const unique = [...new Set(providerIds)];
28866
+ let stored;
28867
+ try {
28868
+ stored = new Set((await listStoredProviders()).map((entry) => entry.providerId));
28869
+ } catch {
28870
+ return [];
28871
+ }
28872
+ const sweep = Promise.all(unique.filter((providerId) => !stored.has(providerId)).map(async (providerId) => {
28873
+ try {
28874
+ return await _authDeps.ambientAuthAvailable(providerId) ? undefined : providerId;
28875
+ } catch {
28876
+ return;
28877
+ }
28878
+ })).then((missing) => missing.filter((id) => id !== undefined));
28879
+ let timer;
28880
+ const expiry = new Promise((resolve15) => {
28881
+ timer = setTimeout(() => resolve15([]), AMBIENT_PROBE_TIMEOUT_MS);
28882
+ });
28883
+ try {
28884
+ return await Promise.race([sweep, expiry]);
28885
+ } finally {
28886
+ if (timer !== undefined)
28887
+ clearTimeout(timer);
28888
+ }
28889
+ }
28747
28890
  async function anyAmbientCredential() {
28748
28891
  let timer;
28749
28892
  const sweep = (async () => {
@@ -28764,13 +28907,13 @@ async function anyAmbientCredential() {
28764
28907
  });
28765
28908
  })();
28766
28909
  const expiry = new Promise((resolve15) => {
28767
- timer = setTimeout(() => resolve15(true), AMBIENT_PROBE_TIMEOUT_MS);
28910
+ timer = _authDeps.setTimeout(() => resolve15(true), AMBIENT_PROBE_TIMEOUT_MS);
28768
28911
  });
28769
28912
  try {
28770
28913
  return await Promise.race([sweep, expiry]);
28771
28914
  } finally {
28772
28915
  if (timer !== undefined)
28773
- clearTimeout(timer);
28916
+ _authDeps.clearTimeout(timer);
28774
28917
  }
28775
28918
  }
28776
28919
  var AuthCancelledError, _authDeps, DEFAULT_PI_AUTH_PATH, AMBIENT_PROBE_TIMEOUT_MS = 2000;
@@ -28788,7 +28931,9 @@ var init_auth = __esm(() => {
28788
28931
  _authDeps = {
28789
28932
  login,
28790
28933
  ambientAuthAvailable,
28791
- providerIds: async () => (await defaultProviders2()).map((provider) => provider.id)
28934
+ providerIds: async () => (await defaultProviders2()).map((provider) => provider.id),
28935
+ setTimeout: (fn, ms) => setTimeout(fn, ms),
28936
+ clearTimeout: (id) => clearTimeout(id)
28792
28937
  };
28793
28938
  DEFAULT_PI_AUTH_PATH = join20(homedir3(), ".pi", "agent", "auth.json");
28794
28939
  });
@@ -28874,11 +29019,12 @@ function resolveContextWindow(override, realWindow) {
28874
29019
  }
28875
29020
  return override;
28876
29021
  }
28877
- var NATIVE_AGENT = "native", THINKING_LEVELS;
29022
+ var THINKING_LEVELS;
28878
29023
  var init_models = __esm(() => {
28879
29024
  init_errors();
28880
29025
  init_logger2();
28881
29026
  init_model_spec();
29027
+ init_config();
28882
29028
  THINKING_LEVELS = {
28883
29029
  off: true,
28884
29030
  minimal: true,
@@ -28892,7 +29038,7 @@ var init_models = __esm(() => {
28892
29038
 
28893
29039
  // src/agents/native/client.ts
28894
29040
  import { createClient, defaultProtocols, defaultProviders as defaultProviders3 } from "@nathapp/nax-ai";
28895
- async function buildNativeClient(catalogOverrides = []) {
29041
+ async function buildNativeClient(catalogOverrides = [], options = {}) {
28896
29042
  return createClient({
28897
29043
  providers: await defaultProviders3(),
28898
29044
  protocols: ({ providerOverrides }) => _clientDeps.defaultProtocols({
@@ -28900,7 +29046,8 @@ async function buildNativeClient(catalogOverrides = []) {
28900
29046
  clientApp: NAX_CLIENT_APP,
28901
29047
  providerOverrides
28902
29048
  }),
28903
- ...catalogOverrides.length > 0 ? { providerOverrides: toProviderOverrides(catalogOverrides) } : {}
29049
+ ...catalogOverrides.length > 0 ? { providerOverrides: toProviderOverrides(catalogOverrides) } : {},
29050
+ ...options.transportRetries !== undefined ? { transportRetries: options.transportRetries } : {}
28904
29051
  });
28905
29052
  }
28906
29053
  function canonicalise(value) {
@@ -29358,7 +29505,7 @@ async function openNativeSession(name, opts) {
29358
29505
  });
29359
29506
  return {
29360
29507
  id: name,
29361
- agentName: NATIVE_AGENT,
29508
+ agentName: NATIVE_AGENT_NAME,
29362
29509
  protocolIds: { recordId: nativeSessionId(name), sessionId: nativeSessionId(name) },
29363
29510
  ...opts.modelDef !== undefined ? { modelDef: opts.modelDef } : {},
29364
29511
  ...opts.modelTier !== undefined ? { modelTier: opts.modelTier } : {}
@@ -31030,7 +31177,7 @@ ${previousSummary}`;
31030
31177
 
31031
31178
  class NativeAgentAdapter {
31032
31179
  catalogOverrides;
31033
- name = NATIVE_AGENT;
31180
+ name = NATIVE_AGENT_NAME;
31034
31181
  displayName = "Native (nax-ai)";
31035
31182
  binary = "";
31036
31183
  capabilities;
@@ -31064,7 +31211,7 @@ class NativeAgentAdapter {
31064
31211
  const client = await getNativeClient(this.catalogOverrides);
31065
31212
  const resolved = await client.model(provider, model);
31066
31213
  const controller = new AbortController;
31067
- const timer = options.timeoutMs !== undefined ? setTimeout(() => controller.abort(), options.timeoutMs) : undefined;
31214
+ const timer = options.timeoutMs !== undefined ? _adapterDeps.setTimeout(() => controller.abort(), options.timeoutMs) : undefined;
31068
31215
  try {
31069
31216
  const sessionId = nativeSessionId(this.oneShotKey);
31070
31217
  const result = await client.complete(resolved, {
@@ -31098,7 +31245,7 @@ class NativeAgentAdapter {
31098
31245
  throw err;
31099
31246
  } finally {
31100
31247
  if (timer !== undefined)
31101
- clearTimeout(timer);
31248
+ _adapterDeps.clearTimeout(timer);
31102
31249
  }
31103
31250
  }
31104
31251
  openSession(name, opts) {
@@ -31142,7 +31289,7 @@ class NativeAgentAdapter {
31142
31289
  });
31143
31290
  const deadlineController = new AbortController;
31144
31291
  const deadlineMs = deadline.remainingMs();
31145
- const deadlineTimer = deadlineMs !== undefined ? setTimeout(() => deadlineController.abort(), deadlineMs) : undefined;
31292
+ const deadlineTimer = deadlineMs !== undefined ? _adapterDeps.setTimeout(() => deadlineController.abort(), deadlineMs) : undefined;
31146
31293
  const turnSignals = [turnController.signal, deadlineController.signal];
31147
31294
  if (opts.signal !== undefined)
31148
31295
  turnSignals.unshift(opts.signal);
@@ -31165,7 +31312,7 @@ class NativeAgentAdapter {
31165
31312
  summarize: async (span, previousSummary) => {
31166
31313
  const remainingMs = deadline.remainingMs();
31167
31314
  const controller = new AbortController;
31168
- const timer = remainingMs !== undefined ? setTimeout(() => controller.abort(), remainingMs) : undefined;
31315
+ const timer = remainingMs !== undefined ? _adapterDeps.setTimeout(() => controller.abort(), remainingMs) : undefined;
31169
31316
  const signal = AbortSignal.any(opts.signal !== undefined ? [opts.signal, controller.signal, turnController.signal, deadlineController.signal] : [controller.signal, turnController.signal, deadlineController.signal]);
31170
31317
  try {
31171
31318
  const res = await client.complete(resolved, {
@@ -31178,13 +31325,13 @@ class NativeAgentAdapter {
31178
31325
  return { text: res.text, usage: summaryUsage, costUsd, rates: resolvedRates };
31179
31326
  } finally {
31180
31327
  if (timer !== undefined)
31181
- clearTimeout(timer);
31328
+ _adapterDeps.clearTimeout(timer);
31182
31329
  }
31183
31330
  },
31184
31331
  complete: async (messages, tools, requestOptions) => {
31185
31332
  const remainingMs = deadline.remainingMs();
31186
31333
  const controller = new AbortController;
31187
- const timer = remainingMs !== undefined ? setTimeout(() => controller.abort(), remainingMs) : undefined;
31334
+ const timer = remainingMs !== undefined ? _adapterDeps.setTimeout(() => controller.abort(), remainingMs) : undefined;
31188
31335
  const signal = AbortSignal.any(opts.signal !== undefined ? [opts.signal, controller.signal, turnController.signal, deadlineController.signal] : [controller.signal, turnController.signal, deadlineController.signal]);
31189
31336
  const requestThinking = requestOptions?.thinking === false ? undefined : thinking;
31190
31337
  try {
@@ -31209,7 +31356,7 @@ class NativeAgentAdapter {
31209
31356
  };
31210
31357
  } finally {
31211
31358
  if (timer !== undefined)
31212
- clearTimeout(timer);
31359
+ _adapterDeps.clearTimeout(timer);
31213
31360
  }
31214
31361
  }
31215
31362
  });
@@ -31229,7 +31376,7 @@ class NativeAgentAdapter {
31229
31376
  throw err;
31230
31377
  } finally {
31231
31378
  if (deadlineTimer !== undefined)
31232
- clearTimeout(deadlineTimer);
31379
+ _adapterDeps.clearTimeout(deadlineTimer);
31233
31380
  }
31234
31381
  hooks?.onStreamActivity?.({
31235
31382
  ...eventBase,
@@ -31244,7 +31391,7 @@ class NativeAgentAdapter {
31244
31391
  return closeNativeSession(handle);
31245
31392
  }
31246
31393
  async closePhysicalSession(handle, _workdir, _options) {
31247
- return closeNativeSession({ id: handle, agentName: NATIVE_AGENT });
31394
+ return closeNativeSession({ id: handle, agentName: NATIVE_AGENT_NAME });
31248
31395
  }
31249
31396
  }
31250
31397
  var CONSERVATIVE_CONTEXT_TOKENS = 128000, FALLBACK_TURN_TIMEOUT_SECONDS = 3600, DEFAULT_TIERS, _adapterDeps;
@@ -31262,7 +31409,12 @@ var init_adapter = __esm(() => {
31262
31409
  init_turn_types();
31263
31410
  init_session_affinity();
31264
31411
  DEFAULT_TIERS = ["fast", "balanced", "powerful"];
31265
- _adapterDeps = { listStoredProviders, anyAmbientCredential };
31412
+ _adapterDeps = {
31413
+ listStoredProviders,
31414
+ anyAmbientCredential,
31415
+ setTimeout: (fn, ms) => setTimeout(fn, ms),
31416
+ clearTimeout: (id) => clearTimeout(id)
31417
+ };
31266
31418
  });
31267
31419
 
31268
31420
  // src/agents/native/model-resolver.ts
@@ -31807,7 +31959,7 @@ async function assertPrdCommitted(prdPath, projectRoot) {
31807
31959
  });
31808
31960
  }
31809
31961
  }
31810
- var init_validate = __esm(() => {
31962
+ var init_validate2 = __esm(() => {
31811
31963
  init_errors();
31812
31964
  init_git();
31813
31965
  });
@@ -31906,7 +32058,7 @@ function validateInjectedStory(raw, existingIds) {
31906
32058
  var STORY_ID_PREFIX = "US";
31907
32059
  var init_inject = __esm(() => {
31908
32060
  init_errors();
31909
- init_validate();
32061
+ init_validate2();
31910
32062
  });
31911
32063
 
31912
32064
  // src/prd/modifies-extract.ts
@@ -33002,7 +33154,7 @@ var init_schema_story = __esm(() => {
33002
33154
  init_test_strategy();
33003
33155
  init_errors();
33004
33156
  init_out_of_scope();
33005
- init_validate();
33157
+ init_validate2();
33006
33158
  VALID_COMPLEXITY = ["simple", "medium", "complex", "expert"];
33007
33159
  WORKDIR_SOURCES = ["stated", "derived", "defaulted"];
33008
33160
  STORY_ID_NO_SEPARATOR = /^([A-Za-z]+)(\d+)$/;
@@ -33357,7 +33509,7 @@ var init_prd = __esm(() => {
33357
33509
  init_out_of_scope_extract();
33358
33510
  init_spec_drift();
33359
33511
  init_spec_lint();
33360
- init_validate();
33512
+ init_validate2();
33361
33513
  init_workdir_canonical();
33362
33514
  init_schema2();
33363
33515
  PRD_MAX_FILE_SIZE = 5 * 1024 * 1024;
@@ -53052,8 +53204,8 @@ var init_version = __esm(() => {
53052
53204
  NAX_AI_VERSION = CATALOG_VERSION;
53053
53205
  NAX_COMMIT = (() => {
53054
53206
  try {
53055
- if (/^[0-9a-f]{6,10}$/.test("a83e5791"))
53056
- return "a83e5791";
53207
+ if (/^[0-9a-f]{6,10}$/.test("3e4ec026"))
53208
+ return "3e4ec026";
53057
53209
  } catch {}
53058
53210
  try {
53059
53211
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -65849,7 +66001,7 @@ var init_coding_tool_support = __esm(() => {
65849
66001
 
65850
66002
  // src/agents/tool-preamble.ts
65851
66003
  function promptWithToolPreamble(agentName, options) {
65852
- const base = agentName === NATIVE_AGENT ? options.prompt : buildContextToolPreamble(options);
66004
+ const base = agentName === NATIVE_AGENT_NAME ? options.prompt : buildContextToolPreamble(options);
65853
66005
  const scope = buildAgentScopeSection(options.codingToolRoot, options.codingToolWorkdirLabel);
65854
66006
  return scope === undefined ? base : `${scope}
65855
66007
 
@@ -65857,7 +66009,7 @@ ${base}`;
65857
66009
  }
65858
66010
  function applyDiffAccessForAgentProtocol(agentName, prompt, advertisedTools) {
65859
66011
  return applyProtocolRegions(prompt, {
65860
- protocol: agentName === NATIVE_AGENT ? "native" : "acp",
66012
+ protocol: agentName === NATIVE_AGENT_NAME ? "native" : "acp",
65861
66013
  advertisedTools: new Set(advertisedTools)
65862
66014
  });
65863
66015
  }
@@ -76918,7 +77070,7 @@ function buildSessionTurnEvent(input) {
76918
77070
  ...result.pricingSource !== undefined ? { pricingSource: result.pricingSource } : {},
76919
77071
  ...result.rates !== undefined ? { rates: result.rates } : {},
76920
77072
  roundTrips: result.internalRoundTrips ?? 1,
76921
- roundTripUnit: input.agentName === NATIVE_AGENT ? "model-call" : "agent-run",
77073
+ roundTripUnit: input.agentName === NATIVE_AGENT_NAME ? "model-call" : "agent-run",
76922
77074
  protocolIds: {
76923
77075
  sessionId: handle.protocolIds?.sessionId ?? null,
76924
77076
  recordId: handle.protocolIds?.recordId ?? null,
@@ -77475,7 +77627,7 @@ var init_manager_run_fallback = __esm(() => {
77475
77627
 
77476
77628
  // src/agents/registry.ts
77477
77629
  function adapterFor(name) {
77478
- return name === NATIVE_AGENT ? new NativeAgentAdapter : new AcpAgentAdapter(name);
77630
+ return name === NATIVE_AGENT_NAME ? new NativeAgentAdapter : new AcpAgentAdapter(name);
77479
77631
  }
77480
77632
  function buildAdapterList() {
77481
77633
  return [...Array.from(_registryTestAdapters.values()), ...KNOWN_AGENT_NAMES.map(adapterFor)];
@@ -77491,12 +77643,12 @@ async function getInstalledAgents() {
77491
77643
  function createAgentRegistry(config2) {
77492
77644
  const logger = getLogger();
77493
77645
  const adapterCache = new Map;
77494
- const protocol = config2.agent?.protocol ?? "acp";
77646
+ const protocol = config2.agent?.protocol ?? DEFAULT_AGENT_PROTOCOL;
77495
77647
  logger?.info("agents", `Agent protocol: ${protocol}`, { protocol, hasConfig: !!config2.agent });
77496
77648
  function cachedAdapter(name) {
77497
77649
  let adapter = adapterCache.get(name);
77498
77650
  if (adapter === undefined) {
77499
- adapter = name === NATIVE_AGENT ? new NativeAgentAdapter(undefined, config2.agent?.native?.catalogOverrides ?? []) : new AcpAgentAdapter(name);
77651
+ adapter = name === NATIVE_AGENT_NAME ? new NativeAgentAdapter(undefined, config2.agent?.native?.catalogOverrides ?? []) : new AcpAgentAdapter(name);
77500
77652
  adapterCache.set(name, adapter);
77501
77653
  logger?.debug("agents", `Created ${adapter.constructor.name} for ${name}`, { name });
77502
77654
  }
@@ -77530,10 +77682,11 @@ function createAgentRegistry(config2) {
77530
77682
  }
77531
77683
  var KNOWN_AGENT_NAMES, _registryTestAdapters;
77532
77684
  var init_registry4 = __esm(() => {
77685
+ init_config();
77533
77686
  init_logger2();
77534
77687
  init_adapter2();
77535
77688
  init_native();
77536
- KNOWN_AGENT_NAMES = ["claude", "codex", "opencode", "gemini", "aider", "pi", NATIVE_AGENT];
77689
+ KNOWN_AGENT_NAMES = ["claude", "codex", "opencode", "gemini", "aider", "pi", NATIVE_AGENT_NAME];
77537
77690
  _registryTestAdapters = new Map;
77538
77691
  });
77539
77692
 
@@ -77602,7 +77755,7 @@ class AgentManager {
77602
77755
  const fromAgent = this._config.agent?.default;
77603
77756
  if (typeof fromAgent === "string" && fromAgent.length > 0)
77604
77757
  return fromAgent;
77605
- return "claude";
77758
+ return DEFAULT_AGENT_NAME;
77606
77759
  }
77607
77760
  isUnavailable(agent, tier, model) {
77608
77761
  return this._cooldowns.isCooling(agent, tier, this._fallbackIdentity.modelId(agent, tier, model));
@@ -77992,6 +78145,7 @@ class AgentManager {
77992
78145
  }
77993
78146
  var MAX_EMITTER_LISTENERS = 100, _agentManagerDeps;
77994
78147
  var init_manager2 = __esm(() => {
78148
+ init_config();
77995
78149
  init_permissions2();
77996
78150
  init_errors();
77997
78151
  init_logger2();
@@ -78036,7 +78190,7 @@ function toAssignment(p, models, defaultAgent) {
78036
78190
  if (!membership.isTier) {
78037
78191
  return { agent: p.target.agent, agentProfileId: p.id, profileModelPin: p.target.model };
78038
78192
  }
78039
- if (membership.viaDefaultAgentFallback && p.target.agent === NATIVE_AGENT2 !== (defaultAgent === NATIVE_AGENT2)) {
78193
+ if (membership.viaDefaultAgentFallback && p.target.agent === NATIVE_AGENT !== (defaultAgent === NATIVE_AGENT)) {
78040
78194
  getSafeLogger()?.warn("routing", "Profile tier resolves only via the default agent across a protocol boundary", {
78041
78195
  profileId: p.id,
78042
78196
  agent: p.target.agent,
@@ -78046,7 +78200,7 @@ function toAssignment(p, models, defaultAgent) {
78046
78200
  }
78047
78201
  return { agent: p.target.agent, agentProfileId: p.id, profileModelTier: targetModel };
78048
78202
  }
78049
- var NATIVE_AGENT2 = "native";
78203
+ var NATIVE_AGENT = "native";
78050
78204
  var init_agent_profile_resolver = __esm(() => {
78051
78205
  init_config();
78052
78206
  init_logger2();
@@ -78146,7 +78300,11 @@ function resolveDefaultAgent(config2) {
78146
78300
  return fromAgent;
78147
78301
  return FALLBACK_DEFAULT_AGENT;
78148
78302
  }
78149
- var FALLBACK_DEFAULT_AGENT = "claude";
78303
+ var FALLBACK_DEFAULT_AGENT;
78304
+ var init_utils = __esm(() => {
78305
+ init_config();
78306
+ FALLBACK_DEFAULT_AGENT = DEFAULT_AGENT_NAME;
78307
+ });
78150
78308
 
78151
78309
  // src/agents/index.ts
78152
78310
  var init_agents = __esm(() => {
@@ -78161,20 +78319,24 @@ var init_agents = __esm(() => {
78161
78319
  init_shared();
78162
78320
  init_version_detection();
78163
78321
  init_types2();
78322
+ init_utils();
78164
78323
  });
78165
78324
 
78166
78325
  // src/cli/agents.ts
78167
78326
  async function agentsListCommand(config2, _workdir) {
78168
- const adapters = Array.from(ACP_ADAPTER_NAMES).map((name) => new AcpAgentAdapter(name));
78169
- const agentVersions = await Promise.all(adapters.map(async (agent) => ({
78327
+ const acpReachable = (config2.agent?.protocol ?? DEFAULT_AGENT_PROTOCOL) !== "native";
78328
+ const adapters = acpReachable ? Array.from(ACP_ADAPTER_NAMES).map((name) => new AcpAgentAdapter(name)) : [];
78329
+ const defaultAgent = resolveDefaultAgent(config2);
78330
+ const acpVersions = await Promise.all(adapters.map(async (agent) => ({
78170
78331
  name: agent.name,
78171
78332
  displayName: agent.displayName,
78172
78333
  binary: agent.binary,
78173
78334
  version: await _cliAgentsDeps.getAgentVersion(agent.binary),
78174
78335
  installed: await agent.isInstalled(),
78175
78336
  capabilities: agent.capabilities,
78176
- isDefault: resolveDefaultAgent(config2) === agent.name
78337
+ isDefault: defaultAgent === agent.name
78177
78338
  })));
78339
+ const agentVersions = [...nativeListing(config2, defaultAgent), ...acpVersions];
78178
78340
  const rows = agentVersions.map((info) => {
78179
78341
  const status = info.installed ? "installed" : "unavailable";
78180
78342
  const versionStr = info.version || "-";
@@ -78208,6 +78370,22 @@ Available Agents:
78208
78370
  }
78209
78371
  console.log();
78210
78372
  }
78373
+ function nativeListing(config2, defaultAgent) {
78374
+ if ((config2.agent?.protocol ?? DEFAULT_AGENT_PROTOCOL) === "acp")
78375
+ return [];
78376
+ const adapter = new NativeAgentAdapter(Object.keys(config2.models[NATIVE_AGENT_NAME] ?? {}));
78377
+ return [
78378
+ {
78379
+ name: adapter.name,
78380
+ displayName: adapter.displayName,
78381
+ binary: "in-process",
78382
+ version: "",
78383
+ installed: true,
78384
+ capabilities: adapter.capabilities,
78385
+ isDefault: defaultAgent === adapter.name
78386
+ }
78387
+ ];
78388
+ }
78211
78389
  function pad(str, width) {
78212
78390
  return str.padEnd(width);
78213
78391
  }
@@ -78215,7 +78393,9 @@ var _cliAgentsDeps;
78215
78393
  var init_agents2 = __esm(() => {
78216
78394
  init_agents();
78217
78395
  init_acp();
78396
+ init_native();
78218
78397
  init_version_detection();
78398
+ init_config();
78219
78399
  _cliAgentsDeps = { getAgentVersion };
78220
78400
  });
78221
78401
 
@@ -84723,6 +84903,14 @@ async function checkClaudeCLI() {
84723
84903
  }
84724
84904
  async function checkAgentCLI(config2) {
84725
84905
  const agent = resolveDefaultAgent(config2);
84906
+ if (agent === NATIVE_AGENT_NAME) {
84907
+ return {
84908
+ name: "agent-cli-available",
84909
+ tier: "blocker",
84910
+ passed: true,
84911
+ message: "native agent runs in-process; no CLI binary required"
84912
+ };
84913
+ }
84726
84914
  try {
84727
84915
  const proc = _checkCliDeps.spawn([agent, "--version"], {
84728
84916
  stdout: "pipe",
@@ -84748,6 +84936,7 @@ async function checkAgentCLI(config2) {
84748
84936
  var _checkCliDeps;
84749
84937
  var init_checks_cli = __esm(() => {
84750
84938
  init_agents();
84939
+ init_native();
84751
84940
  init_bun_deps();
84752
84941
  _checkCliDeps = {
84753
84942
  spawn
@@ -99298,8 +99487,26 @@ function warnInertBashStages(config2, logger) {
99298
99487
  logger?.warn("permissions", `bashApproval "${resolved}" on stage "${stage}" grants no Bash (no Bash(...) allow rule) -- the agent is not offered Bash, so nothing can escalate. Add one rule: "allow": ["Bash(ls *, cat *, git status*)"]`, { storyId: "_setup", stage, bashApproval: resolved });
99299
99488
  }
99300
99489
  }
99490
+ function warnUnreferencedAgentModels(prd, config2, logger) {
99491
+ const storyAgents = prd.userStories.flatMap((story) => story.routing?.agent ? [story.routing.agent] : []);
99492
+ const agents = findUnreferencedAgentModels(config2, storyAgents);
99493
+ if (agents.length === 0)
99494
+ return;
99495
+ logger?.warn("config", describeUnreferencedAgentModels(agents, config2), { storyId: "_setup", agents });
99496
+ }
99497
+ async function assertDefaultNativeCredentials(config2) {
99498
+ const { describeMissingNativeCredentials, findMissingNativeCredentials } = await Promise.resolve().then(() => (init_precheck2(), exports_precheck));
99499
+ const missing = await findMissingNativeCredentials(config2);
99500
+ if (missing.length === 0)
99501
+ return;
99502
+ throw new NaxError(describeMissingNativeCredentials(missing), "NATIVE_CREDENTIALS_MISSING", {
99503
+ stage: "setup",
99504
+ providers: missing.map(({ provider: provider2 }) => provider2)
99505
+ });
99506
+ }
99301
99507
  var init_run_setup_warnings = __esm(() => {
99302
99508
  init_config();
99509
+ init_errors();
99303
99510
  });
99304
99511
 
99305
99512
  // src/execution/lifecycle/gitignore-reconcile.ts
@@ -100116,6 +100323,7 @@ async function initializeAfterLock(options) {
100116
100323
  const prd = initResult.prd;
100117
100324
  statusWriter.setPrd(prd);
100118
100325
  warnProfileMismatch(prd, config2, logger);
100326
+ warnUnreferencedAgentModels(prd, config2, logger);
100119
100327
  let counts = initResult.storyCounts;
100120
100328
  if (counts.paused > 0 && interactionChain !== null) {
100121
100329
  const { promptForPausedStories: promptForPausedStories2 } = await Promise.resolve().then(() => (init_paused_story_prompts(), exports_paused_story_prompts));
@@ -102466,6 +102674,9 @@ async function setupRun(options) {
102466
102674
  if (options.agentManager) {
102467
102675
  await options.agentManager.validateCredentials();
102468
102676
  }
102677
+ if (!options.dryRun) {
102678
+ await assertDefaultNativeCredentials(options.config);
102679
+ }
102469
102680
  const {
102470
102681
  prdPath,
102471
102682
  workdir,
@@ -109108,7 +109319,7 @@ function collectConfiguredModelPins(config2) {
109108
109319
  }
109109
109320
  }
109110
109321
  const pins = [];
109111
- const defaultAgent = cfg.agent?.default ?? "claude";
109322
+ const defaultAgent = cfg.agent?.default ?? DEFAULT_AGENT_NAME;
109112
109323
  if (cfg.review?.semantic !== undefined) {
109113
109324
  const pin = pinFromConfiguredModel("review.semantic.model", cfg.review.semantic.model, defaultAgent, tierEntries);
109114
109325
  if (pin !== undefined)
@@ -109197,7 +109408,7 @@ var init_checks_model_resolution_walk = __esm(() => {
109197
109408
 
109198
109409
  // src/precheck/checks-model-resolution.ts
109199
109410
  function isNativeAgent(agent) {
109200
- return agent === "native";
109411
+ return agent === NATIVE_AGENT_NAME;
109201
109412
  }
109202
109413
  async function checkModelResolution(config2) {
109203
109414
  const { pins, tierEntries, catalogOverrides } = collectConfiguredModelPins(config2);
@@ -109306,6 +109517,52 @@ var init_checks_model_resolution = __esm(() => {
109306
109517
  };
109307
109518
  });
109308
109519
 
109520
+ // src/precheck/checks-native-credentials.ts
109521
+ function providerOf(id) {
109522
+ const slash = id.indexOf("/");
109523
+ return slash > 0 ? id.slice(0, slash) : undefined;
109524
+ }
109525
+ function providerTiers(config2) {
109526
+ const overridden = new Set((config2.agent?.native?.catalogOverrides ?? []).map((override) => override.provider));
109527
+ const byProvider = new Map;
109528
+ for (const [tier, entry] of Object.entries(config2.models?.[NATIVE_AGENT_NAME] ?? {})) {
109529
+ if (entry === undefined)
109530
+ continue;
109531
+ const provider2 = providerOf(typeof entry === "string" ? entry : entry.model);
109532
+ if (provider2 === undefined || overridden.has(provider2))
109533
+ continue;
109534
+ byProvider.set(provider2, [...byProvider.get(provider2) ?? [], tier]);
109535
+ }
109536
+ return byProvider;
109537
+ }
109538
+ async function findMissingNativeCredentials(config2) {
109539
+ if (resolveDefaultAgent(config2) !== NATIVE_AGENT_NAME)
109540
+ return [];
109541
+ const byProvider = providerTiers(config2);
109542
+ if (byProvider.size === 0)
109543
+ return [];
109544
+ const missing = await _nativeCredentialDeps.providersWithoutCredentials([...byProvider.keys()]);
109545
+ return missing.map((provider2) => ({ provider: provider2, tiers: byProvider.get(provider2) ?? [] }));
109546
+ }
109547
+ function describeMissingNativeCredentials(missing) {
109548
+ const uses = missing.map(({ provider: provider2, tiers }) => `${provider2} (${tiers.map((tier) => `models.native.${tier}`).join(", ")})`).join("; ");
109549
+ const logins = missing.map(({ provider: provider2 }) => `nax auth login ${provider2}`).join(" / ");
109550
+ return `The default native agent needs a credential for ${uses}, but none is stored or in the environment. ` + `Run ${logins}, set the provider's API key environment variable, point models.native at a provider you have ` + `credentials for, or set agent.default "claude" to use an acpx agent.`;
109551
+ }
109552
+ async function checkNativeCredentials(config2) {
109553
+ const missing = await findMissingNativeCredentials(config2);
109554
+ if (missing.length === 0) {
109555
+ return { name: CHECK_NAME, tier: "blocker", passed: true, message: "native default agent credentials found" };
109556
+ }
109557
+ return { name: CHECK_NAME, tier: "blocker", passed: false, message: describeMissingNativeCredentials(missing) };
109558
+ }
109559
+ var CHECK_NAME = "native-credentials", _nativeCredentialDeps;
109560
+ var init_checks_native_credentials = __esm(() => {
109561
+ init_agents();
109562
+ init_native();
109563
+ _nativeCredentialDeps = { providersWithoutCredentials };
109564
+ });
109565
+
109309
109566
  // src/precheck/checks-warnings.ts
109310
109567
  import { existsSync as existsSync37 } from "fs";
109311
109568
  import { isAbsolute as isAbsolute22 } from "path";
@@ -109614,6 +109871,7 @@ var init_checks4 = __esm(() => {
109614
109871
  init_checks_agents();
109615
109872
  init_checks_blockers();
109616
109873
  init_checks_model_resolution();
109874
+ init_checks_native_credentials();
109617
109875
  init_checks_warnings();
109618
109876
  });
109619
109877
 
@@ -109742,6 +110000,7 @@ __export(exports_precheck, {
109742
110000
  EXIT_CODES: () => EXIT_CODES,
109743
110001
  _checkDiskSpaceDeps: () => _checkDiskSpaceDeps,
109744
110002
  _modelResolutionDeps: () => _modelResolutionDeps,
110003
+ _nativeCredentialDeps: () => _nativeCredentialDeps,
109745
110004
  _precheckDeps: () => _precheckDeps,
109746
110005
  checkAgentCLI: () => checkAgentCLI,
109747
110006
  checkBuildCommandInReviewChecks: () => checkBuildCommandInReviewChecks,
@@ -109758,6 +110017,7 @@ __export(exports_precheck, {
109758
110017
  checkLintCommand: () => checkLintCommand,
109759
110018
  checkModelResolution: () => checkModelResolution,
109760
110019
  checkMultiAgentHealth: () => checkMultiAgentHealth,
110020
+ checkNativeCredentials: () => checkNativeCredentials,
109761
110021
  checkOptionalCommands: () => checkOptionalCommands,
109762
110022
  checkPRDValid: () => checkPRDValid,
109763
110023
  checkPendingStories: () => checkPendingStories,
@@ -109766,6 +110026,8 @@ __export(exports_precheck, {
109766
110026
  checkTestCommand: () => checkTestCommand,
109767
110027
  checkTypecheckCommand: () => checkTypecheckCommand,
109768
110028
  checkWorkingTreeClean: () => checkWorkingTreeClean,
110029
+ describeMissingNativeCredentials: () => describeMissingNativeCredentials,
110030
+ findMissingNativeCredentials: () => findMissingNativeCredentials,
109769
110031
  parseDiskSpaceOutput: () => parseDiskSpaceOutput,
109770
110032
  runEnvironmentPrecheck: () => runEnvironmentPrecheck,
109771
110033
  runPrecheck: () => runPrecheck2
@@ -109781,6 +110043,7 @@ function getLateEnvironmentBlockers(config2, workdir) {
109781
110043
  return [
109782
110044
  () => checkAgentCLI(config2),
109783
110045
  () => checkModelResolution(config2),
110046
+ () => checkNativeCredentials(config2),
109784
110047
  () => checkDependenciesInstalled(workdir),
109785
110048
  () => checkTestCommand(config2),
109786
110049
  () => checkLintCommand(config2),
@@ -110170,7 +110433,7 @@ ${repairHint}` : codebaseContext;
110170
110433
  agentRouting: config2.routing?.agents,
110171
110434
  profileName: config2.profile,
110172
110435
  models: config2.models,
110173
- defaultAgent: config2.agent?.default ?? "claude",
110436
+ defaultAgent: config2.agent?.default ?? DEFAULT_AGENT_NAME,
110174
110437
  outputPath: prdPath,
110175
110438
  repoRoot: workdir,
110176
110439
  scope: new Set(subStoriesWithParent.map((s) => s.id)),
@@ -110494,7 +110757,7 @@ async function persistPrd(ctx, prd) {
110494
110757
  agentRouting: ctx.config.routing?.agents,
110495
110758
  profileName: ctx.profileName,
110496
110759
  models: ctx.config.models,
110497
- defaultAgent: ctx.config.agent?.default ?? "claude",
110760
+ defaultAgent: ctx.config.agent?.default ?? DEFAULT_AGENT_NAME,
110498
110761
  outputPath: ctx.outputPath,
110499
110762
  repoRoot: ctx.workdir,
110500
110763
  writeFile: ctx.deps.writeFile
@@ -110502,6 +110765,7 @@ async function persistPrd(ctx, prd) {
110502
110765
  }
110503
110766
  var _persistPrdDeps;
110504
110767
  var init_persist_prd = __esm(() => {
110768
+ init_config();
110505
110769
  init_generator2();
110506
110770
  init_logger2();
110507
110771
  init_operations();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.82.0",
3
+ "version": "0.82.1",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {