@nathapp/nax 0.83.5 → 0.83.6

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 +11 -12
  2. package/dist/nax.js +887 -2693
  3. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -29718,7 +29718,7 @@ function isBuiltInModelMap(agent, entry) {
29718
29718
  const tiers = Object.entries(entry);
29719
29719
  return tiers.length === Object.keys(builtIn).length && tiers.every(([tier, value]) => Object.hasOwn(builtIn, tier) && builtIn[tier] === value);
29720
29720
  }
29721
- var DEFAULT_AGENT_PROTOCOL = "hybrid", DEFAULT_AGENT_NAME = "native", DEFAULT_ACP_TRANSPORT = "sdk", DEFAULT_MODEL_MAPS;
29721
+ var DEFAULT_AGENT_PROTOCOL = "hybrid", DEFAULT_AGENT_NAME = "native", DEFAULT_MODEL_MAPS;
29722
29722
  var init_agent_defaults = __esm(() => {
29723
29723
  init_internal();
29724
29724
  DEFAULT_MODEL_MAPS = {
@@ -31247,6 +31247,15 @@ function _applyRemovedWorktreeInheritShim(conf, warn = defaultConfigWarn) {
31247
31247
  execution: { ...execution, worktreeDependencies: { ...worktreeDependencies, mode: "off" } }
31248
31248
  };
31249
31249
  }
31250
+ function _applyRemovedAcpTransportShim(conf, warn = defaultConfigWarn) {
31251
+ const agent = conf.agent;
31252
+ const acp = agent?.acp;
31253
+ if (acp === undefined || !Object.hasOwn(acp, "transport"))
31254
+ return conf;
31255
+ const { transport, ...rest } = acp;
31256
+ warn(transport === "acpx" ? 'agent.acp.transport "acpx" was removed: the acpx CLI transport no longer exists, and ACP agents run through ' + "@nathapp/nax-agent-acp (each agent's ACP launcher must be installed). Remove the key from your config." : "agent.acp.transport was removed and has no effect: ACP agents always run through @nathapp/nax-agent-acp. " + "Remove the key from your config.");
31257
+ return { ...conf, agent: { ...agent, acp: rest } };
31258
+ }
31250
31259
  function applyBatchModeCompat(conf, warn = defaultConfigWarn) {
31251
31260
  const routing = conf.routing;
31252
31261
  const llm = routing?.llm;
@@ -31373,6 +31382,7 @@ function applyConfigCompatShims(conf, logger, dedupe) {
31373
31382
  out = _applyRemovedRoutingKeysShim(out, warn);
31374
31383
  out = _applyLegacyReviewExecutionShim(out, warn);
31375
31384
  out = _applyRemovedWorktreeInheritShim(out, warn);
31385
+ out = _applyRemovedAcpTransportShim(out, warn);
31376
31386
  out = _applyFinishAutoFlowShim(out, warn);
31377
31387
  out = _applyRemovedOptimizerKeysShim(out, warn);
31378
31388
  out = _applyRemovedCrossPackageDepthShim(out, warn);
@@ -33130,7 +33140,6 @@ var init_schemas_infra = __esm(() => {
33130
33140
  path: ["stopAfterRepeats"]
33131
33141
  });
33132
33142
  AgentAcpConfigSchema = exports_external.object({
33133
- transport: exports_external.enum(["acpx", "sdk"]).default(DEFAULT_ACP_TRANSPORT),
33134
33143
  promptRetries: exports_external.number().int().min(0).max(5).default(0),
33135
33144
  trackedSpawnDeadlineMs: exports_external.number().int().positive().default(1e4),
33136
33145
  trackedSpawnStartupDeadlineMs: exports_external.number().int().positive().default(30000)
@@ -33166,7 +33175,6 @@ var init_schemas_infra = __esm(() => {
33166
33175
  rebuildContext: true
33167
33176
  }),
33168
33177
  acp: AgentAcpConfigSchema.default({
33169
- transport: DEFAULT_ACP_TRANSPORT,
33170
33178
  promptRetries: 0,
33171
33179
  trackedSpawnDeadlineMs: 1e4,
33172
33180
  trackedSpawnStartupDeadlineMs: 30000
@@ -33254,7 +33262,7 @@ function validateProtocolGate(data, ctx) {
33254
33262
  ctx.addIssue({
33255
33263
  code: "custom",
33256
33264
  path: ["agent", "default"],
33257
- 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".'
33265
+ 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 ACP agent such as "claude", or use agent.protocol "hybrid".'
33258
33266
  });
33259
33267
  }
33260
33268
  if (protocol === "acp" && modelAgents.includes(NATIVE_AGENT_NAME)) {
@@ -33271,7 +33279,7 @@ function validateProtocolGate(data, ctx) {
33271
33279
  ctx.addIssue({
33272
33280
  code: "custom",
33273
33281
  path: ["models", agent],
33274
- message: `agent.protocol "native" permits only models.native; "${agent}" is an acpx agent. Use "hybrid" to run both.`
33282
+ message: `agent.protocol "native" permits only models.native; "${agent}" is an ACP agent. Use "hybrid" to run both.`
33275
33283
  });
33276
33284
  }
33277
33285
  if ((data.agent?.default ?? DEFAULT_AGENT_NAME) !== NATIVE_AGENT_NAME) {
@@ -33759,7 +33767,6 @@ var init_schemas3 = __esm(() => {
33759
33767
  usageAudit: { enabled: false },
33760
33768
  fallback: { enabled: false, map: {}, maxHopsPerStory: 2, onQualityFailure: false, rebuildContext: true },
33761
33769
  acp: {
33762
- transport: DEFAULT_ACP_TRANSPORT,
33763
33770
  promptRetries: 0,
33764
33771
  trackedSpawnDeadlineMs: 1e4,
33765
33772
  trackedSpawnStartupDeadlineMs: 30000
@@ -34500,7 +34507,7 @@ function describeUnreferencedAgentModels(agents, config2) {
34500
34507
  const maps = agents.map((agent) => `models.${agent}`).join(", ");
34501
34508
  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(", ")}.`;
34502
34509
  if (config2.agent?.protocol === "native") {
34503
- return `${head} Under agent.protocol "native" acpx agents cannot run; remove the map or use protocol "hybrid".`;
34510
+ return `${head} Under agent.protocol "native" ACP agents cannot run; remove the map or use protocol "hybrid".`;
34504
34511
  }
34505
34512
  const defaultAgent = config2.agent?.default ?? DEFAULT_AGENT_NAME;
34506
34513
  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.`;
@@ -34712,7 +34719,6 @@ __export(exports_config, {
34712
34719
  ContextConfigSchema: () => ContextConfigSchema,
34713
34720
  ContextV2ConfigSchema: () => ContextV2ConfigSchema,
34714
34721
  CuratorRetentionConfigSchema: () => CuratorRetentionConfigSchema,
34715
- DEFAULT_ACP_TRANSPORT: () => DEFAULT_ACP_TRANSPORT,
34716
34722
  DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG: () => DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG,
34717
34723
  DEFAULT_AGENT_NAME: () => DEFAULT_AGENT_NAME,
34718
34724
  DEFAULT_AGENT_PROTOCOL: () => DEFAULT_AGENT_PROTOCOL,
@@ -38137,41 +38143,341 @@ var init_accept = __esm(() => {
38137
38143
  init_prd();
38138
38144
  });
38139
38145
 
38140
- // src/agents/turn/abort.ts
38141
- function createAbortError(signal, fallback = "Run aborted") {
38142
- const reason = signal?.reason;
38143
- if (reason instanceof Error) {
38144
- return reason;
38146
+ // src/agents/errors/parse-agent-error.ts
38147
+ function parseAgentError(stderr) {
38148
+ if (!stderr) {
38149
+ return { type: "unknown" };
38145
38150
  }
38146
- if (typeof reason === "string" && reason.length > 0) {
38147
- return new Error(reason);
38151
+ const parsedJson = parseJsonObject(stderr);
38152
+ if (parsedJson) {
38153
+ const classified = classifyJsonPayload(parsedJson);
38154
+ if (classified)
38155
+ return classified;
38148
38156
  }
38149
- return new Error(fallback);
38157
+ if (!parsedJson) {
38158
+ for (const embedded of extractEmbeddedJsonObjects(stderr)) {
38159
+ const classified = classifyJsonPayload(embedded);
38160
+ if (classified)
38161
+ return classified;
38162
+ }
38163
+ }
38164
+ const fromKeyValue = classifyFromCodeTokens(extractKeyValueCodes(stderr));
38165
+ if (fromKeyValue)
38166
+ return fromKeyValue;
38167
+ const flatModel = classifyModelErrorMessage(stderr);
38168
+ if (flatModel)
38169
+ return flatModel;
38170
+ return { type: "unknown" };
38150
38171
  }
38151
- function throwIfAborted(signal, fallback) {
38152
- if (signal?.aborted) {
38153
- throw createAbortError(signal, fallback);
38172
+ function classifyJsonPayload(payload) {
38173
+ const direct = classifyDirectType(payload);
38174
+ if (direct)
38175
+ return direct;
38176
+ const nested = classifyNestedAnthropicError(payload);
38177
+ if (nested)
38178
+ return nested;
38179
+ const jsonRpcModel = classifyJsonRpcModelError(payload);
38180
+ if (jsonRpcModel)
38181
+ return jsonRpcModel;
38182
+ const structured = classifyFromCodeTokens(extractJsonCodeTokens(payload), payload);
38183
+ if (structured)
38184
+ return structured;
38185
+ return null;
38186
+ }
38187
+ function classifyNestedAnthropicError(payload) {
38188
+ if (payload.type !== "error")
38189
+ return null;
38190
+ const inner = payload.error;
38191
+ if (!inner || typeof inner !== "object")
38192
+ return null;
38193
+ const innerType = inner.type;
38194
+ if (typeof innerType !== "string")
38195
+ return null;
38196
+ switch (innerType) {
38197
+ case "authentication_error":
38198
+ case "permission_error":
38199
+ case "invalid_api_key_error":
38200
+ return { type: "auth" };
38201
+ case "rate_limit_error":
38202
+ case "overloaded_error": {
38203
+ const retryAfterSeconds = toNumber(inner.retryAfterSeconds ?? inner.retry_after_seconds ?? payload.retryAfterSeconds ?? payload.retry_after_seconds);
38204
+ return retryAfterSeconds !== undefined ? { type: "rate-limit", retryAfterSeconds } : { type: "rate-limit" };
38205
+ }
38206
+ default:
38207
+ return null;
38154
38208
  }
38155
38209
  }
38156
- async function raceWithAbort(promise2, signal, fallback) {
38157
- if (!signal) {
38158
- return promise2;
38210
+ function extractEmbeddedJsonObjects(text) {
38211
+ const objects = [];
38212
+ let i = 0;
38213
+ while (i < text.length && objects.length < MAX_EMBEDDED_CANDIDATES) {
38214
+ const open3 = text.indexOf("{", i);
38215
+ if (open3 === -1)
38216
+ break;
38217
+ const close = findMatchingBrace(text, open3);
38218
+ if (close === -1)
38219
+ break;
38220
+ const candidate = text.slice(open3, close + 1);
38221
+ try {
38222
+ const parsed = JSON.parse(candidate);
38223
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
38224
+ objects.push(parsed);
38225
+ }
38226
+ } catch {}
38227
+ i = close + 1;
38159
38228
  }
38160
- if (signal.aborted) {
38161
- throw createAbortError(signal, fallback);
38229
+ return objects;
38230
+ }
38231
+ function findMatchingBrace(text, openIdx) {
38232
+ let depth = 0;
38233
+ let inString = false;
38234
+ let escaped = false;
38235
+ for (let i = openIdx;i < text.length; i++) {
38236
+ const ch = text[i];
38237
+ if (escaped) {
38238
+ escaped = false;
38239
+ continue;
38240
+ }
38241
+ if (ch === "\\" && inString) {
38242
+ escaped = true;
38243
+ continue;
38244
+ }
38245
+ if (ch === '"') {
38246
+ inString = !inString;
38247
+ continue;
38248
+ }
38249
+ if (inString)
38250
+ continue;
38251
+ if (ch === "{")
38252
+ depth++;
38253
+ else if (ch === "}") {
38254
+ depth--;
38255
+ if (depth === 0)
38256
+ return i;
38257
+ }
38162
38258
  }
38163
- return await new Promise((resolve21, reject) => {
38164
- const onAbort = () => reject(createAbortError(signal, fallback));
38165
- signal.addEventListener("abort", onAbort, { once: true });
38166
- promise2.then((value) => {
38167
- signal.removeEventListener("abort", onAbort);
38168
- resolve21(value);
38169
- }, (error48) => {
38170
- signal.removeEventListener("abort", onAbort);
38171
- reject(error48);
38172
- });
38173
- });
38259
+ return -1;
38174
38260
  }
38261
+ function parseJsonObject(stderr) {
38262
+ try {
38263
+ const value = JSON.parse(stderr);
38264
+ if (value && typeof value === "object" && !Array.isArray(value)) {
38265
+ return value;
38266
+ }
38267
+ return null;
38268
+ } catch {
38269
+ return null;
38270
+ }
38271
+ }
38272
+ function classifyJsonRpcModelError(payload) {
38273
+ const errObj = payload.error;
38274
+ if (!errObj || typeof errObj !== "object")
38275
+ return null;
38276
+ const message = errObj.message;
38277
+ if (typeof message !== "string")
38278
+ return null;
38279
+ return classifyModelErrorMessage(message);
38280
+ }
38281
+ function classifyModelErrorMessage(message) {
38282
+ if (message.includes("There's an issue with the selected model") && message.includes("Run --model")) {
38283
+ return { type: "model-not-available" };
38284
+ }
38285
+ return null;
38286
+ }
38287
+ function classifyDirectType(payload) {
38288
+ const type = payload.type;
38289
+ if (type !== "rate-limit" && type !== "auth" && type !== "timeout" && type !== "crash" && type !== "unknown") {
38290
+ return null;
38291
+ }
38292
+ const result = { type };
38293
+ if (type === "rate-limit") {
38294
+ const retryAfterSeconds = toNumber(payload.retryAfterSeconds ?? payload.retry_after_seconds ?? payload.retryAfter);
38295
+ if (retryAfterSeconds !== undefined) {
38296
+ result.retryAfterSeconds = retryAfterSeconds;
38297
+ }
38298
+ }
38299
+ return result;
38300
+ }
38301
+ function extractJsonCodeTokens(payload) {
38302
+ const tokens = [];
38303
+ const pushToken = (candidate) => {
38304
+ if (typeof candidate === "number" && Number.isFinite(candidate)) {
38305
+ tokens.push(String(candidate));
38306
+ } else if (typeof candidate === "string" && candidate.trim()) {
38307
+ tokens.push(candidate.trim());
38308
+ }
38309
+ };
38310
+ const MAX_WALK_DEPTH = 8;
38311
+ const walk = (obj, depth) => {
38312
+ if (depth > MAX_WALK_DEPTH)
38313
+ return;
38314
+ const candidates = [obj.code, obj.status, obj.statusCode, obj.httpStatus, obj.errorCode, obj.detailCode];
38315
+ for (const candidate of candidates)
38316
+ pushToken(candidate);
38317
+ for (const key of ["error", "data"]) {
38318
+ const nested = obj[key];
38319
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
38320
+ walk(nested, depth + 1);
38321
+ }
38322
+ }
38323
+ };
38324
+ walk(payload, 0);
38325
+ return tokens;
38326
+ }
38327
+ function extractKeyValueCodes(stderr) {
38328
+ const tokens = [];
38329
+ const patterns = [
38330
+ /(?:status|statusCode|httpStatus|code)\s*[:=]\s*(\d{3})/g,
38331
+ /(?:detailCode|errorCode)\s*[:=]\s*([A-Z0-9_]+)/g
38332
+ ];
38333
+ for (const pattern of patterns) {
38334
+ while (true) {
38335
+ const match = pattern.exec(stderr);
38336
+ if (!match)
38337
+ break;
38338
+ const code = match[1];
38339
+ if (code)
38340
+ tokens.push(code);
38341
+ }
38342
+ }
38343
+ return tokens;
38344
+ }
38345
+ function classifyFromCodeTokens(tokens, payload) {
38346
+ for (const token of tokens.map((value) => value.toUpperCase())) {
38347
+ if (token === "429" || token === "TOO_MANY_REQUESTS" || token === "RATE_LIMIT" || token === "RATE_LIMITED" || token === "QUOTA_EXCEEDED" || token === "RESOURCE_EXHAUSTED") {
38348
+ const retryAfterSeconds = payload ? findRetryAfterSeconds(payload) : undefined;
38349
+ return retryAfterSeconds !== undefined ? { type: "rate-limit", retryAfterSeconds } : { type: "rate-limit" };
38350
+ }
38351
+ if (token === "401" || token === "403" || token === "UNAUTHORIZED" || token === "FORBIDDEN" || token === "AUTH_FAILED" || token === "AUTHENTICATION_FAILED" || token === "PERMISSION_DENIED") {
38352
+ return { type: "auth" };
38353
+ }
38354
+ }
38355
+ return null;
38356
+ }
38357
+ function toNumber(value) {
38358
+ if (typeof value === "number" && Number.isFinite(value))
38359
+ return Math.trunc(value);
38360
+ if (typeof value === "string" && value.trim()) {
38361
+ const parsed = Number.parseInt(value, 10);
38362
+ if (Number.isFinite(parsed))
38363
+ return parsed;
38364
+ }
38365
+ return;
38366
+ }
38367
+ function findRetryAfterSeconds(payload) {
38368
+ const topLevel = toNumber(payload.retryAfterSeconds ?? payload.retry_after_seconds ?? payload.retryAfter);
38369
+ if (topLevel !== undefined)
38370
+ return topLevel;
38371
+ const error48 = payload.error;
38372
+ if (error48 && typeof error48 === "object" && !Array.isArray(error48)) {
38373
+ const errObj = error48;
38374
+ const inError = toNumber(errObj.retryAfterSeconds ?? errObj.retry_after_seconds ?? errObj.retryAfter);
38375
+ if (inError !== undefined)
38376
+ return inError;
38377
+ const data = errObj.data;
38378
+ if (data && typeof data === "object" && !Array.isArray(data)) {
38379
+ const dataObj = data;
38380
+ return toNumber(dataObj.retryAfterSeconds ?? dataObj.retry_after_seconds ?? dataObj.retryAfter);
38381
+ }
38382
+ }
38383
+ return;
38384
+ }
38385
+ var MAX_EMBEDDED_CANDIDATES = 8;
38386
+
38387
+ // src/agents/errors/index.ts
38388
+ var init_errors5 = () => {};
38389
+
38390
+ // src/agents/transport-failure-message.ts
38391
+ function isTransportFailureMessage(message) {
38392
+ if (!message)
38393
+ return false;
38394
+ const haystack = message.toLowerCase();
38395
+ return TRANSPORT_FAULT_MARKERS.some((marker) => haystack.includes(marker));
38396
+ }
38397
+ var TRANSPORT_FAULT_MARKERS;
38398
+ var init_transport_failure_message = __esm(() => {
38399
+ TRANSPORT_FAULT_MARKERS = [
38400
+ "idle timeout exceeded",
38401
+ "stream idle timeout",
38402
+ "upstream timeout",
38403
+ "bad gateway",
38404
+ "service unavailable",
38405
+ "gateway timeout",
38406
+ "econnreset",
38407
+ "econnrefused",
38408
+ "econnaborted",
38409
+ "etimedout",
38410
+ "socket hang up",
38411
+ "premature close",
38412
+ "terminated: other side closed",
38413
+ "network error"
38414
+ ];
38415
+ });
38416
+
38417
+ // src/agents/types.ts
38418
+ var CompleteError, SessionFailureError;
38419
+ var init_types4 = __esm(() => {
38420
+ init_src();
38421
+ CompleteError = class CompleteError extends Error {
38422
+ exitCode;
38423
+ retryable;
38424
+ constructor(message, exitCode, retryable) {
38425
+ super(message);
38426
+ this.exitCode = exitCode;
38427
+ this.retryable = retryable;
38428
+ this.name = "CompleteError";
38429
+ }
38430
+ };
38431
+ SessionFailureError = class SessionFailureError extends Error {
38432
+ adapterFailure;
38433
+ constructor(message, adapterFailure) {
38434
+ super(message);
38435
+ this.adapterFailure = adapterFailure;
38436
+ this.name = "SessionFailureError";
38437
+ }
38438
+ };
38439
+ });
38440
+
38441
+ // src/agents/complete-exception-classifier.ts
38442
+ function classifyCompleteException(err) {
38443
+ if ((err instanceof SessionTurnError || err instanceof SessionFailureError) && err.adapterFailure !== undefined) {
38444
+ return err.adapterFailure;
38445
+ }
38446
+ const fullMessage = errorMessage(err);
38447
+ const message = fullMessage.slice(0, MAX_FAILURE_MESSAGE_CHARS);
38448
+ if (err instanceof NaxError && err.code === "AGENT_TIMEOUT") {
38449
+ return { category: "quality", outcome: "fail-timeout", retriable: true, message };
38450
+ }
38451
+ const parsed = parseAgentError(fullMessage);
38452
+ switch (parsed.type) {
38453
+ case "auth":
38454
+ return { category: "availability", outcome: "fail-auth", retriable: false, message };
38455
+ case "rate-limit":
38456
+ return {
38457
+ category: "availability",
38458
+ outcome: "fail-rate-limit",
38459
+ retriable: true,
38460
+ message,
38461
+ ...parsed.retryAfterSeconds !== undefined ? { retryAfterSeconds: parsed.retryAfterSeconds } : {}
38462
+ };
38463
+ case "model-not-available":
38464
+ return { category: "quality", outcome: "fail-adapter-error", retriable: false, message };
38465
+ case "timeout":
38466
+ return { category: "quality", outcome: "fail-timeout", retriable: true, message };
38467
+ case "crash":
38468
+ return { category: "quality", outcome: "fail-adapter-error", retriable: false, message };
38469
+ default:
38470
+ return isTransportFailureMessage(fullMessage) ? { category: "availability", outcome: "fail-service-down", retriable: true, message } : { category: "quality", outcome: "fail-unknown", retriable: false, message };
38471
+ }
38472
+ }
38473
+ var MAX_FAILURE_MESSAGE_CHARS = 500;
38474
+ var init_complete_exception_classifier = __esm(() => {
38475
+ init_internal();
38476
+ init_errors4();
38477
+ init_errors5();
38478
+ init_transport_failure_message();
38479
+ init_types4();
38480
+ });
38175
38481
 
38176
38482
  // src/agents/cost/calculate.ts
38177
38483
  function resolvePricingSource(model) {
@@ -38188,7 +38494,7 @@ var package_default;
38188
38494
  var init_package = __esm(() => {
38189
38495
  package_default = {
38190
38496
  name: "@nathapp/nax",
38191
- version: "0.83.5",
38497
+ version: "0.83.6",
38192
38498
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
38193
38499
  type: "module",
38194
38500
  bin: {
@@ -38482,2404 +38788,33 @@ var init_cost = __esm(() => {
38482
38788
  init_rate_card();
38483
38789
  });
38484
38790
 
38485
- // src/agents/turn/turn-deadline.ts
38486
- function assembleTurnResult(input) {
38487
- const { totalTokenUsage, totalExactCostUsd, turnCount, interactions, timedOut, rateCard } = input;
38488
- const output = timedOut ? "" : input.output;
38489
- const hasUsage = totalTokenUsage.inputTokens > 0 || totalTokenUsage.outputTokens > 0;
38490
- const priced = hasUsage ? priceCall(totalTokenUsage, rateCard.rates) : undefined;
38491
- return {
38492
- output,
38493
- tokenUsage: totalTokenUsage,
38494
- estimatedCostUsd: priced?.costUsd ?? 0,
38495
- exactCostUsd: totalExactCostUsd,
38496
- internalRoundTrips: turnCount,
38497
- ...interactions.length > 0 ? { interactions } : {},
38498
- timedOut,
38499
- pricingSource: rateCard.source,
38500
- ...priced?.resolvedRates !== undefined ? { rates: priced.resolvedRates } : {}
38501
- };
38502
- }
38503
- function warnWallClockTimeout(sessionName, timeoutSeconds, stage) {
38504
- getSafeLogger2()?.warn(stage, "wall-clock timeout exceeded \u2014 session terminated", {
38505
- sessionName,
38506
- timeoutSeconds
38507
- });
38508
- }
38509
- var init_turn_deadline2 = __esm(() => {
38510
- init_logger2();
38511
- init_cost();
38512
- });
38513
-
38514
- // src/agents/turn/index.ts
38515
- var init_turn = __esm(() => {
38516
- init_turn_deadline2();
38517
- });
38518
-
38519
- // src/utils/bun-deps.ts
38520
- function typedSpawn(cmd, opts) {
38521
- return Bun.spawn(cmd, opts);
38522
- }
38523
- function which2(name) {
38524
- return Bun.which(name);
38525
- }
38526
- function sleep(ms) {
38527
- return Bun.sleep(ms);
38528
- }
38529
- function cancellableDelay(ms, signal) {
38530
- return new Promise((resolve21, reject) => {
38531
- if (signal?.aborted) {
38532
- reject(signal.reason ?? new Error("delay aborted"));
38533
- return;
38534
- }
38535
- const timer = setTimeout(() => {
38536
- signal?.removeEventListener("abort", onAbort);
38537
- resolve21();
38538
- }, ms);
38539
- const onAbort = () => {
38540
- clearTimeout(timer);
38541
- reject(signal?.reason ?? new Error("delay aborted"));
38542
- };
38543
- signal?.addEventListener("abort", onAbort, { once: true });
38544
- });
38545
- }
38546
- var spawn;
38547
- var init_bun_deps = __esm(() => {
38548
- spawn = Bun.spawn;
38549
- });
38550
-
38551
- // src/agents/errors/parse-agent-error.ts
38552
- function classifyCompleteError(error48, pricingSource) {
38553
- if (error48.retryable === undefined)
38554
- return null;
38555
- return {
38556
- output: error48.message,
38557
- tokenUsage: { inputTokens: 0, outputTokens: 0 },
38558
- estimatedCostUsd: 0,
38559
- adapterFailure: {
38560
- category: "quality",
38561
- outcome: "fail-adapter-error",
38562
- retriable: error48.retryable,
38563
- message: error48.message.slice(0, 500)
38564
- },
38565
- ...pricingSource !== undefined ? { pricingSource } : {}
38566
- };
38567
- }
38568
- function classifyParsedAgentError(parsed, message, pricingSource) {
38569
- const base = {
38570
- output: message,
38571
- tokenUsage: { inputTokens: 0, outputTokens: 0 },
38572
- estimatedCostUsd: 0,
38573
- ...pricingSource !== undefined ? { pricingSource } : {}
38574
- };
38575
- const truncated = message.slice(0, 500);
38576
- switch (parsed.type) {
38577
- case "auth":
38578
- return {
38579
- ...base,
38580
- adapterFailure: { category: "availability", outcome: "fail-auth", retriable: false, message: truncated }
38581
- };
38582
- case "rate-limit":
38583
- return {
38584
- ...base,
38585
- adapterFailure: {
38586
- category: "availability",
38587
- outcome: "fail-rate-limit",
38588
- retriable: true,
38589
- message: truncated,
38590
- ...parsed.retryAfterSeconds !== undefined && { retryAfterSeconds: parsed.retryAfterSeconds }
38591
- }
38592
- };
38593
- case "model-not-available":
38594
- return {
38595
- ...base,
38596
- adapterFailure: { category: "quality", outcome: "fail-adapter-error", retriable: false, message: truncated }
38597
- };
38598
- default:
38599
- return null;
38600
- }
38601
- }
38602
- function parseAgentError(stderr) {
38603
- if (!stderr) {
38604
- return { type: "unknown" };
38605
- }
38606
- const parsedJson = parseJsonObject(stderr);
38607
- if (parsedJson) {
38608
- const classified = classifyJsonPayload(parsedJson);
38609
- if (classified)
38610
- return classified;
38611
- }
38612
- if (!parsedJson) {
38613
- for (const embedded of extractEmbeddedJsonObjects(stderr)) {
38614
- const classified = classifyJsonPayload(embedded);
38615
- if (classified)
38616
- return classified;
38617
- }
38618
- }
38619
- const bracketed = extractBracketedCodes(stderr);
38620
- const fromBracketed = classifyFromCodeTokens(bracketed);
38621
- if (fromBracketed)
38622
- return fromBracketed;
38623
- const fromKeyValue = classifyFromCodeTokens(extractKeyValueCodes(stderr));
38624
- if (fromKeyValue)
38625
- return fromKeyValue;
38626
- const flatModel = classifyModelErrorMessage(stderr);
38627
- if (flatModel)
38628
- return flatModel;
38629
- return { type: "unknown" };
38630
- }
38631
- function classifyJsonPayload(payload) {
38632
- const direct = classifyDirectType(payload);
38633
- if (direct)
38634
- return direct;
38635
- const nested = classifyNestedAnthropicError(payload);
38636
- if (nested)
38637
- return nested;
38638
- const jsonRpcModel = classifyJsonRpcModelError(payload);
38639
- if (jsonRpcModel)
38640
- return jsonRpcModel;
38641
- const structured = classifyFromCodeTokens(extractJsonCodeTokens(payload), payload);
38642
- if (structured)
38643
- return structured;
38644
- return null;
38645
- }
38646
- function classifyNestedAnthropicError(payload) {
38647
- if (payload.type !== "error")
38648
- return null;
38649
- const inner = payload.error;
38650
- if (!inner || typeof inner !== "object")
38651
- return null;
38652
- const innerType = inner.type;
38653
- if (typeof innerType !== "string")
38654
- return null;
38655
- switch (innerType) {
38656
- case "authentication_error":
38657
- case "permission_error":
38658
- case "invalid_api_key_error":
38659
- return { type: "auth" };
38660
- case "rate_limit_error":
38661
- case "overloaded_error": {
38662
- const retryAfterSeconds = toNumber(inner.retryAfterSeconds ?? inner.retry_after_seconds ?? payload.retryAfterSeconds ?? payload.retry_after_seconds);
38663
- return retryAfterSeconds !== undefined ? { type: "rate-limit", retryAfterSeconds } : { type: "rate-limit" };
38664
- }
38665
- default:
38666
- return null;
38667
- }
38668
- }
38669
- function extractEmbeddedJsonObjects(text) {
38670
- const objects = [];
38671
- let i = 0;
38672
- while (i < text.length && objects.length < MAX_EMBEDDED_CANDIDATES) {
38673
- const open3 = text.indexOf("{", i);
38674
- if (open3 === -1)
38675
- break;
38676
- const close = findMatchingBrace(text, open3);
38677
- if (close === -1)
38678
- break;
38679
- const candidate = text.slice(open3, close + 1);
38680
- try {
38681
- const parsed = JSON.parse(candidate);
38682
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
38683
- objects.push(parsed);
38684
- }
38685
- } catch {}
38686
- i = close + 1;
38687
- }
38688
- return objects;
38689
- }
38690
- function findMatchingBrace(text, openIdx) {
38691
- let depth = 0;
38692
- let inString = false;
38693
- let escaped = false;
38694
- for (let i = openIdx;i < text.length; i++) {
38695
- const ch = text[i];
38696
- if (escaped) {
38697
- escaped = false;
38698
- continue;
38699
- }
38700
- if (ch === "\\" && inString) {
38701
- escaped = true;
38702
- continue;
38703
- }
38704
- if (ch === '"') {
38705
- inString = !inString;
38706
- continue;
38707
- }
38708
- if (inString)
38709
- continue;
38710
- if (ch === "{")
38711
- depth++;
38712
- else if (ch === "}") {
38713
- depth--;
38714
- if (depth === 0)
38715
- return i;
38716
- }
38717
- }
38718
- return -1;
38719
- }
38720
- function parseJsonObject(stderr) {
38721
- try {
38722
- const value = JSON.parse(stderr);
38723
- if (value && typeof value === "object" && !Array.isArray(value)) {
38724
- return value;
38725
- }
38726
- return null;
38727
- } catch {
38728
- return null;
38729
- }
38730
- }
38731
- function classifyJsonRpcModelError(payload) {
38732
- const errObj = payload.error;
38733
- if (!errObj || typeof errObj !== "object")
38734
- return null;
38735
- const message = errObj.message;
38736
- if (typeof message !== "string")
38737
- return null;
38738
- return classifyModelErrorMessage(message);
38739
- }
38740
- function classifyModelErrorMessage(message) {
38741
- if (message.startsWith("Cannot apply --model") || message.startsWith("Cannot replay saved model")) {
38742
- return { type: "model-not-available" };
38743
- }
38744
- if (message.includes("There's an issue with the selected model") && message.includes("Run --model")) {
38745
- return { type: "model-not-available" };
38746
- }
38747
- return null;
38748
- }
38749
- function classifyDirectType(payload) {
38750
- const type = payload.type;
38751
- if (type !== "rate-limit" && type !== "auth" && type !== "timeout" && type !== "crash" && type !== "unknown") {
38752
- return null;
38753
- }
38754
- const result = { type };
38755
- if (type === "rate-limit") {
38756
- const retryAfterSeconds = toNumber(payload.retryAfterSeconds ?? payload.retry_after_seconds ?? payload.retryAfter);
38757
- if (retryAfterSeconds !== undefined) {
38758
- result.retryAfterSeconds = retryAfterSeconds;
38759
- }
38760
- }
38761
- return result;
38762
- }
38763
- function extractJsonCodeTokens(payload) {
38764
- const tokens = [];
38765
- const pushToken = (candidate) => {
38766
- if (typeof candidate === "number" && Number.isFinite(candidate)) {
38767
- tokens.push(String(candidate));
38768
- } else if (typeof candidate === "string" && candidate.trim()) {
38769
- tokens.push(candidate.trim());
38770
- }
38771
- };
38772
- const MAX_WALK_DEPTH = 8;
38773
- const walk = (obj, depth) => {
38774
- if (depth > MAX_WALK_DEPTH)
38775
- return;
38776
- const candidates = [
38777
- obj.code,
38778
- obj.status,
38779
- obj.statusCode,
38780
- obj.httpStatus,
38781
- obj.errorCode,
38782
- obj.acpxCode,
38783
- obj.detailCode
38784
- ];
38785
- for (const candidate of candidates)
38786
- pushToken(candidate);
38787
- for (const key of ["error", "data"]) {
38788
- const nested = obj[key];
38789
- if (nested && typeof nested === "object" && !Array.isArray(nested)) {
38790
- walk(nested, depth + 1);
38791
- }
38792
- }
38793
- };
38794
- walk(payload, 0);
38795
- return tokens;
38796
- }
38797
- function extractBracketedCodes(stderr) {
38798
- const tokens = [];
38799
- const matches = stderr.match(/\[([A-Z0-9_/-]+)\]/g) ?? [];
38800
- for (const entry of matches) {
38801
- const inner = entry.slice(1, -1);
38802
- for (const token of inner.split("/")) {
38803
- const trimmed = token.trim();
38804
- if (trimmed)
38805
- tokens.push(trimmed);
38806
- }
38807
- }
38808
- return tokens;
38809
- }
38810
- function extractKeyValueCodes(stderr) {
38811
- const tokens = [];
38812
- const patterns = [
38813
- /(?:status|statusCode|httpStatus|code)\s*[:=]\s*(\d{3})/g,
38814
- /(?:acpxCode|detailCode|errorCode)\s*[:=]\s*([A-Z0-9_]+)/g
38815
- ];
38816
- for (const pattern of patterns) {
38817
- while (true) {
38818
- const match = pattern.exec(stderr);
38819
- if (!match)
38820
- break;
38821
- const code = match[1];
38822
- if (code)
38823
- tokens.push(code);
38824
- }
38825
- }
38826
- return tokens;
38827
- }
38828
- function classifyFromCodeTokens(tokens, payload) {
38829
- for (const token of tokens.map((value) => value.toUpperCase())) {
38830
- if (token === "429" || token === "TOO_MANY_REQUESTS" || token === "RATE_LIMIT" || token === "RATE_LIMITED" || token === "QUOTA_EXCEEDED" || token === "RESOURCE_EXHAUSTED") {
38831
- const retryAfterSeconds = payload ? findRetryAfterSeconds(payload) : undefined;
38832
- return retryAfterSeconds !== undefined ? { type: "rate-limit", retryAfterSeconds } : { type: "rate-limit" };
38833
- }
38834
- if (token === "401" || token === "403" || token === "UNAUTHORIZED" || token === "FORBIDDEN" || token === "AUTH_FAILED" || token === "AUTHENTICATION_FAILED" || token === "PERMISSION_DENIED") {
38835
- return { type: "auth" };
38836
- }
38837
- }
38838
- return null;
38839
- }
38840
- function toNumber(value) {
38841
- if (typeof value === "number" && Number.isFinite(value))
38842
- return Math.trunc(value);
38843
- if (typeof value === "string" && value.trim()) {
38844
- const parsed = Number.parseInt(value, 10);
38845
- if (Number.isFinite(parsed))
38846
- return parsed;
38847
- }
38848
- return;
38849
- }
38850
- function findRetryAfterSeconds(payload) {
38851
- const topLevel = toNumber(payload.retryAfterSeconds ?? payload.retry_after_seconds ?? payload.retryAfter);
38852
- if (topLevel !== undefined)
38853
- return topLevel;
38854
- const error48 = payload.error;
38855
- if (error48 && typeof error48 === "object" && !Array.isArray(error48)) {
38856
- const errObj = error48;
38857
- const inError = toNumber(errObj.retryAfterSeconds ?? errObj.retry_after_seconds ?? errObj.retryAfter);
38858
- if (inError !== undefined)
38859
- return inError;
38860
- const data = errObj.data;
38861
- if (data && typeof data === "object" && !Array.isArray(data)) {
38862
- const dataObj = data;
38863
- return toNumber(dataObj.retryAfterSeconds ?? dataObj.retry_after_seconds ?? dataObj.retryAfter);
38864
- }
38865
- }
38866
- return;
38867
- }
38868
- var MAX_EMBEDDED_CANDIDATES = 8;
38869
-
38870
- // src/agents/errors/index.ts
38871
- var init_errors5 = () => {};
38872
-
38873
- // src/agents/shared/env.ts
38874
- import { homedir as homedir5 } from "os";
38875
- import { isAbsolute as isAbsolute10 } from "path";
38876
- function buildAllowedEnv(options) {
38877
- const allowed = {};
38878
- for (const varName of ESSENTIAL_VARS) {
38879
- if (process.env[varName])
38880
- allowed[varName] = process.env[varName];
38881
- }
38882
- const rawHome = process.env.HOME ?? "";
38883
- const safeHome = rawHome && isAbsolute10(rawHome) ? rawHome : homedir5();
38884
- if (rawHome !== safeHome) {
38885
- getSafeLogger2()?.warn("env", `HOME env is not absolute ("${rawHome}"), falling back to os.homedir(): ${safeHome}`);
38886
- }
38887
- allowed.HOME = safeHome;
38888
- for (const varName of API_KEY_VARS) {
38889
- if (process.env[varName])
38890
- allowed[varName] = process.env[varName];
38891
- }
38892
- for (const [key, value] of Object.entries(process.env)) {
38893
- if (ALLOWED_PREFIXES.some((prefix) => key.startsWith(prefix))) {
38894
- allowed[key] = value;
38895
- }
38896
- }
38897
- if (options?.modelEnv)
38898
- Object.assign(allowed, options.modelEnv);
38899
- if (options?.env)
38900
- Object.assign(allowed, options.env);
38901
- return allowed;
38902
- }
38903
- var ESSENTIAL_VARS, API_KEY_VARS, ALLOWED_PREFIXES;
38904
- var init_env = __esm(() => {
38905
- init_logger2();
38906
- ESSENTIAL_VARS = ["PATH", "TMPDIR", "NODE_ENV", "USER", "LOGNAME"];
38907
- API_KEY_VARS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY", "CLAUDE_API_KEY"];
38908
- ALLOWED_PREFIXES = [
38909
- "CLAUDE_",
38910
- "NAX_",
38911
- "CLAW_",
38912
- "TURBO_",
38913
- "ACPX_",
38914
- "CODEX_",
38915
- "GEMINI_",
38916
- "ANTHROPIC_",
38917
- "OPENCODE_",
38918
- "MINIMAX_"
38919
- ];
38920
- });
38921
-
38922
- // src/agents/acp/reasoning-effort.ts
38923
- async function discoverEffortOptionId(params) {
38924
- const { agentName, sessionName, cwd, storyId, spawn: spawn2 } = params;
38925
- const cmd = ["acpx", "--cwd", cwd, "--format", "json", agentName, "sessions", "show", sessionName];
38926
- try {
38927
- const { exitCode, stdout, stderr } = await spawn2(cmd);
38928
- if (exitCode !== 0) {
38929
- getSafeLogger2()?.debug("acp-adapter", "Could not list session config options; falling back to static map", {
38930
- storyId,
38931
- agentName,
38932
- cause: stdout || stderr
38933
- });
38934
- return;
38935
- }
38936
- const parsed = JSON.parse(stdout);
38937
- const options = parsed.acpx?.config_options;
38938
- const match = Array.isArray(options) ? options.find((o) => o?.category === THOUGHT_LEVEL_CATEGORY) : undefined;
38939
- return typeof match?.id === "string" ? match.id : undefined;
38940
- } catch (err) {
38941
- getSafeLogger2()?.debug("acp-adapter", "Effort option discovery failed; falling back to static map", {
38942
- storyId,
38943
- agentName,
38944
- cause: err instanceof Error ? err.message : String(err)
38945
- });
38946
- return;
38947
- }
38948
- }
38949
- async function applyReasoningEffort(params) {
38950
- const { effort, agentName, sessionName, cwd, storyId, spawn: spawn2 } = params;
38951
- if (!effort)
38952
- return;
38953
- const optionName = await discoverEffortOptionId({ agentName, sessionName, cwd, storyId, spawn: spawn2 }) ?? EFFORT_OPTION_BY_AGENT[agentName];
38954
- if (!optionName) {
38955
- getSafeLogger2()?.debug("acp-adapter", "Agent has no known effort option; skipping", {
38956
- storyId,
38957
- agentName,
38958
- effort
38959
- });
38960
- return;
38961
- }
38962
- const cmd = ["acpx", "--cwd", cwd, agentName, "set", optionName, effort, "-s", sessionName];
38963
- const { exitCode, stdout, stderr } = await spawn2(cmd);
38964
- if (exitCode !== 0) {
38965
- getSafeLogger2()?.warn("acp-adapter", `Failed to set ${optionName}; continuing at adapter default`, {
38966
- storyId,
38967
- effort,
38968
- session: sessionName,
38969
- cause: stdout || stderr
38970
- });
38971
- }
38972
- }
38973
- var THOUGHT_LEVEL_CATEGORY = "thought_level", EFFORT_OPTION_BY_AGENT;
38974
- var init_reasoning_effort = __esm(() => {
38975
- init_logger2();
38976
- EFFORT_OPTION_BY_AGENT = {
38977
- codex: "reasoning_effort",
38978
- claude: "effort",
38979
- opencode: "effort",
38980
- pi: "thought_level"
38981
- };
38982
- });
38983
-
38984
- // src/agents/acp/session-ids.ts
38985
- function parseSessionIds(stdout) {
38986
- for (const line of stdout.split(`
38987
- `).reverse()) {
38988
- const trimmed = line.trim();
38989
- if (!trimmed.startsWith("{"))
38990
- continue;
38991
- try {
38992
- const parsed = JSON.parse(trimmed);
38993
- const sessionId = parsed.acpxSessionId;
38994
- const recordId = parsed.acpxRecordId;
38995
- if (typeof sessionId === "string" && sessionId.length > 0) {
38996
- return {
38997
- sessionId,
38998
- recordId: typeof recordId === "string" && recordId.length > 0 ? recordId : undefined
38999
- };
39000
- }
39001
- } catch {}
39002
- }
39003
- return { sessionId: undefined, recordId: undefined };
39004
- }
39005
-
39006
- // src/agents/acp/spawn-client-deps.ts
39007
- var ACPX_STREAM_DRAIN_TIMEOUT_MS = 5000, KILL_TREE_GRACE_MS = 250, TRACKED_SPAWN_DEADLINE_MS = 1e4, TRACKED_SPAWN_STARTUP_DEADLINE_MS = 30000, _spawnClientDeps;
39008
- var init_spawn_client_deps = __esm(() => {
39009
- init_bun_deps();
39010
- _spawnClientDeps = {
39011
- spawn: typedSpawn,
39012
- streamDrainTimeoutMs: ACPX_STREAM_DRAIN_TIMEOUT_MS,
39013
- killTreeGraceMs: KILL_TREE_GRACE_MS,
39014
- trackedSpawnDeadlineMs: TRACKED_SPAWN_DEADLINE_MS,
39015
- trackedSpawnStartupDeadlineMs: TRACKED_SPAWN_STARTUP_DEADLINE_MS
39016
- };
39017
- });
39018
-
39019
- // src/agents/acp/spawn-client-process.ts
39020
- function makeStreamDrain(ms) {
39021
- let id;
39022
- const promise2 = new Promise((resolve21) => {
39023
- id = setTimeout(() => resolve21(""), ms);
39024
- });
39025
- return { promise: promise2, cancel: () => clearTimeout(id) };
39026
- }
39027
- function killProcessTree(pid, graceMs, exited) {
39028
- activeKillTimers.get(pid)?.cancel();
39029
- activeKillTimers.delete(pid);
39030
- try {
39031
- killProcessGroup(pid, "SIGTERM");
39032
- getSafeLogger2()?.debug("acp-adapter", `Sent SIGTERM to process group ${pid}`);
39033
- } catch {}
39034
- let settled2 = false;
39035
- const timer = { id: undefined };
39036
- const gracePromise = new Promise((resolve21) => {
39037
- timer.id = setTimeout(() => resolve21("grace"), graceMs);
39038
- if (timer.id.unref)
39039
- timer.id.unref();
39040
- });
39041
- const exitedPromise = exited?.then(() => "exited", () => "exited");
39042
- const raced = exitedPromise !== undefined ? Promise.race([gracePromise, exitedPromise]) : gracePromise;
39043
- raced.then((outcome) => {
39044
- clearTimeout(timer.id);
39045
- if (settled2)
39046
- return;
39047
- settled2 = true;
39048
- activeKillTimers.delete(pid);
39049
- if (outcome === "exited")
39050
- return;
39051
- if (!isProcessAlive(pid))
39052
- return;
39053
- try {
39054
- killProcessGroup(pid, "SIGKILL");
39055
- } catch {}
39056
- });
39057
- const handle = {
39058
- cancel: () => {
39059
- settled2 = true;
39060
- clearTimeout(timer.id);
39061
- activeKillTimers.delete(pid);
39062
- }
39063
- };
39064
- activeKillTimers.set(pid, handle);
39065
- return handle;
39066
- }
39067
- async function runTrackedSpawn(deps, cmd, opts, onPidSpawned, onPidExited, signal) {
39068
- const proc = deps.spawn(cmd, { stdout: "pipe", stderr: "pipe", ...opts });
39069
- const pid = proc.pid;
39070
- onPidSpawned?.(pid);
39071
- const exitedNotified = proc.exited.catch(() => -1).finally(() => {
39072
- try {
39073
- onPidExited?.(pid);
39074
- } catch {}
39075
- });
39076
- const deadlineController = new AbortController;
39077
- const forwardAbort = () => deadlineController.abort(signal?.reason);
39078
- if (signal?.aborted) {
39079
- deadlineController.abort(signal.reason);
39080
- } else {
39081
- signal?.addEventListener("abort", forwardAbort, { once: true });
39082
- }
39083
- const deadline = cancellableDelay(deps.trackedSpawnDeadlineMs, deadlineController.signal).catch(() => {});
39084
- try {
39085
- const raced = await Promise.race([
39086
- exitedNotified.then((code) => ({ timedOut: false, code })),
39087
- deadline.then(() => ({ timedOut: true, code: -1 }))
39088
- ]);
39089
- if (raced.timedOut) {
39090
- const reason = signal?.aborted ? "aborted" : "deadline exceeded";
39091
- getSafeLogger2()?.warn("acp-adapter", `trackedSpawn ${reason} \u2014 killing process tree for PID ${pid}`, {
39092
- pid,
39093
- deadlineMs: deps.trackedSpawnDeadlineMs,
39094
- reason
39095
- });
39096
- killProcessTree(pid, deps.killTreeGraceMs, proc.exited);
39097
- try {
39098
- proc.stdout?.cancel?.();
39099
- } catch {}
39100
- try {
39101
- proc.stderr?.cancel?.();
39102
- } catch {}
39103
- return { exitCode: -1, stdout: "", stderr: "" };
39104
- }
39105
- const [stdout, stderr] = await Promise.all([
39106
- drainStream(proc.stdout, deps.streamDrainTimeoutMs),
39107
- drainStream(proc.stderr, deps.streamDrainTimeoutMs)
39108
- ]);
39109
- return { exitCode: raced.code, stdout, stderr };
39110
- } finally {
39111
- deadlineController.abort();
39112
- signal?.removeEventListener("abort", forwardAbort);
39113
- }
39114
- }
39115
- async function drainStream(stream, timeoutMs) {
39116
- if (!stream)
39117
- return "";
39118
- const reader = stream.getReader();
39119
- const decoder = new TextDecoder;
39120
- const readAll = async () => {
39121
- let text = "";
39122
- for (;; ) {
39123
- const { done, value } = await reader.read();
39124
- if (done)
39125
- break;
39126
- text += decoder.decode(value, { stream: true });
39127
- }
39128
- text += decoder.decode();
39129
- return text;
39130
- };
39131
- const drain = makeStreamDrain(timeoutMs);
39132
- const winner = await Promise.race([
39133
- readAll().catch(() => "").then((text) => ({ fromStream: true, text })),
39134
- drain.promise.then(() => ({ fromStream: false, text: "" }))
39135
- ]).finally(() => drain.cancel());
39136
- if (!winner.fromStream) {
39137
- reader.cancel().catch(() => {});
39138
- }
39139
- return winner.text;
39140
- }
39141
- var activeKillTimers;
39142
- var init_spawn_client_process = __esm(() => {
39143
- init_internal();
39144
- init_logger2();
39145
- init_bun_deps();
39146
- activeKillTimers = new Map;
39147
- });
39148
-
39149
- // src/agents/acp/parser-line-handlers.ts
39150
- function asFiniteNumber(...values) {
39151
- for (const v of values) {
39152
- if (typeof v === "number" && Number.isFinite(v))
39153
- return v;
39154
- }
39155
- return;
39156
- }
39157
- function isProtocolDrift(event) {
39158
- const looksLikeJsonRpcShape = typeof event.method === "string" && event.params !== undefined || event.id !== undefined && event.result && typeof event.result === "object";
39159
- return event.jsonrpc !== "2.0" && looksLikeJsonRpcShape;
39160
- }
39161
- function reportProtocolDrift(event, state) {
39162
- getSafeLogger2()?.error("acp-adapter", "Unsupported or missing JSON-RPC protocol version in acpx output", {
39163
- jsonrpc: event.jsonrpc,
39164
- method: typeof event.method === "string" ? event.method : undefined
39165
- });
39166
- state.error ??= "Unsupported acpx JSON-RPC protocol version";
39167
- }
39168
- function decorateAcpxError(err, errorMsg, state) {
39169
- let msg = errorMsg;
39170
- const data = err.data;
39171
- if (data && typeof data === "object") {
39172
- const suffix = [data.acpxCode, data.detailCode].filter(Boolean).join("/");
39173
- if (suffix)
39174
- msg = `${msg} [${suffix}]`;
39175
- if (!state.error && data.retryable === true)
39176
- state.retryable = true;
39177
- }
39178
- return msg;
39179
- }
39180
- function handleAgentMessageChunk(update, state) {
39181
- if (update.content?.type === "text" && typeof update.content.text === "string") {
39182
- const text = update.content.text;
39183
- state.text += text;
39184
- return {
39185
- kind: "message_update",
39186
- deltaBytes: text.length
39187
- };
39188
- }
39189
- return;
39190
- }
39191
- function handleAgentThoughtChunk(update) {
39192
- if (update.content?.type === "text" && typeof update.content.text === "string") {
39193
- return {
39194
- kind: "thinking_update",
39195
- deltaBytes: update.content.text.length
39196
- };
39197
- }
39198
- return;
39199
- }
39200
- function handleUsageUpdate(update, state) {
39201
- const activity = { kind: "usage_update" };
39202
- const metaUsage = update._meta != null && typeof update._meta === "object" ? update._meta.usage : undefined;
39203
- if (metaUsage != null && typeof metaUsage === "object") {
39204
- const inp = asFiniteNumber(metaUsage.inputTokens, metaUsage.input_tokens);
39205
- if (inp !== undefined)
39206
- activity.inputTokens = inp;
39207
- const out = asFiniteNumber(metaUsage.outputTokens, metaUsage.output_tokens);
39208
- if (out !== undefined)
39209
- activity.outputTokens = out;
39210
- const cacheRead = asFiniteNumber(metaUsage.cachedReadTokens, metaUsage.cache_read_input_tokens);
39211
- if (cacheRead !== undefined)
39212
- activity.cacheRead = cacheRead;
39213
- const cacheWrite = asFiniteNumber(metaUsage.cachedWriteTokens, metaUsage.cache_creation_input_tokens);
39214
- if (cacheWrite !== undefined)
39215
- activity.cacheWrite = cacheWrite;
39216
- }
39217
- if (activity.outputTokens == null) {
39218
- const used = asFiniteNumber(update.used);
39219
- if (used !== undefined)
39220
- activity.outputTokens = used;
39221
- }
39222
- const costAmount = asFiniteNumber(update.cost?.amount);
39223
- if (costAmount !== undefined) {
39224
- activity.costUsd = costAmount;
39225
- state.exactCostUsd = costAmount;
39226
- }
39227
- return activity;
39228
- }
39229
- function handleToolCallActivity(update) {
39230
- return {
39231
- kind: "tool_call_update",
39232
- toolName: extractToolName(update)
39233
- };
39234
- }
39235
- function extractToolName(update) {
39236
- const directName = update.toolName;
39237
- if (typeof directName === "string" && directName.trim())
39238
- return directName;
39239
- const nestedTool = update.tool;
39240
- if (nestedTool && typeof nestedTool === "object") {
39241
- const name = nestedTool.name;
39242
- if (typeof name === "string" && name.trim())
39243
- return name;
39244
- }
39245
- return;
39246
- }
39247
- function handleSessionUpdate(update, state) {
39248
- const key = update.sessionUpdate;
39249
- if (typeof key !== "string")
39250
- return;
39251
- if (!Object.hasOwn(SESSION_UPDATE_HANDLERS, key))
39252
- return;
39253
- return SESSION_UPDATE_HANDLERS[key](update, state);
39254
- }
39255
- function applyJsonRpcResult(event, state) {
39256
- if (event.id !== undefined && event.result && typeof event.result === "object") {
39257
- const result = event.result;
39258
- if (result.stopReason)
39259
- state.stopReason = result.stopReason;
39260
- if (result.stop_reason)
39261
- state.stopReason = result.stop_reason;
39262
- if (result.usage && typeof result.usage === "object") {
39263
- const u = result.usage;
39264
- const inputTokens = asFiniteNumber(u.inputTokens, u.input_tokens);
39265
- const outputTokens = asFiniteNumber(u.outputTokens, u.output_tokens);
39266
- if (inputTokens !== undefined && outputTokens !== undefined) {
39267
- state.tokenUsage = {
39268
- input_tokens: inputTokens,
39269
- output_tokens: outputTokens,
39270
- cache_read_input_tokens: asFiniteNumber(u.cachedReadTokens, u.cache_read_input_tokens) ?? 0,
39271
- cache_creation_input_tokens: asFiniteNumber(u.cachedWriteTokens, u.cache_creation_input_tokens) ?? 0
39272
- };
39273
- }
39274
- }
39275
- }
39276
- }
39277
- function applyJsonRpcError(event, state) {
39278
- if (event.error && typeof event.error === "object") {
39279
- const err = event.error;
39280
- let errorMsg = typeof err.message === "string" ? err.message : JSON.stringify(event.error);
39281
- errorMsg = decorateAcpxError(err, errorMsg, state);
39282
- if (!state.error)
39283
- state.error = errorMsg;
39284
- }
39285
- }
39286
- function handleJsonRpcEvent(event, state) {
39287
- if (event.method === "session/update" && event.params?.update) {
39288
- const activity = handleSessionUpdate(event.params.update, state);
39289
- if (activity !== undefined)
39290
- return activity;
39291
- }
39292
- applyJsonRpcResult(event, state);
39293
- applyJsonRpcError(event, state);
39294
- return;
39295
- }
39296
- function applyLegacyCumulativeUsage(event, state) {
39297
- if (event.cumulative_token_usage && typeof event.cumulative_token_usage === "object") {
39298
- const c = event.cumulative_token_usage;
39299
- const inputTokens = asFiniteNumber(c.input_tokens);
39300
- const outputTokens = asFiniteNumber(c.output_tokens);
39301
- if (inputTokens !== undefined && outputTokens !== undefined) {
39302
- state.tokenUsage = {
39303
- input_tokens: inputTokens,
39304
- output_tokens: outputTokens,
39305
- cache_read_input_tokens: asFiniteNumber(c.cache_read_input_tokens) ?? 0,
39306
- cache_creation_input_tokens: asFiniteNumber(c.cache_creation_input_tokens) ?? 0
39307
- };
39308
- }
39309
- }
39310
- }
39311
- function applyLegacyUsage(event, state) {
39312
- if (!event.usage)
39313
- return;
39314
- const legacyInputTokens = asFiniteNumber(event.usage.input_tokens, event.usage.prompt_tokens);
39315
- const legacyOutputTokens = asFiniteNumber(event.usage.output_tokens, event.usage.completion_tokens);
39316
- if (legacyInputTokens !== undefined && legacyOutputTokens !== undefined) {
39317
- state.tokenUsage = {
39318
- input_tokens: legacyInputTokens,
39319
- output_tokens: legacyOutputTokens
39320
- };
39321
- }
39322
- }
39323
- function applyLegacyStopReason(event, state) {
39324
- if (event.stopReason)
39325
- state.stopReason = event.stopReason;
39326
- if (event.stop_reason)
39327
- state.stopReason = event.stop_reason;
39328
- }
39329
- function applyLegacyError(event, state) {
39330
- if (!event.error)
39331
- return;
39332
- if (typeof event.error === "string") {
39333
- state.error ??= event.error;
39334
- return;
39335
- }
39336
- let errorMsg = typeof event.error.message === "string" ? event.error.message : JSON.stringify(event.error);
39337
- errorMsg = decorateAcpxError(event.error, errorMsg, state);
39338
- state.error ??= errorMsg;
39339
- }
39340
- function handleLegacyLine(event, state) {
39341
- if (event.result && typeof event.result === "string") {
39342
- state.text = event.result;
39343
- } else if (event.content && typeof event.content === "string") {
39344
- state.text += event.content;
39345
- } else if (event.text && typeof event.text === "string") {
39346
- state.text += event.text;
39347
- }
39348
- applyLegacyCumulativeUsage(event, state);
39349
- applyLegacyUsage(event, state);
39350
- applyLegacyStopReason(event, state);
39351
- applyLegacyError(event, state);
39352
- }
39353
- var SESSION_UPDATE_HANDLERS;
39354
- var init_parser_line_handlers = __esm(() => {
39355
- init_logger2();
39356
- SESSION_UPDATE_HANDLERS = {
39357
- agent_message_chunk: handleAgentMessageChunk,
39358
- agent_thought_chunk: handleAgentThoughtChunk,
39359
- usage_update: handleUsageUpdate,
39360
- tool_call: handleToolCallActivity,
39361
- tool_call_update: handleToolCallActivity
39362
- };
39363
- });
39364
-
39365
- // src/agents/acp/parser.ts
39366
- function createParseState() {
39367
- return {
39368
- text: "",
39369
- tokenUsage: undefined,
39370
- exactCostUsd: undefined,
39371
- stopReason: undefined,
39372
- error: undefined,
39373
- retryable: false,
39374
- sawJsonLine: false
39375
- };
39376
- }
39377
- function parseAcpxJsonLine(line, state) {
39378
- try {
39379
- const event = JSON.parse(line);
39380
- if (!state.sawJsonLine) {
39381
- state.text = "";
39382
- state.sawJsonLine = true;
39383
- }
39384
- if (isProtocolDrift(event)) {
39385
- reportProtocolDrift(event, state);
39386
- return;
39387
- }
39388
- if (event.jsonrpc === "2.0") {
39389
- return handleJsonRpcEvent(event, state);
39390
- }
39391
- handleLegacyLine(event, state);
39392
- } catch {
39393
- if (!state.text && !state.sawJsonLine)
39394
- state.text = line;
39395
- }
39396
- return;
39397
- }
39398
- function finalizeParseState(state) {
39399
- return {
39400
- text: state.text.trim(),
39401
- tokenUsage: state.tokenUsage,
39402
- exactCostUsd: state.exactCostUsd,
39403
- stopReason: state.stopReason,
39404
- error: state.error,
39405
- retryable: state.retryable
39406
- };
39407
- }
39408
- var init_parser = __esm(() => {
39409
- init_parser_line_handlers();
39410
- });
39411
-
39412
- // src/agents/acp/stdout-line-reader.ts
39413
- function readStreamTail(stream, maxBytes = MAX_BUFFERED_STDERR_BYTES) {
39414
- const decoder = new TextDecoder;
39415
- const reader = stream.getReader();
39416
- let tail = "";
39417
- const append = (chunk) => {
39418
- tail += chunk;
39419
- const bytes = Buffer.byteLength(tail, "utf8");
39420
- if (bytes <= maxBytes)
39421
- return;
39422
- let cutoff = bytes - maxBytes;
39423
- let index = 0;
39424
- while (index < tail.length && cutoff > 0) {
39425
- const cp = tail.codePointAt(index) ?? 0;
39426
- const units = cp > 65535 ? 2 : 1;
39427
- index += units;
39428
- cutoff -= Buffer.byteLength(String.fromCodePoint(cp), "utf8");
39429
- }
39430
- tail = tail.slice(index);
39431
- };
39432
- const promise2 = (async () => {
39433
- try {
39434
- while (true) {
39435
- const { done, value } = await reader.read();
39436
- if (done)
39437
- break;
39438
- append(decoder.decode(value, { stream: true }));
39439
- }
39440
- append(decoder.decode());
39441
- return tail;
39442
- } finally {
39443
- reader.releaseLock();
39444
- }
39445
- })();
39446
- return {
39447
- promise: promise2,
39448
- cancel: () => {
39449
- reader.cancel().catch(() => {});
39450
- }
39451
- };
39452
- }
39453
- function readAndParseLines(stream, state, onActivity) {
39454
- const decoder = new TextDecoder;
39455
- let remainder = "";
39456
- const reader = stream.getReader();
39457
- const promise2 = (async () => {
39458
- try {
39459
- while (true) {
39460
- const { done, value } = await reader.read();
39461
- if (done)
39462
- break;
39463
- remainder += decoder.decode(value, { stream: true });
39464
- for (;; ) {
39465
- const nl = remainder.indexOf(`
39466
- `);
39467
- if (nl < 0)
39468
- break;
39469
- const line = remainder.slice(0, nl);
39470
- remainder = remainder.slice(nl + 1);
39471
- if (line.trim()) {
39472
- const activity = parseAcpxJsonLine(line, state);
39473
- if (activity && onActivity)
39474
- onActivity(activity);
39475
- }
39476
- }
39477
- if (Buffer.byteLength(remainder, "utf8") > MAX_BUFFERED_LINE_BYTES) {
39478
- getSafeLogger2()?.error("acp-adapter", "Dropping oversized buffered stdout line (exceeds cap)", {
39479
- bufferedBytes: Buffer.byteLength(remainder, "utf8"),
39480
- maxBufferedLineBytes: MAX_BUFFERED_LINE_BYTES
39481
- });
39482
- remainder = "";
39483
- }
39484
- }
39485
- remainder += decoder.decode();
39486
- if (remainder.trim()) {
39487
- const activity = parseAcpxJsonLine(remainder.trim(), state);
39488
- if (activity && onActivity)
39489
- onActivity(activity);
39490
- }
39491
- } finally {
39492
- reader.releaseLock();
39493
- }
39494
- })();
39495
- return {
39496
- promise: promise2,
39497
- cancel: () => {
39498
- reader.cancel().catch(() => {});
39499
- }
39500
- };
39501
- }
39502
- var MAX_BUFFERED_LINE_BYTES, MAX_BUFFERED_STDERR_BYTES;
39503
- var init_stdout_line_reader = __esm(() => {
39504
- init_logger2();
39505
- init_parser();
39506
- MAX_BUFFERED_LINE_BYTES = 10 * 1024 * 1024;
39507
- MAX_BUFFERED_STDERR_BYTES = 64 * 1024;
39508
- });
39509
-
39510
- // src/agents/acp/spawn-client-session.ts
39511
- var exports_spawn_client_session = {};
39512
- __export(exports_spawn_client_session, {
39513
- SpawnAcpSession: () => SpawnAcpSession
39514
- });
39515
- import { randomUUID as randomUUID8 } from "crypto";
39516
-
39517
- class SpawnAcpSession {
39518
- agentName;
39519
- sessionName;
39520
- cwd;
39521
- model;
39522
- modelLabel;
39523
- timeoutSeconds;
39524
- promptRetries;
39525
- permissionMode;
39526
- env;
39527
- onPidSpawned;
39528
- onPidExited;
39529
- onStreamActivity;
39530
- onActiveCall;
39531
- runId;
39532
- storyId;
39533
- stage;
39534
- trackedSpawnDeadlineMs;
39535
- activeProc = null;
39536
- _externallyCancelled = false;
39537
- id;
39538
- recordId;
39539
- constructor(opts) {
39540
- this.agentName = opts.agentName;
39541
- this.sessionName = opts.sessionName;
39542
- this.cwd = opts.cwd;
39543
- this.model = opts.model;
39544
- this.modelLabel = opts.modelLabel ?? opts.model;
39545
- this.timeoutSeconds = opts.timeoutSeconds;
39546
- this.promptRetries = opts.promptRetries;
39547
- this.permissionMode = opts.permissionMode;
39548
- this.env = opts.env;
39549
- this.onPidSpawned = opts.onPidSpawned;
39550
- this.onPidExited = opts.onPidExited;
39551
- this.onStreamActivity = opts.onStreamActivity;
39552
- this.onActiveCall = opts.onActiveCall;
39553
- this.runId = opts.runId ?? "";
39554
- this.storyId = opts.storyId;
39555
- this.stage = opts.stage;
39556
- this.id = opts.id;
39557
- this.recordId = opts.recordId;
39558
- this.trackedSpawnDeadlineMs = opts.trackedSpawnDeadlineMs ?? _spawnClientDeps.trackedSpawnDeadlineMs;
39559
- }
39560
- async prompt(text) {
39561
- const callId = randomUUID8();
39562
- const emit = this.onStreamActivity;
39563
- const now = () => Date.now();
39564
- this._externallyCancelled = false;
39565
- const baseEvent = {
39566
- callId,
39567
- runId: this.runId,
39568
- agentName: this.agentName,
39569
- sessionName: this.sessionName,
39570
- storyId: this.storyId,
39571
- stage: this.stage
39572
- };
39573
- const cmd = [
39574
- "acpx",
39575
- "--cwd",
39576
- this.cwd,
39577
- "--format",
39578
- "json",
39579
- ...this.permissionMode === "approve-all" ? ["--approve-all"] : [],
39580
- "--model",
39581
- this.model,
39582
- "--timeout",
39583
- String(this.timeoutSeconds),
39584
- ...this.promptRetries > 0 ? ["--prompt-retries", String(this.promptRetries)] : [],
39585
- this.agentName,
39586
- "prompt",
39587
- "-s",
39588
- this.sessionName,
39589
- "--file",
39590
- "-"
39591
- ];
39592
- getSafeLogger2()?.info("acp-adapter", "Sending prompt", {
39593
- session: this.sessionName,
39594
- permission: this.permissionMode,
39595
- cmd: cmd.join(" ")
39596
- });
39597
- getSafeLogger2()?.debug("acp-adapter", `Sending prompt to session: ${this.sessionName}`);
39598
- let proc;
39599
- try {
39600
- proc = _spawnClientDeps.spawn(cmd, {
39601
- cwd: this.cwd,
39602
- stdin: "pipe",
39603
- stdout: "pipe",
39604
- stderr: "pipe",
39605
- env: this.env,
39606
- detached: true
39607
- });
39608
- } catch (spawnErr) {
39609
- emit?.({ ...baseEvent, kind: "agent.call_ended", status: "error", timestamp: now() });
39610
- throw spawnErr;
39611
- }
39612
- this.activeProc = proc;
39613
- const processPid = proc.pid;
39614
- this.onPidSpawned?.(processPid);
39615
- this.onActiveCall?.(callId, () => this.cancelActivePrompt());
39616
- emit?.({
39617
- ...baseEvent,
39618
- kind: "agent.call_started",
39619
- model: this.modelLabel,
39620
- timeoutSeconds: this.timeoutSeconds,
39621
- timestamp: now()
39622
- });
39623
- emit?.({
39624
- ...baseEvent,
39625
- kind: "agent.process_update",
39626
- status: "spawned",
39627
- pid: processPid,
39628
- timestamp: now()
39629
- });
39630
- let exitNotified = false;
39631
- const notifyExit = () => {
39632
- if (exitNotified)
39633
- return;
39634
- exitNotified = true;
39635
- try {
39636
- this.onPidExited?.(processPid);
39637
- } catch {}
39638
- };
39639
- let callEndedEmitted = false;
39640
- try {
39641
- try {
39642
- proc.stdin?.write(text);
39643
- proc.stdin?.end();
39644
- } catch {
39645
- getSafeLogger2()?.warn("acp-adapter", "Failed to write prompt to acpx stdin (subprocess exited early)", {
39646
- session: this.sessionName
39647
- });
39648
- }
39649
- const parseState = createParseState();
39650
- const onActivity = emit ? (activity) => {
39651
- if (activity.kind === "message_update") {
39652
- emit({ ...baseEvent, kind: "agent.message_update", deltaBytes: activity.deltaBytes, timestamp: now() });
39653
- } else if (activity.kind === "thinking_update") {
39654
- emit({ ...baseEvent, kind: "agent.thinking_update", deltaBytes: activity.deltaBytes, timestamp: now() });
39655
- } else if (activity.kind === "usage_update") {
39656
- emit({
39657
- ...baseEvent,
39658
- kind: "agent.usage_update",
39659
- inputTokens: activity.inputTokens,
39660
- outputTokens: activity.outputTokens,
39661
- costUsd: activity.costUsd,
39662
- ...activity.cacheRead !== undefined ? { cacheRead: activity.cacheRead } : {},
39663
- ...activity.cacheWrite !== undefined ? { cacheWrite: activity.cacheWrite } : {},
39664
- cadence: "agent",
39665
- timestamp: now()
39666
- });
39667
- } else if (activity.kind === "tool_call_update") {
39668
- emit({
39669
- ...baseEvent,
39670
- kind: "agent.tool_call_update",
39671
- toolName: activity.toolName,
39672
- timestamp: now()
39673
- });
39674
- }
39675
- } : undefined;
39676
- const parseHandle = readAndParseLines(proc.stdout, parseState, onActivity);
39677
- const parsePromise = parseHandle.promise.catch(() => {});
39678
- const stderrHandle = readStreamTail(proc.stderr);
39679
- const stderrPromise = stderrHandle.promise.catch(() => "");
39680
- const exitCode = await proc.exited;
39681
- const drainA = makeStreamDrain(_spawnClientDeps.streamDrainTimeoutMs);
39682
- const drainB = makeStreamDrain(_spawnClientDeps.streamDrainTimeoutMs);
39683
- const stdoutRaceResult = Promise.race([
39684
- parsePromise.then(() => "parsed"),
39685
- drainA.promise.then(() => "drain")
39686
- ]).finally(() => drainA.cancel());
39687
- const [stdoutWinner, stderr] = await Promise.all([
39688
- stdoutRaceResult,
39689
- Promise.race([stderrPromise, drainB.promise]).finally(() => drainB.cancel())
39690
- ]);
39691
- if (stdoutWinner === "drain")
39692
- parseHandle.cancel();
39693
- if (stderr === "")
39694
- stderrHandle.cancel();
39695
- emit?.({ ...baseEvent, kind: "agent.process_update", status: "exited", exitCode, timestamp: now() });
39696
- if (exitCode !== 0) {
39697
- const parsedOnError = finalizeParseState(parseState);
39698
- const errorContent = parsedOnError.error || stderr || `Exit code ${exitCode}`;
39699
- getSafeLogger2()?.warn("acp-adapter", `Session prompt exited with code ${exitCode}`, {
39700
- exitCode,
39701
- error: errorContent.slice(0, 500),
39702
- ...stderr && stderr !== errorContent ? { banner: stderr.trim().slice(0, 200) } : {}
39703
- });
39704
- callEndedEmitted = true;
39705
- emit?.({ ...baseEvent, kind: "agent.call_ended", status: "error", exitCode, timestamp: now() });
39706
- const errResponse = {
39707
- messages: [{ role: "assistant", content: errorContent }],
39708
- stopReason: "error",
39709
- retryable: parsedOnError.retryable,
39710
- exitCode
39711
- };
39712
- if (this._externallyCancelled)
39713
- errResponse.cancelled = true;
39714
- return errResponse;
39715
- }
39716
- try {
39717
- const parsed = finalizeParseState(parseState);
39718
- callEndedEmitted = true;
39719
- const cancelled = this._externallyCancelled;
39720
- emit?.({ ...baseEvent, kind: "agent.call_ended", status: cancelled ? "error" : "success", timestamp: now() });
39721
- const successResponse = {
39722
- messages: [{ role: "assistant", content: parsed.text || "" }],
39723
- stopReason: cancelled ? "error" : parsed.stopReason ?? "end_turn",
39724
- cumulative_token_usage: parsed.tokenUsage,
39725
- exactCostUsd: parsed.exactCostUsd,
39726
- error: parsed.error,
39727
- retryable: parsed.retryable
39728
- };
39729
- if (cancelled)
39730
- successResponse.cancelled = true;
39731
- return successResponse;
39732
- } catch (err) {
39733
- getSafeLogger2()?.warn("acp-adapter", "Failed to parse session prompt response", {
39734
- stderr: stderr.slice(0, 200)
39735
- });
39736
- throw err;
39737
- }
39738
- } catch (err) {
39739
- if (!callEndedEmitted) {
39740
- callEndedEmitted = true;
39741
- emit?.({ ...baseEvent, kind: "agent.call_ended", status: "error", timestamp: now() });
39742
- }
39743
- throw err;
39744
- } finally {
39745
- this.activeProc = null;
39746
- notifyExit();
39747
- }
39748
- }
39749
- async trackedSpawn(cmd, opts, signal) {
39750
- return runTrackedSpawn({ ..._spawnClientDeps, trackedSpawnDeadlineMs: this.trackedSpawnDeadlineMs }, cmd, opts, this.onPidSpawned, this.onPidExited, signal);
39751
- }
39752
- killActiveProcTree() {
39753
- if (!this.activeProc)
39754
- return;
39755
- killProcessTree(this.activeProc.pid, _spawnClientDeps.killTreeGraceMs, this.activeProc.exited);
39756
- }
39757
- async close(options) {
39758
- if (this.activeProc) {
39759
- this.killActiveProcTree();
39760
- this.activeProc = null;
39761
- }
39762
- const cmd = ["acpx", "--cwd", this.cwd, this.agentName, "sessions", "close", this.sessionName];
39763
- getSafeLogger2()?.debug("acp-adapter", `Closing session: ${this.sessionName}`);
39764
- const { exitCode, stderr } = await this.trackedSpawn(cmd, undefined, options?.signal);
39765
- if (exitCode !== 0) {
39766
- getSafeLogger2()?.warn("acp-adapter", "Failed to close session", {
39767
- sessionName: this.sessionName,
39768
- stderr: stderr.slice(0, 200)
39769
- });
39770
- }
39771
- if (options?.forceTerminate) {
39772
- try {
39773
- await this.trackedSpawn(["acpx", "--cwd", this.cwd, this.agentName, "stop"], undefined, options?.signal);
39774
- } catch (err) {
39775
- getSafeLogger2()?.debug("acp-adapter", "acpx stop failed (swallowed)", { cause: String(err) });
39776
- }
39777
- }
39778
- }
39779
- async cancelActivePrompt() {
39780
- this._externallyCancelled = true;
39781
- if (this.activeProc) {
39782
- this.killActiveProcTree();
39783
- this.activeProc = null;
39784
- }
39785
- const cmd = ["acpx", "--cwd", this.cwd, this.agentName, "cancel"];
39786
- getSafeLogger2()?.debug("acp-adapter", `Cancelling active prompt: ${this.sessionName}`);
39787
- await this.trackedSpawn(cmd);
39788
- }
39789
- }
39790
- var init_spawn_client_session = __esm(() => {
39791
- init_agents();
39792
- init_logger2();
39793
- init_spawn_client_deps();
39794
- init_spawn_client_process();
39795
- init_stdout_line_reader();
39796
- });
39797
-
39798
- // src/agents/acp/spawn-client.ts
39799
- class SpawnAcpClient {
39800
- model;
39801
- rawModel;
39802
- reasoningEffort;
39803
- cwd;
39804
- timeoutSeconds;
39805
- promptRetries;
39806
- env;
39807
- onPidSpawned;
39808
- onPidExited;
39809
- onStreamActivity;
39810
- onActiveCall;
39811
- runId;
39812
- storyId;
39813
- stage;
39814
- trackedSpawnDeadlineMs;
39815
- trackedSpawnStartupDeadlineMs;
39816
- constructor(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts) {
39817
- const parts = cmdStr.split(/\s+/);
39818
- const modelIdx = parts.indexOf("--model");
39819
- const rawModel = modelIdx >= 0 && parts[modelIdx + 1] ? parts[modelIdx + 1] : "default";
39820
- const spec = parseModelSpec(rawModel);
39821
- this.rawModel = rawModel;
39822
- this.model = spec.model;
39823
- this.reasoningEffort = spec.effort;
39824
- const lastToken = parts[parts.length - 1];
39825
- if (!lastToken || lastToken.startsWith("-")) {
39826
- throw new NaxError(`[acp-adapter] Could not parse agentName from cmdStr: "${cmdStr}"`, "ACP_AGENT_NAME_PARSE_FAILED", {
39827
- stage: "session",
39828
- cmdStr
39829
- });
39830
- }
39831
- if (!cwd) {
39832
- throw new NaxError("[acp-adapter] SpawnAcpClient requires cwd", "ACP_CWD_REQUIRED", { stage: "session" });
39833
- }
39834
- this.cwd = cwd;
39835
- this.timeoutSeconds = timeoutSeconds ?? DEFAULT_ACP_TIMEOUT_SECONDS;
39836
- this.promptRetries = promptRetries ?? 0;
39837
- this.env = buildAllowedEnv({ modelEnv: opts?.env });
39838
- this.onPidSpawned = onPidSpawned;
39839
- this.onPidExited = onPidExited;
39840
- this.onStreamActivity = opts?.onStreamActivity;
39841
- this.onActiveCall = opts?.onActiveCall;
39842
- this.runId = opts?.runId;
39843
- this.storyId = opts?.storyId;
39844
- this.stage = opts?.stage;
39845
- this.trackedSpawnDeadlineMs = opts?.trackedSpawnDeadlineMs ?? _spawnClientDeps.trackedSpawnDeadlineMs;
39846
- this.trackedSpawnStartupDeadlineMs = opts?.trackedSpawnStartupDeadlineMs ?? _spawnClientDeps.trackedSpawnStartupDeadlineMs;
39847
- }
39848
- async start() {}
39849
- async trackedSpawn(cmd, signal, deadlineMsOverride) {
39850
- return runTrackedSpawn({ ..._spawnClientDeps, trackedSpawnDeadlineMs: deadlineMsOverride ?? this.trackedSpawnStartupDeadlineMs }, cmd, undefined, this.onPidSpawned, this.onPidExited, signal);
39851
- }
39852
- async createSession(opts) {
39853
- const sessionName = opts.sessionName || `nax-${Date.now()}`;
39854
- const cmd = [
39855
- "acpx",
39856
- "--cwd",
39857
- this.cwd,
39858
- "--format",
39859
- "json",
39860
- opts.agentName,
39861
- "sessions",
39862
- "ensure",
39863
- "--name",
39864
- sessionName
39865
- ];
39866
- getSafeLogger2()?.debug("acp-adapter", `Ensuring session: ${sessionName}`);
39867
- const { exitCode, stdout, stderr } = await this.trackedSpawn(cmd);
39868
- if (exitCode !== 0) {
39869
- throw new NaxError(`[acp-adapter] Failed to create session: ${stdout || stderr || `exit code ${exitCode}`}`, "ACP_SESSION_CREATE_FAILED", { stage: "session", agentName: opts.agentName, exitCode });
39870
- }
39871
- const { sessionId, recordId } = parseSessionIds(stdout);
39872
- await applyReasoningEffort({
39873
- effort: this.reasoningEffort,
39874
- agentName: opts.agentName,
39875
- sessionName,
39876
- cwd: this.cwd,
39877
- storyId: this.storyId,
39878
- spawn: (c) => this.trackedSpawn(c)
39879
- });
39880
- const { SpawnAcpSession: SpawnAcpSession2 } = await Promise.resolve().then(() => (init_spawn_client_session(), exports_spawn_client_session));
39881
- return new SpawnAcpSession2({
39882
- agentName: opts.agentName,
39883
- sessionName,
39884
- cwd: this.cwd,
39885
- model: this.model,
39886
- modelLabel: this.rawModel,
39887
- timeoutSeconds: this.timeoutSeconds,
39888
- promptRetries: this.promptRetries,
39889
- permissionMode: opts.permissionMode,
39890
- env: this.env,
39891
- onPidSpawned: this.onPidSpawned,
39892
- onPidExited: this.onPidExited,
39893
- onStreamActivity: this.onStreamActivity,
39894
- onActiveCall: this.onActiveCall,
39895
- runId: this.runId,
39896
- storyId: this.storyId,
39897
- stage: this.stage,
39898
- id: sessionId,
39899
- recordId,
39900
- trackedSpawnDeadlineMs: this.trackedSpawnDeadlineMs
39901
- });
39902
- }
39903
- async loadSession(sessionName, agentName, permissionMode) {
39904
- const cmd = ["acpx", "--cwd", this.cwd, "--format", "json", agentName, "sessions", "ensure", "--name", sessionName];
39905
- const { exitCode, stdout } = await this.trackedSpawn(cmd);
39906
- if (exitCode !== 0) {
39907
- if (exitCode === -1) {
39908
- getSafeLogger2()?.warn("acp-adapter", "sessions ensure hit its trackedSpawn deadline \u2014 falling back to a context-less session", { sessionName, agentName });
39909
- }
39910
- return null;
39911
- }
39912
- const { sessionId, recordId } = parseSessionIds(stdout);
39913
- await applyReasoningEffort({
39914
- effort: this.reasoningEffort,
39915
- agentName,
39916
- sessionName,
39917
- cwd: this.cwd,
39918
- storyId: this.storyId,
39919
- spawn: (c) => this.trackedSpawn(c)
39920
- });
39921
- const { SpawnAcpSession: SpawnAcpSession2 } = await Promise.resolve().then(() => (init_spawn_client_session(), exports_spawn_client_session));
39922
- return new SpawnAcpSession2({
39923
- agentName,
39924
- sessionName,
39925
- cwd: this.cwd,
39926
- model: this.model,
39927
- modelLabel: this.rawModel,
39928
- timeoutSeconds: this.timeoutSeconds,
39929
- promptRetries: this.promptRetries,
39930
- permissionMode,
39931
- env: this.env,
39932
- onPidSpawned: this.onPidSpawned,
39933
- onPidExited: this.onPidExited,
39934
- onStreamActivity: this.onStreamActivity,
39935
- onActiveCall: this.onActiveCall,
39936
- runId: this.runId,
39937
- storyId: this.storyId,
39938
- stage: this.stage,
39939
- id: sessionId,
39940
- recordId,
39941
- trackedSpawnDeadlineMs: this.trackedSpawnDeadlineMs
39942
- });
39943
- }
39944
- async closeSession(sessionName, agentName, signal) {
39945
- const cmd = ["acpx", "--cwd", this.cwd, agentName, "sessions", "close", sessionName];
39946
- const { exitCode, stderr } = await this.trackedSpawn(cmd, signal, this.trackedSpawnDeadlineMs);
39947
- if (exitCode !== 0) {
39948
- getSafeLogger2()?.debug("acp-adapter", "Session close failed (ignored)", {
39949
- sessionName,
39950
- agentName,
39951
- exitCode,
39952
- stderr: stderr.slice(0, 200)
39953
- });
39954
- }
39955
- }
39956
- async forceStop(agentName, signal) {
39957
- const cmd = ["acpx", "--cwd", this.cwd, agentName, "stop"];
39958
- const { exitCode, stderr } = await this.trackedSpawn(cmd, signal, this.trackedSpawnDeadlineMs);
39959
- if (exitCode !== 0) {
39960
- getSafeLogger2()?.debug("acp-adapter", "forceStop failed (ignored)", {
39961
- agentName,
39962
- exitCode,
39963
- stderr: stderr.slice(0, 200)
39964
- });
39965
- }
39966
- }
39967
- async close() {}
39968
- }
39969
- function createSpawnAcpClient(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts) {
39970
- return new SpawnAcpClient(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts);
39971
- }
39972
- var DEFAULT_ACP_TIMEOUT_SECONDS = 1800;
39973
- var init_spawn_client = __esm(() => {
39974
- init_src();
39975
- init_errors4();
39976
- init_logger2();
39977
- init_env();
39978
- init_reasoning_effort();
39979
- init_spawn_client_deps();
39980
- init_spawn_client_process();
39981
- init_spawn_client_deps();
39982
- });
39983
-
39984
- // src/agents/acp/adapter-lifecycle.ts
39985
- async function ensureAcpSession(client2, sessionName, agentName, permissionMode) {
39986
- if (!agentName) {
39987
- throw new NaxError("[acp-adapter] agentName is required for ensureAcpSession", "ACP_AGENT_NAME_REQUIRED", {
39988
- stage: "open-session",
39989
- sessionName
39990
- });
39991
- }
39992
- if (client2.cwd && !await _acpAdapterDeps.cwdExists(client2.cwd)) {
39993
- throw new NaxError(`[acp-adapter] Session cwd does not exist: ${client2.cwd} \u2014 cannot start agent "${agentName}". If this is a new package for the feature, ensure its directory is created before the run.`, "SESSION_CWD_MISSING", { stage: "open-session", agentName, cwd: client2.cwd, sessionName });
39994
- }
39995
- if (client2.loadSession) {
39996
- try {
39997
- const existing2 = await client2.loadSession(sessionName, agentName, permissionMode);
39998
- if (existing2) {
39999
- getSafeLogger2()?.debug("acp-adapter", `Resumed existing session: ${sessionName}`);
40000
- return { session: existing2, resumed: true };
40001
- }
40002
- } catch {}
40003
- }
40004
- getSafeLogger2()?.debug("acp-adapter", `Creating new session: ${sessionName}`);
40005
- return { session: await client2.createSession({ agentName, permissionMode, sessionName }), resumed: false };
40006
- }
40007
- async function runSessionPrompt(session2, prompt, timeoutMs, signal) {
40008
- throwIfAborted(signal, "Run aborted \u2014 shutdown in progress");
40009
- const promptPromise = session2.prompt(prompt);
40010
- let timeoutId;
40011
- const timeoutPromise = new Promise((resolve21) => {
40012
- timeoutId = setTimeout(() => resolve21("timeout"), timeoutMs);
40013
- });
40014
- let abortHandler;
40015
- const abortPromise = signal ? new Promise((resolve21) => {
40016
- abortHandler = () => resolve21("aborted");
40017
- signal.addEventListener("abort", abortHandler, { once: true });
40018
- }) : null;
40019
- let winner;
40020
- try {
40021
- winner = await Promise.race([promptPromise, timeoutPromise, ...abortPromise !== null ? [abortPromise] : []]);
40022
- } finally {
40023
- clearTimeout(timeoutId);
40024
- if (signal && abortHandler) {
40025
- signal.removeEventListener("abort", abortHandler);
40026
- }
40027
- }
40028
- if (winner === "timeout" || winner === "aborted") {
40029
- promptPromise.catch(() => {});
40030
- try {
40031
- await session2.cancelActivePrompt();
40032
- } catch {
40033
- await session2.close().catch(() => {});
40034
- }
40035
- return { response: null, timedOut: winner === "timeout", aborted: winner === "aborted" };
40036
- }
40037
- return { response: winner, timedOut: false, aborted: false };
40038
- }
40039
- async function closeAcpSession(session2) {
40040
- try {
40041
- await session2.close();
40042
- } catch (err) {
40043
- getSafeLogger2()?.warn("acp-adapter", "Failed to close session", { error: String(err) });
40044
- }
40045
- }
40046
-
40047
- class AcpSessionHandleImpl {
40048
- id;
40049
- agentName;
40050
- protocolIds;
40051
- modelDef;
40052
- modelTier;
40053
- _client;
40054
- _session;
40055
- _sessionName;
40056
- _resumed;
40057
- _timeoutSeconds;
40058
- _modelDef;
40059
- _rateCard;
40060
- _permissionMode;
40061
- constructor(opts) {
40062
- this.id = opts.id;
40063
- this.agentName = opts.agentName;
40064
- this.protocolIds = opts.protocolIds;
40065
- this._client = opts.client;
40066
- this._session = opts.session;
40067
- this._sessionName = opts.sessionName;
40068
- this._resumed = opts.resumed;
40069
- this._timeoutSeconds = opts.timeoutSeconds;
40070
- this._modelDef = opts.modelDef;
40071
- this.modelDef = opts.modelDef;
40072
- this.modelTier = opts.modelTier;
40073
- this._rateCard = opts.rateCard;
40074
- this._permissionMode = opts.permissionMode;
40075
- }
40076
- }
40077
- var _acpAdapterDeps, _fallbackDeps;
40078
- var init_adapter_lifecycle = __esm(() => {
40079
- init_cost();
40080
- init_errors4();
40081
- init_logger2();
40082
- init_bun_deps();
40083
- init_errors5();
40084
- init_turn();
40085
- init_spawn_client();
40086
- _acpAdapterDeps = {
40087
- which: which2,
40088
- sleep,
40089
- async cwdExists(dir) {
40090
- const { stat: stat7 } = await import("fs/promises");
40091
- try {
40092
- return (await stat7(dir)).isDirectory();
40093
- } catch {
40094
- return false;
40095
- }
40096
- },
40097
- createClient(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts) {
40098
- return createSpawnAcpClient(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts);
40099
- },
40100
- resolveRateCard(modelId) {
40101
- return resolveRateCard(modelId);
40102
- }
40103
- };
40104
- _fallbackDeps = {
40105
- parseAgentError,
40106
- sleep
40107
- };
40108
- });
40109
-
40110
- // src/agents/acp/adapter-close-physical.ts
40111
- async function closePhysicalSession(agentName, handle, workdir, options) {
40112
- const cmdStr = `acpx ${agentName}`;
40113
- const client2 = _acpAdapterDeps.createClient(cmdStr, workdir, undefined, undefined);
40114
- try {
40115
- await client2.start();
40116
- try {
40117
- if (client2.closeSession) {
40118
- await client2.closeSession(handle, agentName, options?.signal);
40119
- if (options?.force) {
40120
- await client2.forceStop?.(agentName, options?.signal).catch(() => {});
40121
- }
40122
- } else if (client2.loadSession) {
40123
- const session2 = await client2.loadSession(handle, agentName, SESSION_CLOSE_PERMISSION_MODE);
40124
- if (session2)
40125
- await session2.close({ forceTerminate: options?.force, signal: options?.signal }).catch(() => {});
40126
- }
40127
- } catch (err) {
40128
- getSafeLogger2()?.warn("acp-adapter", `[close] Failed to close session ${handle}`, { error: String(err) });
40129
- }
40130
- } finally {
40131
- await client2.close().catch(() => {});
40132
- }
40133
- }
40134
- var init_adapter_close_physical = __esm(() => {
40135
- init_config();
40136
- init_logger2();
40137
- init_adapter_lifecycle();
40138
- });
40139
-
40140
- // src/agents/session-naming.ts
40141
- import { createHash as createHash6 } from "crypto";
40142
- function computeAcpHandle(workdir, featureName, storyId, sessionRole) {
40143
- const hash2 = createHash6("sha256").update(workdir).digest("hex").slice(0, 8);
40144
- const sanitize = (s) => s.replace(/[^a-z0-9]+/gi, "-").toLowerCase().replace(/^-+|-+$/g, "");
40145
- const parts = ["nax", hash2];
40146
- if (featureName)
40147
- parts.push(sanitize(featureName));
40148
- if (storyId)
40149
- parts.push(sanitize(storyId));
40150
- if (sessionRole)
40151
- parts.push(sanitize(sessionRole));
40152
- return parts.join("-");
40153
- }
40154
- var init_session_naming = () => {};
40155
-
40156
- // src/agents/types.ts
40157
- var CompleteError, SessionFailureError;
40158
- var init_types4 = __esm(() => {
40159
- init_src();
40160
- CompleteError = class CompleteError extends Error {
40161
- exitCode;
40162
- retryable;
40163
- constructor(message, exitCode, retryable) {
40164
- super(message);
40165
- this.exitCode = exitCode;
40166
- this.retryable = retryable;
40167
- this.name = "CompleteError";
40168
- }
40169
- };
40170
- SessionFailureError = class SessionFailureError extends Error {
40171
- adapterFailure;
40172
- constructor(message, adapterFailure) {
40173
- super(message);
40174
- this.adapterFailure = adapterFailure;
40175
- this.name = "SessionFailureError";
40176
- }
40177
- };
40178
- });
40179
-
40180
- // src/agents/acp/adapter-output.ts
40181
- function extractOutput(response) {
40182
- if (!response)
40183
- return "";
40184
- return response.messages.filter((m) => m.role === "assistant").map((m) => m.content).join(`
40185
- `).trim();
40186
- }
40187
- function deriveTokenUsage(wire, rateCard, mapper) {
40188
- const tokenUsage = wire ? mapper.toInternal(wire) : { inputTokens: 0, outputTokens: 0 };
40189
- const nonzeroUsage = tokenUsage.inputTokens > 0 || tokenUsage.outputTokens > 0;
40190
- const priced = nonzeroUsage ? priceCall(tokenUsage, rateCard.rates) : undefined;
40191
- return {
40192
- tokenUsage,
40193
- estimatedCostUsd: priced?.costUsd ?? 0,
40194
- rates: priced?.resolvedRates
40195
- };
38791
+ // src/utils/bun-deps.ts
38792
+ function typedSpawn(cmd, opts) {
38793
+ return Bun.spawn(cmd, opts);
40196
38794
  }
40197
- function buildTurnResult2(input) {
40198
- const { lastResponse, ...rest } = input;
40199
- return assembleTurnResult({ ...rest, output: extractOutput(lastResponse) });
38795
+ function sleep(ms) {
38796
+ return Bun.sleep(ms);
40200
38797
  }
40201
- var init_adapter_output = __esm(() => {
40202
- init_cost();
40203
- init_turn();
40204
- });
40205
-
40206
- // src/agents/acp/adapter-complete-flow.ts
40207
- async function runCompleteFlow(input) {
40208
- const { adapter, prompt, options, mapper, rateCard, createClient: createClient2 } = input;
40209
- const tryOneAgent = async (agentName) => {
40210
- const cmdStr = `acpx --model ${options.modelDef.model} ${agentName}`;
40211
- const timeoutMs = options.timeoutMs ?? 120000;
40212
- const timeoutSeconds = Math.ceil(timeoutMs / 1000);
40213
- const client2 = createClient2(cmdStr, options.workdir, timeoutSeconds, options.onPidSpawned, options.promptRetries, options.onPidExited, {
40214
- onStreamActivity: options.onStreamActivity,
40215
- onActiveCall: options.onActiveCall,
40216
- trackedSpawnDeadlineMs: options.trackedSpawnDeadlineMs,
40217
- trackedSpawnStartupDeadlineMs: options.trackedSpawnStartupDeadlineMs,
40218
- env: options.modelDef.env
40219
- });
40220
- await client2.start();
40221
- let session2 = null;
40222
- try {
40223
- const completeSessionName = options.sessionName ?? computeAcpHandle(options.workdir, options.featureName, options.storyId, options.sessionRole);
40224
- session2 = await client2.createSession({
40225
- agentName,
40226
- permissionMode: options.resolvedPermissions.mode,
40227
- sessionName: completeSessionName
40228
- });
40229
- let timeoutId;
40230
- const timeoutPromise = new Promise((_, reject) => {
40231
- timeoutId = setTimeout(() => reject(new NaxError("complete() timed out", "AGENT_TIMEOUT", { stage: "acp", timeoutMs })), timeoutMs);
40232
- });
40233
- timeoutPromise.catch(() => {});
40234
- let response;
40235
- try {
40236
- const promptPromise = session2.prompt(prompt);
40237
- promptPromise.catch(() => {});
40238
- response = await Promise.race([promptPromise, timeoutPromise]);
40239
- } finally {
40240
- clearTimeout(timeoutId);
40241
- }
40242
- if (response.stopReason === "error") {
40243
- if (response.cancelled) {
40244
- const { tokenUsage: tokenUsage2, estimatedCostUsd: estimatedCostUsd2, rates: rates2 } = deriveTokenUsage(response.cumulative_token_usage, rateCard, mapper);
40245
- return {
40246
- output: "",
40247
- tokenUsage: tokenUsage2,
40248
- estimatedCostUsd: estimatedCostUsd2,
40249
- exactCostUsd: response.exactCostUsd,
40250
- cancelled: true,
40251
- pricingSource: rateCard.source,
40252
- ...rates2 !== undefined ? { rates: rates2 } : {}
40253
- };
40254
- }
40255
- const errSuffix = response.error ? `: ${response.error.slice(0, 500)}` : "";
40256
- throw new CompleteError(`complete() failed: stop reason is error${errSuffix}`, undefined, response.retryable);
40257
- }
40258
- const text = response.messages.filter((m) => m.role === "assistant").map((m) => m.content).join(`
40259
- `).trim();
40260
- let unwrapped = text;
40261
- try {
40262
- const envelope = JSON.parse(text);
40263
- if (envelope?.type === "result" && typeof envelope?.result === "string") {
40264
- unwrapped = envelope.result;
40265
- }
40266
- } catch {}
40267
- if (!unwrapped) {
40268
- throw new CompleteError("complete() returned empty output");
40269
- }
40270
- const { tokenUsage, estimatedCostUsd, rates } = deriveTokenUsage(response.cumulative_token_usage, rateCard, mapper);
40271
- const exactCostUsd = response.exactCostUsd;
40272
- if (exactCostUsd !== undefined) {
40273
- getSafeLogger2()?.info("acp-adapter", "complete() cost", {
40274
- costUsd: exactCostUsd,
40275
- model: options.modelDef.model
40276
- });
40277
- }
40278
- return {
40279
- output: unwrapped,
40280
- tokenUsage,
40281
- estimatedCostUsd,
40282
- exactCostUsd,
40283
- pricingSource: rateCard.source,
40284
- ...rates !== undefined ? { rates } : {}
40285
- };
40286
- } finally {
40287
- if (session2) {
40288
- await session2.close({ forceTerminate: true }).catch(() => {});
40289
- }
40290
- await client2.close().catch(() => {});
40291
- }
40292
- };
40293
- try {
40294
- return await tryOneAgent(adapter.name);
40295
- } catch (err) {
40296
- const error48 = err instanceof Error ? err : new Error(String(err));
40297
- if (error48 instanceof CompleteError) {
40298
- const classified = classifyCompleteError(error48, rateCard.source);
40299
- if (classified)
40300
- return classified;
38798
+ function cancellableDelay(ms, signal) {
38799
+ return new Promise((resolve21, reject) => {
38800
+ if (signal?.aborted) {
38801
+ reject(signal.reason ?? new Error("delay aborted"));
38802
+ return;
40301
38803
  }
40302
- const degraded = classifyParsedAgentError(_fallbackDeps.parseAgentError(error48.message), error48.message, rateCard.source);
40303
- if (degraded)
40304
- return degraded;
40305
- throw err;
40306
- }
40307
- }
40308
- var init_adapter_complete_flow = __esm(() => {
40309
- init_errors4();
40310
- init_logger2();
40311
- init_errors5();
40312
- init_session_naming();
40313
- init_types4();
40314
- init_adapter_lifecycle();
40315
- init_adapter_output();
40316
- });
40317
-
40318
- // src/agents/interaction/output-parsing.ts
40319
- function extractQuestion(output) {
40320
- const text = output.trim();
40321
- if (!text)
40322
- return null;
40323
- const lines = text.split(`
40324
- `).filter((l) => l.trim().length > 0);
40325
- const lastLine = lines.at(-1)?.trim() ?? "";
40326
- const lower = lastLine.toLowerCase();
40327
- const markers = [
40328
- "please confirm",
40329
- "please specify",
40330
- "please provide",
40331
- "which would you",
40332
- "should i ",
40333
- "do you want",
40334
- "can you clarify"
40335
- ];
40336
- const isQuestion = lastLine.endsWith("?") && lastLine.length > 10 || markers.some((m) => lower.includes(m));
40337
- if (!isQuestion)
40338
- return null;
40339
- const paragraphs = text.split(/\n\n+/);
40340
- const questionPara = paragraphs.at(-1)?.trim() ?? lastLine;
40341
- const contextPara = paragraphs.at(-2)?.trim();
40342
- return contextPara ? `${contextPara}
40343
-
40344
- ${questionPara}` : questionPara;
40345
- }
40346
- function extractContextToolCall(output) {
40347
- const match = output.match(CONTEXT_TOOL_CALL_PATTERN);
40348
- if (!match)
40349
- return null;
40350
- const [, name, rawInput] = match;
40351
- const trimmedInput = rawInput.trim();
40352
- if (!trimmedInput) {
40353
- return { name, input: {} };
40354
- }
40355
- try {
40356
- return { name, input: JSON.parse(trimmedInput) };
40357
- } catch (error48) {
40358
- return {
40359
- name,
40360
- error: `Invalid JSON tool input: ${error48 instanceof Error ? error48.message : String(error48)}`
38804
+ const timer = setTimeout(() => {
38805
+ signal?.removeEventListener("abort", onAbort);
38806
+ resolve21();
38807
+ }, ms);
38808
+ const onAbort = () => {
38809
+ clearTimeout(timer);
38810
+ reject(signal?.reason ?? new Error("delay aborted"));
40361
38811
  };
40362
- }
40363
- }
40364
- var CONTEXT_TOOL_CALL_PATTERN;
40365
- var init_output_parsing = __esm(() => {
40366
- CONTEXT_TOOL_CALL_PATTERN = /<nax_tool_call\s+name="([^"]+)">\s*([\s\S]*?)\s*<\/nax_tool_call>/i;
40367
- });
40368
-
40369
- // src/agents/interaction/turn-interactions.ts
40370
- async function awaitInteractionReply(ctx, interaction, warnSuffix) {
40371
- let timeoutId;
40372
- try {
40373
- const response = await Promise.race([
40374
- raceWithAbort(ctx.interactionHandler.onInteraction(interaction), ctx.signal, INTERACTION_ABORT_MESSAGE),
40375
- new Promise((resolve21) => {
40376
- timeoutId = setTimeout(() => resolve21(null), ctx.timeoutMs ?? INTERACTION_TIMEOUT_MS);
40377
- })
40378
- ]);
40379
- if (response) {
40380
- return { kind: "answered", answer: response.answer };
40381
- }
40382
- } catch (err) {
40383
- if (ctx.signal?.aborted) {
40384
- return { kind: "aborted" };
40385
- }
40386
- getSafeLogger2()?.warn(ctx.stage, `InteractionHandler.onInteraction failed${warnSuffix}${err instanceof Error ? err.message : String(err)}`);
40387
- } finally {
40388
- clearTimeout(timeoutId);
40389
- }
40390
- return { kind: "no-reply" };
40391
- }
40392
- function toContextToolInteraction(toolCall) {
40393
- return toolCall.error ? { kind: "context-tool", name: toolCall.name, error: toolCall.error } : { kind: "context-tool", name: toolCall.name, input: toolCall.input };
40394
- }
40395
- var INTERACTION_TIMEOUT_MS, INTERACTION_ABORT_MESSAGE = "Run aborted \u2014 shutdown in progress";
40396
- var init_turn_interactions = __esm(() => {
40397
- init_logger2();
40398
- init_turn();
40399
- INTERACTION_TIMEOUT_MS = 5 * 60 * 1000;
40400
- });
40401
-
40402
- // src/agents/interaction/index.ts
40403
- var init_interaction = __esm(() => {
40404
- init_output_parsing();
40405
- init_turn_interactions();
40406
- });
40407
-
40408
- // src/agents/acp/adapter-send-turn.ts
40409
- function buildSendTurnFrame(input) {
40410
- const { impl, mapper, opts } = input;
40411
- const { _sessionName: sessionName, _timeoutSeconds: timeoutSeconds, _rateCard: rateCard } = impl;
40412
- return {
40413
- impl,
40414
- mapper,
40415
- opts,
40416
- sessionName,
40417
- timeoutSeconds,
40418
- rateCard,
40419
- maxInteractions: opts.maxInteractions ?? DEFAULT_MAX_INTERACTIONS,
40420
- turnDeadline: createTurnDeadline(timeoutSeconds)
40421
- };
40422
- }
40423
- function initialSendTurnState(prompt) {
40424
- return {
40425
- sessionRecreated: false,
40426
- totalTokenUsage: { inputTokens: 0, outputTokens: 0 },
40427
- totalExactCostUsd: undefined,
40428
- interactions: [],
40429
- turnCount: 0,
40430
- lastResponse: null,
40431
- timedOut: false,
40432
- aborted: false,
40433
- currentPrompt: prompt
40434
- };
40435
- }
40436
- function zeroCostAbortedResult(frame) {
40437
- return {
40438
- output: "",
40439
- tokenUsage: { inputTokens: 0, outputTokens: 0 },
40440
- estimatedCostUsd: 0,
40441
- internalRoundTrips: 0,
40442
- pricingSource: frame.rateCard.source
40443
- };
40444
- }
40445
- async function beginTurnIteration(frame, state) {
40446
- if (frame.turnDeadline.expired()) {
40447
- state.timedOut = true;
40448
- warnWallClockTimeout(frame.sessionName, frame.timeoutSeconds, "acp-adapter");
40449
- return { kind: "break" };
40450
- }
40451
- state.turnCount++;
40452
- getSafeLogger2()?.debug("acp-adapter", `Session turn ${state.turnCount}/${frame.maxInteractions}`, {
40453
- sessionName: frame.sessionName
40454
- });
40455
- const turnResult = await runSessionPrompt(frame.impl._session, state.currentPrompt, frame.turnDeadline.remainingMs() ?? 0, frame.opts.signal);
40456
- if (turnResult.timedOut) {
40457
- state.timedOut = true;
40458
- warnWallClockTimeout(frame.sessionName, frame.timeoutSeconds, "acp-adapter");
40459
- return { kind: "break" };
40460
- }
40461
- if (turnResult.aborted) {
40462
- state.aborted = true;
40463
- return { kind: "break" };
40464
- }
40465
- state.lastResponse = turnResult.response;
40466
- if (!state.lastResponse)
40467
- return { kind: "break" };
40468
- return { kind: "response", response: state.lastResponse };
40469
- }
40470
- async function recoverNoSession(frame, state) {
40471
- if (state.lastResponse?.exitCode !== 4 || state.sessionRecreated) {
40472
- return false;
40473
- }
40474
- state.sessionRecreated = true;
40475
- getSafeLogger2()?.info("acp-adapter", "NO_SESSION detected \u2014 re-establishing session", {
40476
- sessionName: frame.sessionName
40477
- });
40478
- try {
40479
- const ensured = await ensureAcpSession(frame.impl._client, frame.impl._sessionName, frame.impl.agentName, frame.impl._permissionMode);
40480
- frame.impl._session = ensured.session;
40481
- state.turnCount--;
40482
- return true;
40483
- } catch (err) {
40484
- getSafeLogger2()?.warn("acp-adapter", "Session re-establishment failed after NO_SESSION", {
40485
- sessionName: frame.sessionName,
40486
- error: err instanceof Error ? err.message : String(err)
40487
- });
40488
- return false;
40489
- }
40490
- }
40491
- function accumulateUsage(frame, state, response) {
40492
- if (response.cumulative_token_usage) {
40493
- state.totalTokenUsage = addTokenUsage(state.totalTokenUsage, frame.mapper.toInternal(response.cumulative_token_usage));
40494
- }
40495
- if (response.exactCostUsd !== undefined) {
40496
- state.totalExactCostUsd = (state.totalExactCostUsd ?? 0) + response.exactCostUsd;
40497
- }
40498
- }
40499
- function replyContext(frame) {
40500
- return { interactionHandler: frame.opts.interactionHandler, signal: frame.opts.signal, stage: "acp-adapter" };
40501
- }
40502
- async function handleContextToolCall(frame, state, toolCall) {
40503
- const interaction = toContextToolInteraction(toolCall);
40504
- const reply = await awaitInteractionReply(replyContext(frame), interaction, " for context-tool: ");
40505
- if (reply.kind === "answered") {
40506
- state.currentPrompt = reply.answer;
40507
- return true;
40508
- }
40509
- if (reply.kind === "aborted") {
40510
- state.aborted = true;
40511
- }
40512
- return false;
40513
- }
40514
- async function handleQuestion(frame, state, question) {
40515
- const reply = await awaitInteractionReply(replyContext(frame), { kind: "question", text: question }, ": ");
40516
- if (reply.kind === "answered") {
40517
- state.interactions.push({ turnIndex: state.turnCount, question, reply: reply.answer });
40518
- state.currentPrompt = reply.answer;
40519
- return true;
40520
- }
40521
- if (reply.kind === "aborted") {
40522
- state.aborted = true;
40523
- }
40524
- return false;
40525
- }
40526
- async function processResponseInteractions(frame, state, response) {
40527
- const outputText = extractOutput(response);
40528
- const isEndTurn = response.stopReason === "end_turn";
40529
- if (isEndTurn) {
40530
- const toolCall = extractContextToolCall(outputText);
40531
- if (toolCall) {
40532
- return await handleContextToolCall(frame, state, toolCall) ? "continue" : "break";
40533
- }
40534
- const question = extractQuestion(outputText);
40535
- if (question) {
40536
- return await handleQuestion(frame, state, question) ? "continue" : "break";
40537
- }
40538
- }
40539
- return "break";
40540
- }
40541
- function maybeWarnBudgetSpent(frame, state) {
40542
- if (state.turnCount >= frame.maxInteractions && !state.timedOut && !state.aborted && frame.maxInteractions > 1) {
40543
- getSafeLogger2()?.warn("acp-adapter", "Interaction budget spent", {
40544
- sessionName: frame.sessionName,
40545
- maxInteractions: frame.maxInteractions
40546
- });
40547
- }
40548
- }
40549
- function throwIfTurnFailed(frame, state) {
40550
- const lastResponse = state.lastResponse;
40551
- if (lastResponse?.stopReason !== "error") {
40552
- return;
40553
- }
40554
- const hasUsage = state.totalTokenUsage.inputTokens > 0 || state.totalTokenUsage.outputTokens > 0;
40555
- throw new SessionTurnError(lastResponse.cancelled ? "Agent session ended with stop reason: error (externally cancelled)" : "Agent session ended with stop reason: error", lastResponse.cancelled === true, lastResponse.retryable === true, state.totalTokenUsage, hasUsage ? estimateCostUsd(state.totalTokenUsage, frame.rateCard.rates) : 0, state.totalExactCostUsd, frame.rateCard.source);
40556
- }
40557
- async function runTurnLoop(frame, state) {
40558
- while (state.turnCount < frame.maxInteractions) {
40559
- const began = await beginTurnIteration(frame, state);
40560
- if (began.kind === "break")
40561
- break;
40562
- if (await recoverNoSession(frame, state))
40563
- continue;
40564
- accumulateUsage(frame, state, began.response);
40565
- if (await processResponseInteractions(frame, state, began.response) === "break")
40566
- break;
40567
- }
40568
- maybeWarnBudgetSpent(frame, state);
40569
- throwIfTurnFailed(frame, state);
40570
- return buildTurnResult2({
40571
- lastResponse: state.lastResponse,
40572
- totalTokenUsage: state.totalTokenUsage,
40573
- totalExactCostUsd: state.totalExactCostUsd,
40574
- turnCount: state.turnCount,
40575
- interactions: state.interactions,
40576
- timedOut: state.timedOut,
40577
- rateCard: frame.rateCard
38812
+ signal?.addEventListener("abort", onAbort, { once: true });
40578
38813
  });
40579
38814
  }
40580
- var DEFAULT_MAX_INTERACTIONS = 10;
40581
- var init_adapter_send_turn = __esm(() => {
40582
- init_src();
40583
- init_logger2();
40584
- init_cost();
40585
- init_interaction();
40586
- init_turn();
40587
- init_types4();
40588
- init_adapter_lifecycle();
40589
- init_adapter_output();
40590
- });
40591
-
40592
- // src/agents/acp/agent-entries.ts
40593
- function resolveRegistryEntry(agentName) {
40594
- return AGENT_REGISTRY[agentName] ?? DEFAULT_ENTRY;
40595
- }
40596
- var AGENT_REGISTRY, DEFAULT_ENTRY, ACP_ADAPTER_NAMES;
40597
- var init_agent_entries = __esm(() => {
40598
- AGENT_REGISTRY = {
40599
- claude: {
40600
- binary: "claude",
40601
- displayName: "Claude Code (ACP)",
40602
- supportedTiers: ["fast", "balanced", "powerful"],
40603
- maxContextTokens: 200000
40604
- },
40605
- codex: {
40606
- binary: "codex",
40607
- displayName: "OpenAI Codex (ACP)",
40608
- supportedTiers: ["fast", "balanced"],
40609
- maxContextTokens: 128000
40610
- },
40611
- gemini: {
40612
- binary: "gemini",
40613
- displayName: "Gemini CLI (ACP)",
40614
- supportedTiers: ["fast", "balanced", "powerful"],
40615
- maxContextTokens: 1e6
40616
- },
40617
- opencode: {
40618
- binary: "opencode",
40619
- displayName: "opencode (ACP)",
40620
- supportedTiers: ["fast", "balanced", "powerful"],
40621
- maxContextTokens: 128000
40622
- },
40623
- pi: {
40624
- binary: "pi",
40625
- displayName: "Pi Coding Agent (ACP)",
40626
- supportedTiers: ["fast", "balanced", "powerful"],
40627
- maxContextTokens: 128000
40628
- }
40629
- };
40630
- DEFAULT_ENTRY = {
40631
- binary: "claude",
40632
- displayName: "ACP Agent",
40633
- supportedTiers: ["balanced"],
40634
- maxContextTokens: 128000
40635
- };
40636
- ACP_ADAPTER_NAMES = new Set(Object.keys(AGENT_REGISTRY));
40637
- });
40638
-
40639
- // src/agents/acp/token-mapper.ts
40640
- function toFiniteTokenCount2(value) {
40641
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
40642
- }
40643
- function toFiniteOrUndefined(value) {
40644
- if (value === undefined)
40645
- return;
40646
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
40647
- }
40648
-
40649
- class AcpTokenUsageMapper {
40650
- toInternal(wire) {
40651
- return {
40652
- inputTokens: toFiniteTokenCount2(wire.input_tokens),
40653
- outputTokens: toFiniteTokenCount2(wire.output_tokens),
40654
- cacheReadTokens: toFiniteOrUndefined(wire.cache_read_input_tokens),
40655
- cacheWriteTokens: toFiniteOrUndefined(wire.cache_creation_input_tokens)
40656
- };
40657
- }
40658
- }
40659
- var defaultAcpTokenUsageMapper;
40660
- var init_token_mapper = __esm(() => {
40661
- defaultAcpTokenUsageMapper = new AcpTokenUsageMapper;
40662
- });
40663
-
40664
- // src/agents/acp/adapter.ts
40665
- class AcpAgentAdapter {
40666
- name;
40667
- displayName;
40668
- binary;
40669
- capabilities;
40670
- _mapper;
40671
- constructor(agentName, mapper = defaultAcpTokenUsageMapper) {
40672
- const entry = resolveRegistryEntry(agentName);
40673
- this.name = agentName;
40674
- this.displayName = entry.displayName;
40675
- this.binary = entry.binary;
40676
- this._mapper = mapper;
40677
- this.capabilities = {
40678
- supportedTiers: entry.supportedTiers,
40679
- maxContextTokens: entry.maxContextTokens,
40680
- features: new Set(["tdd", "review", "refactor"])
40681
- };
40682
- }
40683
- async isInstalled() {
40684
- const path3 = _acpAdapterDeps.which(this.binary);
40685
- return path3 !== null;
40686
- }
40687
- buildCommand(_options) {
40688
- return ["acpx", this.name, "session"];
40689
- }
40690
- buildAllowedEnv(_options) {
40691
- return {};
40692
- }
40693
- async complete(prompt, options) {
40694
- const rateCard = await _acpAdapterDeps.resolveRateCard(options.modelDef.model);
40695
- return runCompleteFlow({
40696
- adapter: this,
40697
- prompt,
40698
- options,
40699
- mapper: this._mapper,
40700
- rateCard,
40701
- createClient: _acpAdapterDeps.createClient
40702
- });
40703
- }
40704
- async closePhysicalSession(handle, workdir, options) {
40705
- return closePhysicalSession(this.name, handle, workdir, options);
40706
- }
40707
- async openSession(name, opts) {
40708
- const {
40709
- agentName,
40710
- workdir,
40711
- resolvedPermissions,
40712
- modelDef,
40713
- timeoutSeconds,
40714
- promptRetries,
40715
- onSessionEstablished,
40716
- onPidSpawned,
40717
- onPidExited
40718
- } = opts;
40719
- const { signal } = opts;
40720
- throwIfAborted(signal, "Run aborted \u2014 shutdown in progress");
40721
- const rateCard = await _acpAdapterDeps.resolveRateCard(modelDef.model);
40722
- const cmdStr = `acpx --model ${modelDef.model} ${agentName}`;
40723
- const client2 = _acpAdapterDeps.createClient(cmdStr, workdir, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, {
40724
- onStreamActivity: opts.onStreamActivity,
40725
- onActiveCall: opts.onActiveCall,
40726
- trackedSpawnDeadlineMs: opts.trackedSpawnDeadlineMs,
40727
- trackedSpawnStartupDeadlineMs: opts.trackedSpawnStartupDeadlineMs,
40728
- env: modelDef.env
40729
- });
40730
- let session2;
40731
- try {
40732
- await raceWithAbort(client2.start(), signal, "Run aborted \u2014 shutdown in progress");
40733
- const permissionMode = resolvedPermissions.mode;
40734
- getSafeLogger2()?.info("acp-adapter", "Permission mode resolved", {
40735
- permission: permissionMode,
40736
- stage: "open-session"
40737
- });
40738
- const ensured = await raceWithAbort(ensureAcpSession(client2, name, agentName, permissionMode), signal, "Run aborted \u2014 shutdown in progress");
40739
- session2 = ensured.session;
40740
- const protocolIds = {
40741
- recordId: session2.recordId ?? null,
40742
- sessionId: session2.id ?? null
40743
- };
40744
- if (onSessionEstablished) {
40745
- try {
40746
- onSessionEstablished(protocolIds, name);
40747
- } catch (err) {
40748
- getSafeLogger2()?.warn("acp-adapter", "onSessionEstablished callback threw \u2014 continuing", {
40749
- sessionName: name,
40750
- error: err instanceof Error ? err.message : String(err)
40751
- });
40752
- }
40753
- }
40754
- throwIfAborted(signal, "Run aborted \u2014 shutdown in progress");
40755
- return new AcpSessionHandleImpl({
40756
- id: name,
40757
- agentName,
40758
- protocolIds,
40759
- client: client2,
40760
- session: session2,
40761
- sessionName: name,
40762
- resumed: ensured.resumed,
40763
- timeoutSeconds,
40764
- modelDef,
40765
- modelTier: opts.modelTier,
40766
- rateCard,
40767
- permissionMode: resolvedPermissions.mode
40768
- });
40769
- } catch (error48) {
40770
- if (session2) {
40771
- await closeAcpSession(session2).catch(() => {});
40772
- }
40773
- await client2.close().catch(() => {});
40774
- throw error48;
40775
- }
40776
- }
40777
- async sendTurn(handle, prompt, opts) {
40778
- const impl = handle;
40779
- const frame = buildSendTurnFrame({ impl, mapper: this._mapper, opts });
40780
- if (frame.opts.signal?.aborted) {
40781
- return zeroCostAbortedResult(frame);
40782
- }
40783
- return runTurnLoop(frame, initialSendTurnState(prompt));
40784
- }
40785
- async closeSession(handle) {
40786
- const impl = handle;
40787
- try {
40788
- await closeAcpSession(impl._session);
40789
- } finally {
40790
- await impl._client.close().catch(() => {});
40791
- }
40792
- }
40793
- }
40794
- var init_adapter = __esm(() => {
40795
- init_logger2();
40796
- init_turn();
40797
- init_adapter_close_physical();
40798
- init_adapter_complete_flow();
40799
- init_adapter_lifecycle();
40800
- init_adapter_send_turn();
40801
- init_agent_entries();
40802
- init_token_mapper();
40803
- init_adapter_lifecycle();
40804
- init_adapter_output();
40805
- init_agent_entries();
40806
- });
40807
-
40808
- // src/agents/acp/index.ts
40809
- var init_acp = __esm(() => {
40810
- init_adapter();
40811
- init_parser();
40812
- init_spawn_client();
40813
- init_stdout_line_reader();
40814
- init_token_mapper();
40815
- });
40816
-
40817
- // src/agents/transport-failure-message.ts
40818
- function isTransportFailureMessage(message) {
40819
- if (!message)
40820
- return false;
40821
- const haystack = message.toLowerCase();
40822
- return TRANSPORT_FAULT_MARKERS.some((marker) => haystack.includes(marker));
40823
- }
40824
- var TRANSPORT_FAULT_MARKERS;
40825
- var init_transport_failure_message = __esm(() => {
40826
- TRANSPORT_FAULT_MARKERS = [
40827
- "idle timeout exceeded",
40828
- "stream idle timeout",
40829
- "upstream timeout",
40830
- "bad gateway",
40831
- "service unavailable",
40832
- "gateway timeout",
40833
- "econnreset",
40834
- "econnrefused",
40835
- "econnaborted",
40836
- "etimedout",
40837
- "socket hang up",
40838
- "premature close",
40839
- "terminated: other side closed",
40840
- "network error"
40841
- ];
40842
- });
40843
-
40844
- // src/agents/complete-exception-classifier.ts
40845
- function classifyCompleteException(err) {
40846
- if ((err instanceof SessionTurnError || err instanceof SessionFailureError) && err.adapterFailure !== undefined) {
40847
- return err.adapterFailure;
40848
- }
40849
- const fullMessage = errorMessage(err);
40850
- const message = fullMessage.slice(0, MAX_FAILURE_MESSAGE_CHARS);
40851
- if (err instanceof NaxError && err.code === "AGENT_TIMEOUT") {
40852
- return { category: "quality", outcome: "fail-timeout", retriable: true, message };
40853
- }
40854
- const parsed = parseAgentError(fullMessage);
40855
- switch (parsed.type) {
40856
- case "auth":
40857
- return { category: "availability", outcome: "fail-auth", retriable: false, message };
40858
- case "rate-limit":
40859
- return {
40860
- category: "availability",
40861
- outcome: "fail-rate-limit",
40862
- retriable: true,
40863
- message,
40864
- ...parsed.retryAfterSeconds !== undefined ? { retryAfterSeconds: parsed.retryAfterSeconds } : {}
40865
- };
40866
- case "model-not-available":
40867
- return { category: "quality", outcome: "fail-adapter-error", retriable: false, message };
40868
- case "timeout":
40869
- return { category: "quality", outcome: "fail-timeout", retriable: true, message };
40870
- case "crash":
40871
- return { category: "quality", outcome: "fail-adapter-error", retriable: false, message };
40872
- default:
40873
- return isTransportFailureMessage(fullMessage) ? { category: "availability", outcome: "fail-service-down", retriable: true, message } : { category: "quality", outcome: "fail-unknown", retriable: false, message };
40874
- }
40875
- }
40876
- var MAX_FAILURE_MESSAGE_CHARS = 500;
40877
- var init_complete_exception_classifier = __esm(() => {
40878
- init_internal();
40879
- init_errors4();
40880
- init_errors5();
40881
- init_transport_failure_message();
40882
- init_types4();
38815
+ var spawn;
38816
+ var init_bun_deps = __esm(() => {
38817
+ spawn = Bun.spawn;
40883
38818
  });
40884
38819
 
40885
38820
  // src/agents/retry/failure-policy.ts
@@ -43734,14 +41669,14 @@ function analyzeTestExitCode(output, exitCode) {
43734
41669
  }
43735
41670
  return result;
43736
41671
  }
43737
- var init_parser2 = __esm(() => {
41672
+ var init_parser = __esm(() => {
43738
41673
  init_detector2();
43739
41674
  init_parse_bun();
43740
41675
  init_parse_rust();
43741
41676
  });
43742
41677
 
43743
41678
  // src/test-runners/resolver.ts
43744
- import { dirname as dirname12, isAbsolute as isAbsolute11, join as join31, relative as relative12, resolve as resolve21 } from "path";
41679
+ import { dirname as dirname12, isAbsolute as isAbsolute10, join as join31, relative as relative12, resolve as resolve21 } from "path";
43745
41680
  function buildResolved(globs, resolution) {
43746
41681
  return {
43747
41682
  globs,
@@ -43762,7 +41697,7 @@ function validateGlobs(patterns, stage) {
43762
41697
  }
43763
41698
  }
43764
41699
  async function resolveTestFilePatterns(config2, workdir, packageDir, options) {
43765
- if (packageDir !== undefined && isAbsolute11(packageDir)) {
41700
+ if (packageDir !== undefined && isAbsolute10(packageDir)) {
43766
41701
  throw new NaxError(`resolveTestFilePatterns: packageDir must be relative to workdir, got absolute path "${packageDir}"`, "INVALID_PACKAGE_DIR", { stage: "resolver", workdir, packageDir });
43767
41702
  }
43768
41703
  if (packageDir) {
@@ -44464,7 +42399,7 @@ var init_test_runners = __esm(() => {
44464
42399
  init_detector2();
44465
42400
  init_parse_bun();
44466
42401
  init_parse_rust();
44467
- init_parser2();
42402
+ init_parser();
44468
42403
  init_resolver();
44469
42404
  init_scoped_selection();
44470
42405
  });
@@ -44650,7 +42585,7 @@ var init_diff_utils = __esm(() => {
44650
42585
 
44651
42586
  // src/utils/path-security.ts
44652
42587
  import { realpathSync as realpathSync4 } from "fs";
44653
- import { dirname as dirname13, isAbsolute as isAbsolute12, join as join34, normalize as normalize3, resolve as resolve22 } from "path";
42588
+ import { dirname as dirname13, isAbsolute as isAbsolute11, join as join34, normalize as normalize3, resolve as resolve22 } from "path";
44654
42589
  function safeRealpathForComparison(p) {
44655
42590
  try {
44656
42591
  return realpathSync4(p);
@@ -44668,7 +42603,7 @@ function isRelativeAndSafe(filePath2) {
44668
42603
  if (!isSafeRelativePath(filePath2))
44669
42604
  return false;
44670
42605
  const normalized = normalize3(filePath2);
44671
- if (isAbsolute12(normalized))
42606
+ if (isAbsolute11(normalized))
44672
42607
  return false;
44673
42608
  return true;
44674
42609
  }
@@ -44677,7 +42612,7 @@ function validateModulePath(modulePath, allowedRoots) {
44677
42612
  return { valid: false, error: "Module path is empty" };
44678
42613
  }
44679
42614
  const resolvedRoots = allowedRoots.map((r) => safeRealpathForComparison(resolve22(r)));
44680
- if (isAbsolute12(modulePath)) {
42615
+ if (isAbsolute11(modulePath)) {
44681
42616
  const normalized = normalize3(modulePath);
44682
42617
  const resolved = safeRealpathForComparison(normalized);
44683
42618
  const isWithin3 = resolvedRoots.some((root) => resolved.startsWith(`${root}/`) || resolved === root);
@@ -46713,7 +44648,7 @@ var init_hermetic = __esm(() => {
46713
44648
  });
46714
44649
 
46715
44650
  // src/prompts/sections/protocol-region.ts
46716
- import { randomUUID as randomUUID9 } from "crypto";
44651
+ import { randomUUID as randomUUID8 } from "crypto";
46717
44652
  function call(tool, input) {
46718
44653
  return `${tool} ${JSON.stringify(input)}`;
46719
44654
  }
@@ -46830,7 +44765,7 @@ var KIND_PATTERN, NONCE, PROTOCOL_REGION_MARKER_PREFIX = "<!--nax:", REGION, OWN
46830
44765
  var init_protocol_region = __esm(() => {
46831
44766
  init_errors4();
46832
44767
  KIND_PATTERN = /^[a-z][a-z-]*$/;
46833
- NONCE = randomUUID9().slice(0, 8);
44768
+ NONCE = randomUUID8().slice(0, 8);
46834
44769
  REGION = new RegExp(`<!--nax:([a-z][a-z-]*):([a-f0-9]+) (\\{.*?\\})-->\\n` + `((?:(?!<!--nax:[a-z][a-z-]*:[a-f0-9]+ )[\\s\\S])*?)` + `<!--\\/nax:\\1-->\\n?`, "g");
46835
44770
  OWN_OPEN = new RegExp(`<!--nax:[a-z][a-z-]*:${NONCE} `, "g");
46836
44771
  REGISTRY = {
@@ -51790,6 +49725,14 @@ function lintForNeutrality(content, fileName) {
51790
49725
  function estimateTokens3(text) {
51791
49726
  return Math.ceil(text.length / 4);
51792
49727
  }
49728
+ function handleFrontmatterFailure(err, filePath2, onInvalidFrontmatter, issues, logger) {
49729
+ if (!(err instanceof RulesFrontmatterError))
49730
+ throw err;
49731
+ logger.warn("canonical-loader", onInvalidFrontmatter === "throw" ? "Invalid rule frontmatter \u2014 load will fail" : "Invalid rule frontmatter \u2014 skipping file", { file: filePath2, error: err.message });
49732
+ if (onInvalidFrontmatter === "throw") {
49733
+ issues.push({ file: filePath2, message: err.message });
49734
+ }
49735
+ }
51793
49736
  function applyCanonicalRulesBudget(rules, budgetTokens, options = {}) {
51794
49737
  const enforce = options.enforce === true;
51795
49738
  const totalTokens = rules.reduce((sum, r) => sum + (r.tokens ?? estimateTokens3(r.content)), 0);
@@ -51848,6 +49791,7 @@ async function loadCanonicalRules(workdir, options = {}) {
51848
49791
  }
51849
49792
  const rules = [];
51850
49793
  const allViolations = [];
49794
+ const frontmatterIssues = [];
51851
49795
  for (const filePath2 of filePaths) {
51852
49796
  const normalizedPath = filePath2.replaceAll("\\", "/");
51853
49797
  const normalizedRulesDir = rulesDir.replaceAll("\\", "/");
@@ -51868,14 +49812,8 @@ async function loadCanonicalRules(workdir, options = {}) {
51868
49812
  try {
51869
49813
  parsed = parseFrontmatter(content, filePath2);
51870
49814
  } catch (err) {
51871
- if (err instanceof RulesFrontmatterError) {
51872
- logger.warn("canonical-loader", "Invalid rule frontmatter \u2014 skipping file", {
51873
- file: filePath2,
51874
- error: err.message
51875
- });
51876
- continue;
51877
- }
51878
- throw err;
49815
+ handleFrontmatterFailure(err, filePath2, options.onInvalidFrontmatter, frontmatterIssues, logger);
49816
+ continue;
51879
49817
  }
51880
49818
  if (!parsed.content)
51881
49819
  continue;
@@ -51901,6 +49839,9 @@ async function loadCanonicalRules(workdir, options = {}) {
51901
49839
  ...parsed.warnings.length > 0 && { warnings: parsed.warnings }
51902
49840
  });
51903
49841
  }
49842
+ if (frontmatterIssues.length > 0) {
49843
+ throw new RulesFrontmatterLintError(frontmatterIssues);
49844
+ }
51904
49845
  if (allViolations.length > 0) {
51905
49846
  throw new NeutralityLintError(allViolations);
51906
49847
  }
@@ -51936,7 +49877,7 @@ async function loadCanonicalRules(workdir, options = {}) {
51936
49877
  }
51937
49878
  return budgetResult.rules;
51938
49879
  }
51939
- var CANONICAL_RULES_DIR = ".nax/rules", _canonicalLoaderDeps, NEUTRALITY_RULES, DEFAULT_CANONICAL_RULES_BUDGET_TOKENS = 8192, RULES_BUDGET_WARNING_RATIO = 0.75, RULE_ALLOW_MARKER, NeutralityLintError;
49880
+ var CANONICAL_RULES_DIR = ".nax/rules", _canonicalLoaderDeps, NEUTRALITY_RULES, DEFAULT_CANONICAL_RULES_BUDGET_TOKENS = 8192, RULES_BUDGET_WARNING_RATIO = 0.75, RULE_ALLOW_MARKER, NeutralityLintError, RulesFrontmatterLintError;
51940
49881
  var init_canonical_loader = __esm(() => {
51941
49882
  init_errors4();
51942
49883
  init_logger2();
@@ -52040,6 +49981,19 @@ ${summary}`, "NEUTRALITY_LINT_FAILED", {
52040
49981
  this.violations = violations;
52041
49982
  }
52042
49983
  };
49984
+ RulesFrontmatterLintError = class RulesFrontmatterLintError extends NaxError {
49985
+ issues;
49986
+ constructor(issues) {
49987
+ const summary = issues.map((i) => ` ${i.file}: ${i.message}`).join(`
49988
+ `);
49989
+ super(`Canonical rules frontmatter lint failed:
49990
+ ${summary}`, "RULES_FRONTMATTER_LINT_FAILED", {
49991
+ stage: "canonical-loader",
49992
+ issueCount: issues.length
49993
+ });
49994
+ this.issues = issues;
49995
+ }
49996
+ };
52043
49997
  });
52044
49998
 
52045
49999
  // src/context/engine/agent-profiles.ts
@@ -52433,7 +50387,7 @@ var init_digest = __esm(() => {
52433
50387
 
52434
50388
  // src/context/engine/manifest-store.ts
52435
50389
  import { mkdir as mkdir13 } from "fs/promises";
52436
- import { dirname as dirname14, isAbsolute as isAbsolute13, join as join37, relative as relative17, resolve as resolve24 } from "path";
50390
+ import { dirname as dirname14, isAbsolute as isAbsolute12, join as join37, relative as relative17, resolve as resolve24 } from "path";
52437
50391
  function contextStoryDir(projectDir, featureId, storyId) {
52438
50392
  return join37(featureDir(projectDir, featureId), "stories", storyId);
52439
50393
  }
@@ -52444,11 +50398,11 @@ function rebuildManifestPath(projectDir, featureId, storyId) {
52444
50398
  return join37(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
52445
50399
  }
52446
50400
  function toStoredPath(projectDir, pathValue) {
52447
- const relativePath = isAbsolute13(pathValue) ? relative17(projectDir, pathValue) : pathValue;
50401
+ const relativePath = isAbsolute12(pathValue) ? relative17(projectDir, pathValue) : pathValue;
52448
50402
  return relativePath === "" ? "." : relativePath;
52449
50403
  }
52450
50404
  function toAbsolutePath(projectDir, pathValue) {
52451
- return isAbsolute13(pathValue) ? pathValue : resolve24(projectDir, pathValue);
50405
+ return isAbsolute12(pathValue) ? pathValue : resolve24(projectDir, pathValue);
52452
50406
  }
52453
50407
  function toStoredManifest(projectDir, manifest) {
52454
50408
  return {
@@ -52993,10 +50947,10 @@ var init_static_rules_scoping = __esm(() => {
52993
50947
  });
52994
50948
 
52995
50949
  // src/context/engine/providers/static-rules.ts
52996
- import { createHash as createHash7 } from "crypto";
50950
+ import { createHash as createHash6 } from "crypto";
52997
50951
  import { join as join38 } from "path";
52998
50952
  function contentHash8(content) {
52999
- return createHash7("sha256").update(content).digest("hex").slice(0, 8);
50953
+ return createHash6("sha256").update(content).digest("hex").slice(0, 8);
53000
50954
  }
53001
50955
  function canonicalRuleId(rule) {
53002
50956
  return rule.id ?? rule.fileName.replace(/\.md$/i, "");
@@ -53733,9 +51687,9 @@ var init_loop_handler_forwarding = __esm(() => {
53733
51687
  });
53734
51688
 
53735
51689
  // src/session/manager-deps.ts
53736
- import { randomUUID as randomUUID10 } from "crypto";
51690
+ import { randomUUID as randomUUID9 } from "crypto";
53737
51691
  import { mkdir as mkdir14 } from "fs/promises";
53738
- import { isAbsolute as isAbsolute14, join as join39, relative as relative18, sep as sep13 } from "path";
51692
+ import { isAbsolute as isAbsolute13, join as join39, relative as relative18, sep as sep13 } from "path";
53739
51693
  function resolveProjectDirFromScratchDir(scratchDir) {
53740
51694
  const marker = `${sep13}.nax${sep13}features${sep13}`;
53741
51695
  const markerIdx = scratchDir.lastIndexOf(marker);
@@ -53747,7 +51701,7 @@ function resolveProjectDirFromScratchDir(scratchDir) {
53747
51701
  return;
53748
51702
  }
53749
51703
  function toProjectRelativePath(projectDir, pathValue) {
53750
- const relativePath = isAbsolute14(pathValue) ? relative18(projectDir, pathValue) : pathValue;
51704
+ const relativePath = isAbsolute13(pathValue) ? relative18(projectDir, pathValue) : pathValue;
53751
51705
  return relativePath === "" ? "." : relativePath;
53752
51706
  }
53753
51707
  function deriveNativeTranscriptDir(opts) {
@@ -53775,7 +51729,7 @@ var init_manager_deps = __esm(() => {
53775
51729
  _sessionManagerDeps = {
53776
51730
  now: () => new Date().toISOString(),
53777
51731
  nowMs: () => Date.now(),
53778
- uuid: () => randomUUID10(),
51732
+ uuid: () => randomUUID9(),
53779
51733
  sessionScratchDir: (projectDir, featureName, sessionId) => join39(featureDir(projectDir, featureName), "sessions", sessionId),
53780
51734
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
53781
51735
  await mkdir14(scratchDir, { recursive: true });
@@ -53838,9 +51792,9 @@ var init_model_selection = __esm(() => {
53838
51792
  });
53839
51793
 
53840
51794
  // src/runtime/session-name.ts
53841
- import { createHash as createHash8 } from "crypto";
51795
+ import { createHash as createHash7 } from "crypto";
53842
51796
  function formatSessionName(req) {
53843
- const hash2 = createHash8("sha256").update(req.workdir).digest("hex").slice(0, 8);
51797
+ const hash2 = createHash7("sha256").update(req.workdir).digest("hex").slice(0, 8);
53844
51798
  const sanitize = (s) => s.replace(/[^a-z0-9]+/gi, "-").toLowerCase().replace(/^-+|-+$/g, "");
53845
51799
  const parts = ["nax", hash2];
53846
51800
  if (req.featureName)
@@ -55042,9 +52996,9 @@ var init_staleness = __esm(() => {
55042
52996
  });
55043
52997
 
55044
52998
  // src/context/engine/providers/feature-context.ts
55045
- import { createHash as createHash9 } from "crypto";
52999
+ import { createHash as createHash8 } from "crypto";
55046
53000
  function contentHash82(content) {
55047
- return createHash9("sha256").update(content).digest("hex").slice(0, 8);
53001
+ return createHash8("sha256").update(content).digest("hex").slice(0, 8);
55048
53002
  }
55049
53003
  function estimateTokens4(text) {
55050
53004
  return Math.ceil(text.length / 4);
@@ -55449,9 +53403,9 @@ var init_code_neighbor_cache = __esm(() => {
55449
53403
  });
55450
53404
 
55451
53405
  // src/context/engine/providers/code-neighbor-chunk.ts
55452
- import { createHash as createHash10 } from "crypto";
53406
+ import { createHash as createHash9 } from "crypto";
55453
53407
  function contentHash83(content) {
55454
- return createHash10("sha256").update(content).digest("hex").slice(0, 8);
53408
+ return createHash9("sha256").update(content).digest("hex").slice(0, 8);
55455
53409
  }
55456
53410
  function assembleCodeNeighborChunk(input) {
55457
53411
  const { sections: sections2, truncated, maxGlobFiles } = input;
@@ -55529,7 +53483,7 @@ var MAX_CHUNK_TOKENS = 500;
55529
53483
  var init_code_neighbor_chunk = () => {};
55530
53484
 
55531
53485
  // src/context/engine/providers/code-neighbor-phases.ts
55532
- import { isAbsolute as isAbsolute15, join as join44, relative as relative19, resolve as resolve25 } from "path";
53486
+ import { isAbsolute as isAbsolute14, join as join44, relative as relative19, resolve as resolve25 } from "path";
55533
53487
  function parseImportSpecifiers(content) {
55534
53488
  const specifiers = new Set;
55535
53489
  for (const match of content.matchAll(FROM_PATTERN)) {
@@ -55559,7 +53513,7 @@ function resolveImport(specifier, fromFile, workdir) {
55559
53513
  function packageScopeRelative(execRoot, packageDir) {
55560
53514
  if (!packageDir)
55561
53515
  return "";
55562
- const rel = isAbsolute15(packageDir) ? relative19(execRoot, packageDir) : packageDir;
53516
+ const rel = isAbsolute14(packageDir) ? relative19(execRoot, packageDir) : packageDir;
55563
53517
  const normalized = rel.replace(/\\/g, "/").replace(/\/+$/, "");
55564
53518
  if (normalized === "" || normalized === "." || normalized.startsWith(".."))
55565
53519
  return "";
@@ -56584,7 +54538,7 @@ var init_render = __esm(() => {
56584
54538
  });
56585
54539
 
56586
54540
  // src/context/engine/rebuild.ts
56587
- import { randomUUID as randomUUID11 } from "crypto";
54541
+ import { randomUUID as randomUUID10 } from "crypto";
56588
54542
  function toPackedChunks(prior, newAgentId, targetAgentId) {
56589
54543
  const priorAgentForNeutralize = prior.agentId ?? "";
56590
54544
  return prior.chunks.map((c) => {
@@ -56711,7 +54665,7 @@ var init_rebuild = __esm(() => {
56711
54665
  init_render();
56712
54666
  init_scratch_neutralizer();
56713
54667
  DEFAULT_REBUILD_DEPS = {
56714
- uuid: () => randomUUID11(),
54668
+ uuid: () => randomUUID10(),
56715
54669
  getLogger: () => getLogger2()
56716
54670
  };
56717
54671
  });
@@ -56757,7 +54711,7 @@ var init_scoring = __esm(() => {
56757
54711
  });
56758
54712
 
56759
54713
  // src/context/engine/orchestrator.ts
56760
- import { createHash as createHash11, randomUUID as randomUUID12 } from "crypto";
54714
+ import { createHash as createHash10, randomUUID as randomUUID11 } from "crypto";
56761
54715
  function buildPullToolDescriptors(stageToolNames, pullConfig) {
56762
54716
  if (!pullConfig?.enabled || stageToolNames.length === 0)
56763
54717
  return [];
@@ -56924,7 +54878,7 @@ class ContextOrchestrator {
56924
54878
  const providerResults = fetchResults.map(({ providerStatus }) => providerStatus);
56925
54879
  if (request.priorStageDigest && (request.planDigestBoost ?? 1) > 1) {
56926
54880
  const boost = request.planDigestBoost ?? 1;
56927
- const hash2 = createHash11("sha256").update(request.priorStageDigest).digest("hex").slice(0, 8);
54881
+ const hash2 = createHash10("sha256").update(request.priorStageDigest).digest("hex").slice(0, 8);
56928
54882
  const tokens = Math.ceil(request.priorStageDigest.length / 4);
56929
54883
  allRaw.push({
56930
54884
  id: `plan-digest:${hash2}`,
@@ -57060,7 +55014,7 @@ var init_orchestrator = __esm(() => {
57060
55014
  init_stage_config();
57061
55015
  _orchestratorDeps = {
57062
55016
  now: () => Date.now(),
57063
- uuid: () => randomUUID12(),
55017
+ uuid: () => randomUUID11(),
57064
55018
  getLogger: getLogger2,
57065
55019
  rebuild
57066
55020
  };
@@ -57068,10 +55022,10 @@ var init_orchestrator = __esm(() => {
57068
55022
  });
57069
55023
 
57070
55024
  // src/context/engine/providers/git-history.ts
57071
- import { createHash as createHash12 } from "crypto";
55025
+ import { createHash as createHash11 } from "crypto";
57072
55026
  import { join as join46 } from "path";
57073
55027
  function contentHash84(content) {
57074
- return createHash12("sha256").update(content).digest("hex").slice(0, 8);
55028
+ return createHash11("sha256").update(content).digest("hex").slice(0, 8);
57075
55029
  }
57076
55030
  async function hasHistoryAt(path5, repoRoot) {
57077
55031
  const { stdout, exitCode } = await _gitHistoryDeps.gitWithTimeout(["log", "--oneline", "-1", "--", path5], repoRoot);
@@ -57215,10 +55169,10 @@ var init_git_history = __esm(() => {
57215
55169
  });
57216
55170
 
57217
55171
  // src/context/engine/providers/lint-config.ts
57218
- import { createHash as createHash13 } from "crypto";
55172
+ import { createHash as createHash12 } from "crypto";
57219
55173
  import { join as join47 } from "path";
57220
55174
  function contentHash85(content) {
57221
- return createHash13("sha256").update(content).digest("hex").slice(0, 8);
55175
+ return createHash12("sha256").update(content).digest("hex").slice(0, 8);
57222
55176
  }
57223
55177
  function distillLintConfig(tool, raw) {
57224
55178
  if (tool !== "biome")
@@ -57956,9 +55910,9 @@ var init_metrics = __esm(() => {
57956
55910
  });
57957
55911
 
57958
55912
  // src/context/engine/providers/prior-run-failure.ts
57959
- import { createHash as createHash14 } from "crypto";
55913
+ import { createHash as createHash13 } from "crypto";
57960
55914
  function contentHash86(content) {
57961
- return createHash14("sha256").update(content).digest("hex").slice(0, 8);
55915
+ return createHash13("sha256").update(content).digest("hex").slice(0, 8);
57962
55916
  }
57963
55917
  function aggregatePriorFailures(runs, storyId) {
57964
55918
  let totalAttempts = 0;
@@ -58068,9 +56022,9 @@ async function readJsonlTail(path6, maxBytes = DEFAULT_JSONL_TAIL_BYTES) {
58068
56022
  var DEFAULT_JSONL_TAIL_BYTES = 65536;
58069
56023
 
58070
56024
  // src/context/engine/providers/session-scratch.ts
58071
- import { createHash as createHash15 } from "crypto";
56025
+ import { createHash as createHash14 } from "crypto";
58072
56026
  function contentHash87(content) {
58073
- return createHash15("sha256").update(content).digest("hex").slice(0, 8);
56027
+ return createHash14("sha256").update(content).digest("hex").slice(0, 8);
58074
56028
  }
58075
56029
  function parseJsonl(raw) {
58076
56030
  const entries = [];
@@ -58177,9 +56131,9 @@ var init_session_scratch = __esm(() => {
58177
56131
  });
58178
56132
 
58179
56133
  // src/context/engine/providers/test-coverage.ts
58180
- import { createHash as createHash16 } from "crypto";
56134
+ import { createHash as createHash15 } from "crypto";
58181
56135
  function contentHash88(content) {
58182
- return createHash16("sha256").update(content).digest("hex").slice(0, 8);
56136
+ return createHash15("sha256").update(content).digest("hex").slice(0, 8);
58183
56137
  }
58184
56138
 
58185
56139
  class TestCoverageProvider {
@@ -58259,9 +56213,9 @@ var init_test_coverage = __esm(() => {
58259
56213
  });
58260
56214
 
58261
56215
  // src/context/engine/providers/tool-diagnostics.ts
58262
- import { createHash as createHash17 } from "crypto";
56216
+ import { createHash as createHash16 } from "crypto";
58263
56217
  function contentHash89(content) {
58264
- return createHash17("sha256").update(content).digest("hex").slice(0, 8);
56218
+ return createHash16("sha256").update(content).digest("hex").slice(0, 8);
58265
56219
  }
58266
56220
  function parseToolDiagnosticsJsonl(raw) {
58267
56221
  const entries = [];
@@ -58643,9 +56597,9 @@ var init_registry4 = __esm(() => {
58643
56597
  });
58644
56598
 
58645
56599
  // src/trust/store.ts
58646
- import { randomUUID as randomUUID13 } from "crypto";
56600
+ import { randomUUID as randomUUID12 } from "crypto";
58647
56601
  import { mkdir as mkdir18, rename as rename5, stat as stat8, unlink as unlink4, writeFile as writeFile10 } from "fs/promises";
58648
- import { dirname as dirname20, isAbsolute as isAbsolute16, join as join48 } from "path";
56602
+ import { dirname as dirname20, isAbsolute as isAbsolute15, join as join48 } from "path";
58649
56603
  function trustStorePath() {
58650
56604
  return join48(globalConfigDir(), "trust.json");
58651
56605
  }
@@ -58716,7 +56670,7 @@ async function readFoldersForWrite(storePath) {
58716
56670
  return read.state === "ok" ? read.file.folders : [];
58717
56671
  }
58718
56672
  async function writeTrustStore(storePath, file2) {
58719
- const tempPath = `${storePath}.${process.pid}.${randomUUID13()}.tmp`;
56673
+ const tempPath = `${storePath}.${process.pid}.${randomUUID12()}.tmp`;
58720
56674
  try {
58721
56675
  await writeFile10(tempPath, `${JSON.stringify(file2, null, 2)}
58722
56676
  `, { mode: 384 });
@@ -58745,7 +56699,7 @@ var init_store2 = __esm(() => {
58745
56699
  TrustStoreFileSchema = exports_external.object({
58746
56700
  version: exports_external.literal(1),
58747
56701
  folders: exports_external.array(exports_external.object({
58748
- path: exports_external.string().refine((path6) => isAbsolute16(path6), { message: "path must be absolute" }),
56702
+ path: exports_external.string().refine((path6) => isAbsolute15(path6), { message: "path must be absolute" }),
58749
56703
  addedAt: exports_external.string(),
58750
56704
  via: exports_external.enum(["prompt", "cli"])
58751
56705
  }))
@@ -58811,7 +56765,7 @@ var init_trust = __esm(() => {
58811
56765
  });
58812
56766
 
58813
56767
  // src/context/engine/providers/plugin-loader.ts
58814
- import { isAbsolute as isAbsolute17, join as join49, resolve as resolve28 } from "path";
56768
+ import { isAbsolute as isAbsolute16, join as join49, resolve as resolve28 } from "path";
58815
56769
  function isInitialisable(p) {
58816
56770
  return typeof p.init === "function";
58817
56771
  }
@@ -58834,7 +56788,7 @@ function extractProvider(mod) {
58834
56788
  return null;
58835
56789
  }
58836
56790
  function resolveModuleSpecifier(specifier, workdir) {
58837
- if (isAbsolute17(specifier)) {
56791
+ if (isAbsolute16(specifier)) {
58838
56792
  throw new NaxError(`Plugin module path must be a bare package name or a project-relative path (./... or ../...): got absolute "${specifier}"`, "PLUGIN_MODULE_ABSOLUTE_PATH", { stage: "context", workdir, specifier });
58839
56793
  }
58840
56794
  if (specifier.startsWith("./") || specifier.startsWith("../")) {
@@ -59016,8 +56970,8 @@ var init_version = __esm(() => {
59016
56970
  NAX_AI_VERSION = CATALOG_VERSION;
59017
56971
  NAX_COMMIT = (() => {
59018
56972
  try {
59019
- if (/^[0-9a-f]{6,10}$/.test("d79057cfa"))
59020
- return "d79057cfa";
56973
+ if (/^[0-9a-f]{6,10}$/.test("eab634ebe"))
56974
+ return "eab634ebe";
59021
56975
  } catch {}
59022
56976
  try {
59023
56977
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -60028,18 +57982,18 @@ var init_in_flight_usage = __esm(() => {
60028
57982
  });
60029
57983
 
60030
57984
  // src/runtime/packages.ts
60031
- import { isAbsolute as isAbsolute18, join as join53, relative as relative21 } from "path";
57985
+ import { isAbsolute as isAbsolute17, join as join53, relative as relative21 } from "path";
60032
57986
  function packageWorkdir(view) {
60033
57987
  const { packageDir, repoRoot } = view;
60034
57988
  if (!packageDir)
60035
57989
  return repoRoot;
60036
- if (!repoRoot || isAbsolute18(packageDir))
57990
+ if (!repoRoot || isAbsolute17(packageDir))
60037
57991
  return packageDir;
60038
57992
  return join53(repoRoot, packageDir);
60039
57993
  }
60040
57994
  function createPackageView(config2, packageDir, repoRoot, hasOverride, overlay) {
60041
57995
  const memo2 = new Map;
60042
- const relativeFromRoot = packageDir ? isAbsolute18(packageDir) && isAbsolute18(repoRoot) ? stripLeadingSlash(relative21(repoRoot, packageDir)) : packageDir : "";
57996
+ const relativeFromRoot = packageDir ? isAbsolute17(packageDir) && isAbsolute17(repoRoot) ? stripLeadingSlash(relative21(repoRoot, packageDir)) : packageDir : "";
60043
57997
  return {
60044
57998
  packageDir,
60045
57999
  relativeFromRoot,
@@ -60075,7 +58029,7 @@ function createPackageRegistry(loader, repoRoot) {
60075
58029
  function toRelativeKey(packageDir) {
60076
58030
  if (!packageDir)
60077
58031
  return "";
60078
- if (isAbsolute18(packageDir) && isAbsolute18(repoRoot)) {
58032
+ if (isAbsolute17(packageDir) && isAbsolute17(repoRoot)) {
60079
58033
  if (packageDir === repoRoot)
60080
58034
  return "";
60081
58035
  return stripLeadingSlash(relative21(repoRoot, packageDir));
@@ -60143,7 +58097,7 @@ function storyExecRoot(view) {
60143
58097
  const { repoRoot, packageDir } = view;
60144
58098
  if (!packageDir)
60145
58099
  return repoRoot;
60146
- if (!repoRoot || isAbsolute18(packageDir))
58100
+ if (!repoRoot || isAbsolute17(packageDir))
60147
58101
  return packageDir;
60148
58102
  const segments = packageDir.split("/");
60149
58103
  if (segments[0] !== ".nax-wt" || segments.length < 2)
@@ -70504,7 +68458,7 @@ var require_which = __commonJS(function(exports, module) {
70504
68458
  pathExtExe
70505
68459
  };
70506
68460
  };
70507
- var which3 = (cmd, opt, cb) => {
68461
+ var which2 = (cmd, opt, cb) => {
70508
68462
  if (typeof opt === "function") {
70509
68463
  cb = opt;
70510
68464
  opt = {};
@@ -70566,8 +68520,8 @@ var require_which = __commonJS(function(exports, module) {
70566
68520
  return null;
70567
68521
  throw getNotFoundError(cmd);
70568
68522
  };
70569
- module.exports = which3;
70570
- which3.sync = whichSync;
68523
+ module.exports = which2;
68524
+ which2.sync = whichSync;
70571
68525
  });
70572
68526
 
70573
68527
  // ../../node_modules/.bun/path-key@3.1.1/node_modules/path-key/index.js
@@ -70587,7 +68541,7 @@ var require_path_key = __commonJS(function(exports, module) {
70587
68541
  // ../../node_modules/.bun/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js
70588
68542
  var require_resolveCommand = __commonJS(function(exports, module) {
70589
68543
  var path7 = __require("path");
70590
- var which3 = require_which();
68544
+ var which2 = require_which();
70591
68545
  var getPathKey = require_path_key();
70592
68546
  function resolveCommandAttempt(parsed, withoutPathExt) {
70593
68547
  const env2 = parsed.options.env || process.env;
@@ -70601,7 +68555,7 @@ var require_resolveCommand = __commonJS(function(exports, module) {
70601
68555
  }
70602
68556
  let resolved;
70603
68557
  try {
70604
- resolved = which3.sync(parsed.command, {
68558
+ resolved = which2.sync(parsed.command, {
70605
68559
  path: env2[getPathKey({ env: env2 })],
70606
68560
  pathExt: withoutPathExt ? path7.delimiter : undefined
70607
68561
  });
@@ -71106,7 +69060,7 @@ var init_client4 = __esm(() => {
71106
69060
  });
71107
69061
 
71108
69062
  // src/mcp/lock.ts
71109
- import { createHash as createHash18 } from "crypto";
69063
+ import { createHash as createHash17 } from "crypto";
71110
69064
  import { mkdir as mkdir22, readFile as readFile9, writeFile as writeFile11 } from "fs/promises";
71111
69065
  import { join as join55 } from "path";
71112
69066
  function mcpLockPath(projectRoot) {
@@ -71121,7 +69075,7 @@ function canonical3(value) {
71121
69075
  return Object.fromEntries(entries.map(([k, v]) => [k, canonical3(v)]));
71122
69076
  }
71123
69077
  function schemaHash(schema) {
71124
- return createHash18("sha256").update(JSON.stringify(canonical3(schema) ?? null)).digest("hex").slice(0, 16);
69078
+ return createHash17("sha256").update(JSON.stringify(canonical3(schema) ?? null)).digest("hex").slice(0, 16);
71125
69079
  }
71126
69080
  async function readMcpLock(projectRoot) {
71127
69081
  try {
@@ -71642,7 +69596,7 @@ var init_flake_probe = __esm(() => {
71642
69596
  init_internal();
71643
69597
  init_errors4();
71644
69598
  init_logger2();
71645
- init_parser2();
69599
+ init_parser();
71646
69600
  init_executor();
71647
69601
  NO_TESTS_EXECUTED_MARKERS = [/no tests? to run/i, /^ran 0 tests?/im];
71648
69602
  _flakeProbeDeps = {
@@ -72981,10 +70935,10 @@ async function resolvePackageName(root) {
72981
70935
  var init_exec_package_name = () => {};
72982
70936
 
72983
70937
  // src/agents/nax-owned-writes.ts
72984
- import { isAbsolute as isAbsolute19, relative as relative22, resolve as resolve31, sep as sep15 } from "path";
70938
+ import { isAbsolute as isAbsolute18, relative as relative22, resolve as resolve31, sep as sep15 } from "path";
72985
70939
  function naxWriteOptIns(root, allowWrite) {
72986
70940
  const naxDir = resolve31(root, ".nax");
72987
- const names = allowWrite.map((p) => relative22(naxDir, isAbsolute19(p) ? resolve31(p) : resolve31(root, p))).filter((rel) => rel !== "" && !rel.startsWith("..") && !rel.includes(sep15) && !isAbsolute19(rel)).filter((name) => name !== NAX_SCRATCHPAD_ENTRY && !NAX_NEVER_OPT_IN.has(name));
70941
+ const names = allowWrite.map((p) => relative22(naxDir, isAbsolute18(p) ? resolve31(p) : resolve31(root, p))).filter((rel) => rel !== "" && !rel.startsWith("..") && !rel.includes(sep15) && !isAbsolute18(rel)).filter((name) => name !== NAX_SCRATCHPAD_ENTRY && !NAX_NEVER_OPT_IN.has(name));
72988
70942
  return new Set(names);
72989
70943
  }
72990
70944
  function isNaxConfigSegments(segments) {
@@ -73562,12 +71516,12 @@ var init_tool_preamble = __esm(() => {
73562
71516
  });
73563
71517
 
73564
71518
  // src/session/instruction-options.ts
73565
- import { isAbsolute as isAbsolute20, relative as relative23 } from "path";
71519
+ import { isAbsolute as isAbsolute19, relative as relative23 } from "path";
73566
71520
  function instructionOptionsForSession(agentName, options) {
73567
71521
  if (agentName !== "native")
73568
71522
  return {};
73569
71523
  const packageDir = options.codingToolPackageDir;
73570
- const packageScope = packageDir ? isAbsolute20(packageDir) ? relative23(options.workdir, packageDir) : packageOverrideKey(packageDir) : ".";
71524
+ const packageScope = packageDir ? isAbsolute19(packageDir) ? relative23(options.workdir, packageDir) : packageOverrideKey(packageDir) : ".";
73571
71525
  return {
73572
71526
  instructionFileName: options.config.agent?.native?.instructionFileName ?? DEFAULT_INSTRUCTION_FILE_NAME,
73573
71527
  instructionDirectories: options.instructionDirectories ?? [options.codingToolWorkdirLabel ?? (packageScope || ".")],
@@ -73774,7 +71728,7 @@ __export(exports_runtime, {
73774
71728
  totalSpendUsd: () => totalSpendUsd,
73775
71729
  writeProjectIdentity: () => writeProjectIdentity
73776
71730
  });
73777
- import { basename as basename9, isAbsolute as isAbsolute21, join as join60, resolve as resolve32 } from "path";
71731
+ import { basename as basename9, isAbsolute as isAbsolute20, join as join60, resolve as resolve32 } from "path";
73778
71732
  function createRuntime(config2, workdir, opts) {
73779
71733
  const runId = crypto.randomUUID();
73780
71734
  const controller = new AbortController;
@@ -73805,7 +71759,7 @@ function createRuntime(config2, workdir, opts) {
73805
71759
  const reviewAuditor = opts?.reviewAuditor ?? (config2.review?.audit?.enabled ? new ReviewAuditor(runId, outputDir) : createNoOpReviewAuditor());
73806
71760
  const usageEnabled = config2.agent?.usageAudit?.enabled ?? false;
73807
71761
  const configuredUsageDir = config2.agent?.usageAudit?.dir;
73808
- const usageDir = configuredUsageDir === undefined ? join60(outputDir, "usage") : isAbsolute21(configuredUsageDir) ? configuredUsageDir : resolve32(workdir, configuredUsageDir);
71762
+ const usageDir = configuredUsageDir === undefined ? join60(outputDir, "usage") : isAbsolute20(configuredUsageDir) ? configuredUsageDir : resolve32(workdir, configuredUsageDir);
73809
71763
  let usageAuditor;
73810
71764
  if (opts?.usageAuditor) {
73811
71765
  usageAuditor = opts.usageAuditor;
@@ -74001,12 +71955,12 @@ var init_runtime3 = __esm(() => {
74001
71955
 
74002
71956
  // src/context/engine/stage-assembler.ts
74003
71957
  import { readdir as readdir4 } from "fs/promises";
74004
- import { isAbsolute as isAbsolute22, join as join61, resolve as resolve33 } from "path";
71958
+ import { isAbsolute as isAbsolute21, join as join61, resolve as resolve33 } from "path";
74005
71959
  function dedupeScratchDirs(dirs) {
74006
71960
  return [...new Set(dirs.filter((dir) => Boolean(dir)))];
74007
71961
  }
74008
71962
  function toAbsolutePath2(projectDir, pathValue) {
74009
- return isAbsolute22(pathValue) ? pathValue : resolve33(projectDir, pathValue);
71963
+ return isAbsolute21(pathValue) ? pathValue : resolve33(projectDir, pathValue);
74010
71964
  }
74011
71965
  async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId, ttlMs) {
74012
71966
  const logger = getLogger2();
@@ -80131,6 +78085,54 @@ var init_fix_review2 = __esm(() => {
80131
78085
  };
80132
78086
  });
80133
78087
 
78088
+ // src/agents/shared/env.ts
78089
+ import { homedir as homedir5 } from "os";
78090
+ import { isAbsolute as isAbsolute22 } from "path";
78091
+ function buildAllowedEnv(options) {
78092
+ const allowed = {};
78093
+ for (const varName of ESSENTIAL_VARS) {
78094
+ if (process.env[varName])
78095
+ allowed[varName] = process.env[varName];
78096
+ }
78097
+ const rawHome = process.env.HOME ?? "";
78098
+ const safeHome = rawHome && isAbsolute22(rawHome) ? rawHome : homedir5();
78099
+ if (rawHome !== safeHome) {
78100
+ getSafeLogger2()?.warn("env", `HOME env is not absolute ("${rawHome}"), falling back to os.homedir(): ${safeHome}`);
78101
+ }
78102
+ allowed.HOME = safeHome;
78103
+ for (const varName of API_KEY_VARS) {
78104
+ if (process.env[varName])
78105
+ allowed[varName] = process.env[varName];
78106
+ }
78107
+ for (const [key, value] of Object.entries(process.env)) {
78108
+ if (ALLOWED_PREFIXES.some((prefix) => key.startsWith(prefix))) {
78109
+ allowed[key] = value;
78110
+ }
78111
+ }
78112
+ if (options?.modelEnv)
78113
+ Object.assign(allowed, options.modelEnv);
78114
+ if (options?.env)
78115
+ Object.assign(allowed, options.env);
78116
+ return allowed;
78117
+ }
78118
+ var ESSENTIAL_VARS, API_KEY_VARS, ALLOWED_PREFIXES;
78119
+ var init_env = __esm(() => {
78120
+ init_logger2();
78121
+ ESSENTIAL_VARS = ["PATH", "TMPDIR", "NODE_ENV", "USER", "LOGNAME"];
78122
+ API_KEY_VARS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY", "CLAUDE_API_KEY"];
78123
+ ALLOWED_PREFIXES = [
78124
+ "CLAUDE_",
78125
+ "NAX_",
78126
+ "CLAW_",
78127
+ "TURBO_",
78128
+ "CODEX_",
78129
+ "GEMINI_",
78130
+ "ANTHROPIC_",
78131
+ "OPENCODE_",
78132
+ "MINIMAX_"
78133
+ ];
78134
+ });
78135
+
80134
78136
  // src/utils/command-argv.ts
80135
78137
  function parseCommandToArgv(command) {
80136
78138
  const safeEnv = buildAllowedEnv();
@@ -84759,7 +82761,93 @@ var init_manager_run_fallback = __esm(() => {
84759
82761
  init_swap_decision();
84760
82762
  });
84761
82763
 
84762
- // src/agents/acp-sdk/pricing.ts
82764
+ // src/agents/turn/abort.ts
82765
+ function createAbortError(signal, fallback = "Run aborted") {
82766
+ const reason = signal?.reason;
82767
+ if (reason instanceof Error) {
82768
+ return reason;
82769
+ }
82770
+ if (typeof reason === "string" && reason.length > 0) {
82771
+ return new Error(reason);
82772
+ }
82773
+ return new Error(fallback);
82774
+ }
82775
+ function throwIfAborted(signal, fallback) {
82776
+ if (signal?.aborted) {
82777
+ throw createAbortError(signal, fallback);
82778
+ }
82779
+ }
82780
+ async function raceWithAbort(promise3, signal, fallback) {
82781
+ if (!signal) {
82782
+ return promise3;
82783
+ }
82784
+ if (signal.aborted) {
82785
+ throw createAbortError(signal, fallback);
82786
+ }
82787
+ return await new Promise((resolve35, reject) => {
82788
+ const onAbort = () => reject(createAbortError(signal, fallback));
82789
+ signal.addEventListener("abort", onAbort, { once: true });
82790
+ promise3.then((value) => {
82791
+ signal.removeEventListener("abort", onAbort);
82792
+ resolve35(value);
82793
+ }, (error48) => {
82794
+ signal.removeEventListener("abort", onAbort);
82795
+ reject(error48);
82796
+ });
82797
+ });
82798
+ }
82799
+
82800
+ // src/agents/turn/turn-deadline.ts
82801
+ function assembleTurnResult(input) {
82802
+ const { totalTokenUsage, totalExactCostUsd, turnCount, interactions, timedOut, rateCard } = input;
82803
+ const output = timedOut ? "" : input.output;
82804
+ const hasUsage = totalTokenUsage.inputTokens > 0 || totalTokenUsage.outputTokens > 0;
82805
+ const priced = hasUsage ? priceCall(totalTokenUsage, rateCard.rates) : undefined;
82806
+ return {
82807
+ output,
82808
+ tokenUsage: totalTokenUsage,
82809
+ estimatedCostUsd: priced?.costUsd ?? 0,
82810
+ exactCostUsd: totalExactCostUsd,
82811
+ internalRoundTrips: turnCount,
82812
+ ...interactions.length > 0 ? { interactions } : {},
82813
+ timedOut,
82814
+ pricingSource: rateCard.source,
82815
+ ...priced?.resolvedRates !== undefined ? { rates: priced.resolvedRates } : {}
82816
+ };
82817
+ }
82818
+ function warnWallClockTimeout(sessionName, timeoutSeconds, stage) {
82819
+ getSafeLogger2()?.warn(stage, "wall-clock timeout exceeded \u2014 session terminated", {
82820
+ sessionName,
82821
+ timeoutSeconds
82822
+ });
82823
+ }
82824
+ var init_turn_deadline2 = __esm(() => {
82825
+ init_logger2();
82826
+ init_cost();
82827
+ });
82828
+
82829
+ // src/agents/turn/index.ts
82830
+ var init_turn = __esm(() => {
82831
+ init_turn_deadline2();
82832
+ });
82833
+
82834
+ // src/agents/session-naming.ts
82835
+ import { createHash as createHash18 } from "crypto";
82836
+ function computeAcpHandle(workdir, featureName, storyId, sessionRole) {
82837
+ const hash2 = createHash18("sha256").update(workdir).digest("hex").slice(0, 8);
82838
+ const sanitize = (s) => s.replace(/[^a-z0-9]+/gi, "-").toLowerCase().replace(/^-+|-+$/g, "");
82839
+ const parts = ["nax", hash2];
82840
+ if (featureName)
82841
+ parts.push(sanitize(featureName));
82842
+ if (storyId)
82843
+ parts.push(sanitize(storyId));
82844
+ if (sessionRole)
82845
+ parts.push(sanitize(sessionRole));
82846
+ return parts.join("-");
82847
+ }
82848
+ var init_session_naming = () => {};
82849
+
82850
+ // src/agents/acp/pricing.ts
84763
82851
  function spendOfResult(result) {
84764
82852
  return {
84765
82853
  tokenUsage: result.tokenUsage,
@@ -84795,7 +82883,7 @@ var init_pricing = __esm(() => {
84795
82883
  });
84796
82884
  });
84797
82885
 
84798
- // src/agents/acp-sdk/failure-map.ts
82886
+ // src/agents/acp/failure-map.ts
84799
82887
  function capped(text) {
84800
82888
  return text.slice(0, MAX_MESSAGE_CHARS);
84801
82889
  }
@@ -88959,7 +87047,7 @@ function client3(options) {
88959
87047
  return new ClientApp(options);
88960
87048
  }
88961
87049
  var methods, startActiveSession, AgentContext, ClientContext, AgentConnectionHandle, ClientConnectionHandle, ActiveSession, agentRequestSpecs, agentNotificationSpecs, clientRequestSpecs, clientNotificationSpecs, agentRequestSpecsByMethod, agentNotificationSpecsByMethod, clientRequestSpecsByMethod, clientNotificationSpecsByMethod, sessionUpdateRouters, appBuilder, runAgentConnectHandlers, runClientConnectHandlers, stableConnectionOptions, AgentApp, ClientApp, legacyAgentRequestMethods, legacyAgentNotificationMethods, legacyClientRequestMethods, legacyClientNotificationMethods, TerminalHandle;
88962
- var init_acp2 = __esm(() => {
87050
+ var init_acp = __esm(() => {
88963
87051
  init_schema3();
88964
87052
  init_zod_gen();
88965
87053
  init_stream();
@@ -89593,7 +87681,7 @@ function stopReasonError(stopReason) {
89593
87681
  }
89594
87682
  var EXCERPT_BYTES = 4096, AUTH_REQUIRED_CODE = -32000, RESOURCE_NOT_FOUND_CODE = -32002, RESTORE_STEPS, SESSION_NOT_FOUND_TEXT2, ACP_STOP_CODES;
89595
87683
  var init_errors6 = __esm(() => {
89596
- init_acp2();
87684
+ init_acp();
89597
87685
  init_src();
89598
87686
  init_text();
89599
87687
  RESTORE_STEPS = new Set(["session/resume", "session/load"]);
@@ -90420,7 +88508,7 @@ async function agentGoneError(during, launched, secrets) {
90420
88508
  }
90421
88509
  var GONE_WAIT_MS = 1000, STDERR_DRAIN_MS = 200;
90422
88510
  var init_launch = __esm(() => {
90423
- init_acp2();
88511
+ init_acp();
90424
88512
  init_src();
90425
88513
  init_errors6();
90426
88514
  });
@@ -90522,7 +88610,7 @@ function openConnection(target, handlers) {
90522
88610
  };
90523
88611
  }
90524
88612
  var init_connection = __esm(() => {
90525
- init_acp2();
88613
+ init_acp();
90526
88614
  });
90527
88615
 
90528
88616
  // ../nax-agent-acp/src/client/pre-approval.ts
@@ -90794,7 +88882,7 @@ function initialDoc(o, record4, agentSessionId) {
90794
88882
  }
90795
88883
  var MAX_OFFERED_MODELS = 50, MAX_MODEL_ID_CHARS = 100;
90796
88884
  var init_open = __esm(() => {
90797
- init_acp2();
88885
+ init_acp();
90798
88886
  init_src();
90799
88887
  init_capabilities();
90800
88888
  init_connection();
@@ -94120,7 +92208,97 @@ var init_client5 = __esm(() => {
94120
92208
  init_launchable();
94121
92209
  });
94122
92210
 
94123
- // src/agents/acp-sdk/ask-port.ts
92211
+ // src/agents/interaction/output-parsing.ts
92212
+ function extractQuestion(output) {
92213
+ const text = output.trim();
92214
+ if (!text)
92215
+ return null;
92216
+ const lines2 = text.split(`
92217
+ `).filter((l) => l.trim().length > 0);
92218
+ const lastLine = lines2.at(-1)?.trim() ?? "";
92219
+ const lower = lastLine.toLowerCase();
92220
+ const markers = [
92221
+ "please confirm",
92222
+ "please specify",
92223
+ "please provide",
92224
+ "which would you",
92225
+ "should i ",
92226
+ "do you want",
92227
+ "can you clarify"
92228
+ ];
92229
+ const isQuestion = lastLine.endsWith("?") && lastLine.length > 10 || markers.some((m) => lower.includes(m));
92230
+ if (!isQuestion)
92231
+ return null;
92232
+ const paragraphs = text.split(/\n\n+/);
92233
+ const questionPara = paragraphs.at(-1)?.trim() ?? lastLine;
92234
+ const contextPara = paragraphs.at(-2)?.trim();
92235
+ return contextPara ? `${contextPara}
92236
+
92237
+ ${questionPara}` : questionPara;
92238
+ }
92239
+ function extractContextToolCall(output) {
92240
+ const match = output.match(CONTEXT_TOOL_CALL_PATTERN);
92241
+ if (!match)
92242
+ return null;
92243
+ const [, name, rawInput] = match;
92244
+ const trimmedInput = rawInput.trim();
92245
+ if (!trimmedInput) {
92246
+ return { name, input: {} };
92247
+ }
92248
+ try {
92249
+ return { name, input: JSON.parse(trimmedInput) };
92250
+ } catch (error48) {
92251
+ return {
92252
+ name,
92253
+ error: `Invalid JSON tool input: ${error48 instanceof Error ? error48.message : String(error48)}`
92254
+ };
92255
+ }
92256
+ }
92257
+ var CONTEXT_TOOL_CALL_PATTERN;
92258
+ var init_output_parsing = __esm(() => {
92259
+ CONTEXT_TOOL_CALL_PATTERN = /<nax_tool_call\s+name="([^"]+)">\s*([\s\S]*?)\s*<\/nax_tool_call>/i;
92260
+ });
92261
+
92262
+ // src/agents/interaction/turn-interactions.ts
92263
+ async function awaitInteractionReply(ctx, interaction, warnSuffix) {
92264
+ let timeoutId;
92265
+ try {
92266
+ const response = await Promise.race([
92267
+ raceWithAbort(ctx.interactionHandler.onInteraction(interaction), ctx.signal, INTERACTION_ABORT_MESSAGE),
92268
+ new Promise((resolve36) => {
92269
+ timeoutId = setTimeout(() => resolve36(null), ctx.timeoutMs ?? INTERACTION_TIMEOUT_MS);
92270
+ })
92271
+ ]);
92272
+ if (response) {
92273
+ return { kind: "answered", answer: response.answer };
92274
+ }
92275
+ } catch (err) {
92276
+ if (ctx.signal?.aborted) {
92277
+ return { kind: "aborted" };
92278
+ }
92279
+ getSafeLogger2()?.warn(ctx.stage, `InteractionHandler.onInteraction failed${warnSuffix}${err instanceof Error ? err.message : String(err)}`);
92280
+ } finally {
92281
+ clearTimeout(timeoutId);
92282
+ }
92283
+ return { kind: "no-reply" };
92284
+ }
92285
+ function toContextToolInteraction(toolCall) {
92286
+ return toolCall.error ? { kind: "context-tool", name: toolCall.name, error: toolCall.error } : { kind: "context-tool", name: toolCall.name, input: toolCall.input };
92287
+ }
92288
+ var INTERACTION_TIMEOUT_MS, INTERACTION_ABORT_MESSAGE = "Run aborted \u2014 shutdown in progress";
92289
+ var init_turn_interactions = __esm(() => {
92290
+ init_logger2();
92291
+ init_turn();
92292
+ INTERACTION_TIMEOUT_MS = 5 * 60 * 1000;
92293
+ });
92294
+
92295
+ // src/agents/interaction/index.ts
92296
+ var init_interaction = __esm(() => {
92297
+ init_output_parsing();
92298
+ init_turn_interactions();
92299
+ });
92300
+
92301
+ // src/agents/acp/ask-port.ts
94124
92302
  function beatWhileWaiting(turn, beatMs) {
94125
92303
  let timer;
94126
92304
  const beat = () => {
@@ -94169,13 +92347,13 @@ function createAskPort(slot, beatMs = AWAITING_HUMAN_BEAT_MS, audit) {
94169
92347
  }
94170
92348
  };
94171
92349
  }
94172
- var STAGE = "acp-sdk", AWAITING_HUMAN_BEAT_MS = 30000;
92350
+ var STAGE = "acp", AWAITING_HUMAN_BEAT_MS = 30000;
94173
92351
  var init_ask_port = __esm(() => {
94174
92352
  init_logger2();
94175
92353
  init_interaction();
94176
92354
  });
94177
92355
 
94178
- // src/agents/acp-sdk/profile-map.ts
92356
+ // src/agents/acp/profile-map.ts
94179
92357
  function acpProfileFor(mode) {
94180
92358
  return PROFILE_BY_MODE[mode];
94181
92359
  }
@@ -94188,7 +92366,7 @@ var init_profile_map = __esm(() => {
94188
92366
  });
94189
92367
  });
94190
92368
 
94191
- // src/agents/acp-sdk/open-context.ts
92369
+ // src/agents/acp/open-context.ts
94192
92370
  function backendEnv(modelEnv) {
94193
92371
  const allowed2 = buildAllowedEnv(modelEnv === undefined ? undefined : { modelEnv: { ...modelEnv } });
94194
92372
  return Object.fromEntries(Object.entries(allowed2).filter((entry2) => typeof entry2[1] === "string" && ENV_NAME.test(entry2[0]) && !entry2[1].includes("\x00")));
@@ -94244,7 +92422,7 @@ var init_open_context = __esm(() => {
94244
92422
  ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
94245
92423
  });
94246
92424
 
94247
- // src/agents/acp-sdk/tool-audit.ts
92425
+ // src/agents/acp/tool-audit.ts
94248
92426
  function inputOf2(input) {
94249
92427
  return typeof input === "object" && input !== null && !Array.isArray(input) ? input : { value: input };
94250
92428
  }
@@ -94300,7 +92478,7 @@ var init_tool_audit2 = __esm(() => {
94300
92478
  init_src();
94301
92479
  });
94302
92480
 
94303
- // src/agents/acp-sdk/turn-slot.ts
92481
+ // src/agents/acp/turn-slot.ts
94304
92482
  function createTurnSlot() {
94305
92483
  let running;
94306
92484
  return {
@@ -94320,7 +92498,7 @@ var init_turn_slot = __esm(() => {
94320
92498
  IDLE_SIGNAL2 = new AbortController().signal;
94321
92499
  });
94322
92500
 
94323
- // src/agents/acp-sdk/session.ts
92501
+ // src/agents/acp/session.ts
94324
92502
  import { stat as stat10 } from "fs/promises";
94325
92503
  function errorText(err) {
94326
92504
  return err instanceof Error ? err.message : String(err);
@@ -94336,6 +92514,18 @@ async function discard(store, name) {
94336
92514
  });
94337
92515
  });
94338
92516
  }
92517
+ async function discardAcpSessionLeftover(transcriptDir, name) {
92518
+ if (transcriptDir === undefined)
92519
+ return;
92520
+ try {
92521
+ await discard(transcriptStoreFor(transcriptDir), name);
92522
+ } catch (err) {
92523
+ getSafeLogger2()?.warn(STAGE2, "Could not discard a stale ACP session transcript", {
92524
+ sessionName: name,
92525
+ error: errorText(err)
92526
+ });
92527
+ }
92528
+ }
94339
92529
  function processHooks(base) {
94340
92530
  return {
94341
92531
  spawned: (pid) => {
@@ -94354,7 +92544,7 @@ function killAgent(process5) {
94354
92544
  killProcessGroup(process5.pid, "SIGKILL");
94355
92545
  }
94356
92546
  async function openBackend2(base, resume) {
94357
- const backend2 = _acpSdkDeps.acpBackend(backendOptions(base.agent, base.opts, processHooks(base)));
92547
+ const backend2 = _acpDeps.acpBackend(backendOptions(base.agent, base.opts, processHooks(base)));
94358
92548
  return backend2.open(openContext({
94359
92549
  name: base.name,
94360
92550
  opts: base.opts,
@@ -94441,7 +92631,7 @@ async function createSession(name, agent, opts) {
94441
92631
  process: { pid: undefined }
94442
92632
  };
94443
92633
  try {
94444
- const rateCard = await _acpSdkDeps.resolveRateCard(opts.modelDef.model);
92634
+ const rateCard = await _acpDeps.resolveRateCard(opts.modelDef.model);
94445
92635
  const opened = await openWithLeftover(base);
94446
92636
  const protocolIds = await protocolIdsOf(base.store, name);
94447
92637
  const handle2 = Object.freeze({
@@ -94517,7 +92707,7 @@ async function reopenFresh(session2) {
94517
92707
  await discard(session2.store, session2.name);
94518
92708
  session2.opened = await openBackend2(session2, undefined);
94519
92709
  }
94520
- var STAGE2 = "acp-sdk", DISCARD_CODES, _acpSdkDeps;
92710
+ var STAGE2 = "acp", DISCARD_CODES, _acpDeps;
94521
92711
  var init_session3 = __esm(() => {
94522
92712
  init_src();
94523
92713
  init_client5();
@@ -94535,7 +92725,7 @@ var init_session3 = __esm(() => {
94535
92725
  "AGENT_SESSION_NOT_FOUND",
94536
92726
  "TRANSCRIPT_CORRUPT"
94537
92727
  ]);
94538
- _acpSdkDeps = {
92728
+ _acpDeps = {
94539
92729
  acpBackend: (options) => acpBackend(options),
94540
92730
  launchCandidateKind: (agent) => launchCandidateKind(agent),
94541
92731
  resolveRateCard: (modelId) => resolveRateCard(modelId),
@@ -94550,7 +92740,7 @@ var init_session3 = __esm(() => {
94550
92740
  };
94551
92741
  });
94552
92742
 
94553
- // src/agents/acp-sdk/prompt-retry.ts
92743
+ // src/agents/acp/prompt-retry.ts
94554
92744
  function isRetryablePromptError(err) {
94555
92745
  if (isSessionGone(err))
94556
92746
  return false;
@@ -94571,8 +92761,8 @@ var init_prompt_retry = __esm(() => {
94571
92761
  RETRYABLE_RPC_CODES = new Set([-32603, -32700]);
94572
92762
  });
94573
92763
 
94574
- // src/agents/acp-sdk/stream-bridge.ts
94575
- import { randomUUID as randomUUID14 } from "crypto";
92764
+ // src/agents/acp/stream-bridge.ts
92765
+ import { randomUUID as randomUUID13 } from "crypto";
94576
92766
  function toolUpdate(toolName) {
94577
92767
  return toolName === undefined ? { kind: "agent.tool_call_update" } : { kind: "agent.tool_call_update", toolName };
94578
92768
  }
@@ -94617,7 +92807,7 @@ function sender(ctx) {
94617
92807
  try {
94618
92808
  ctx.emit(event);
94619
92809
  } catch (err) {
94620
- getSafeLogger2()?.debug("acp-sdk", "A stream listener threw; the turn continues", {
92810
+ getSafeLogger2()?.debug("acp", "A stream listener threw; the turn continues", {
94621
92811
  sessionName: ctx.sessionName,
94622
92812
  error: err instanceof Error ? err.message : String(err)
94623
92813
  });
@@ -94625,7 +92815,7 @@ function sender(ctx) {
94625
92815
  };
94626
92816
  }
94627
92817
  function startCall(ctx, now = Date.now) {
94628
- const callId = randomUUID14();
92818
+ const callId = randomUUID13();
94629
92819
  const send = sender(ctx);
94630
92820
  const base = {
94631
92821
  callId,
@@ -94673,8 +92863,8 @@ var init_stream_bridge = __esm(() => {
94673
92863
  init_logger2();
94674
92864
  });
94675
92865
 
94676
- // src/agents/acp-sdk/turn-loop.ts
94677
- import { randomUUID as randomUUID15 } from "crypto";
92866
+ // src/agents/acp/turn-loop.ts
92867
+ import { randomUUID as randomUUID14 } from "crypto";
94678
92868
  function used(state) {
94679
92869
  return state.turnCount + state.asked;
94680
92870
  }
@@ -94750,7 +92940,7 @@ async function sendWithRetries(loop, attempt) {
94750
92940
  retry: retryIndex + 1,
94751
92941
  of: retries
94752
92942
  });
94753
- await _acpSdkDeps.delay(promptRetryDelayMs(retryIndex), attempt.controller.signal);
92943
+ await _acpDeps.delay(promptRetryDelayMs(retryIndex), attempt.controller.signal);
94754
92944
  }
94755
92945
  }
94756
92946
  return sendOnce(loop.session, loop.state.currentPrompt, attempt);
@@ -94765,7 +92955,7 @@ async function runIteration(loop) {
94765
92955
  }
94766
92956
  const timer = armDeadline(controller, loop.deadline.remainingMs());
94767
92957
  const call2 = startCall(session2.stream);
94768
- const turnId = opts.turnId ?? randomUUID15();
92958
+ const turnId = opts.turnId ?? randomUUID14();
94769
92959
  try {
94770
92960
  session2.opts.onActiveCall?.(call2.callId, async () => controller.abort(new WatchdogCancel));
94771
92961
  session2.slot.set({
@@ -94792,7 +92982,7 @@ async function runIteration(loop) {
94792
92982
  session2.slot.clear();
94793
92983
  }
94794
92984
  }
94795
- function replyContext2(loop) {
92985
+ function replyContext(loop) {
94796
92986
  return { interactionHandler: loop.opts.interactionHandler, signal: loop.opts.signal, stage: STAGE3 };
94797
92987
  }
94798
92988
  function answerOf(loop, reply2) {
@@ -94805,13 +92995,13 @@ function answerOf(loop, reply2) {
94805
92995
  async function nextPrompt(loop, output) {
94806
92996
  const toolCall = extractContextToolCall(output);
94807
92997
  if (toolCall !== null) {
94808
- const reply2 = await awaitInteractionReply(replyContext2(loop), toContextToolInteraction(toolCall), " for context-tool: ");
92998
+ const reply2 = await awaitInteractionReply(replyContext(loop), toContextToolInteraction(toolCall), " for context-tool: ");
94809
92999
  return answerOf(loop, reply2);
94810
93000
  }
94811
93001
  const question = extractQuestion(output);
94812
93002
  if (question === null)
94813
93003
  return;
94814
- const answer = answerOf(loop, await awaitInteractionReply(replyContext2(loop), { kind: "question", text: question }, ": "));
93004
+ const answer = answerOf(loop, await awaitInteractionReply(replyContext(loop), { kind: "question", text: question }, ": "));
94815
93005
  if (answer !== undefined)
94816
93006
  loop.state.interactions.push({ turnIndex: loop.state.turnCount, question, reply: answer });
94817
93007
  return answer;
@@ -94859,7 +93049,7 @@ async function runLoop(loop) {
94859
93049
  rateCard: loop.session.rateCard
94860
93050
  });
94861
93051
  }
94862
- async function runTurnLoop2(session2, prompt, opts) {
93052
+ async function runTurnLoop(session2, prompt, opts) {
94863
93053
  if (session2.running !== undefined) {
94864
93054
  throw new NaxError(`ACP session "${session2.name}" already has a turn in flight`, "ACP_SDK_TURN_IN_FLIGHT", {
94865
93055
  stage: STAGE3,
@@ -94869,7 +93059,7 @@ async function runTurnLoop2(session2, prompt, opts) {
94869
93059
  const run = runLoop({
94870
93060
  session: session2,
94871
93061
  opts,
94872
- max: opts.maxInteractions ?? DEFAULT_MAX_INTERACTIONS2,
93062
+ max: opts.maxInteractions ?? DEFAULT_MAX_INTERACTIONS,
94873
93063
  deadline: createTurnDeadline(session2.opts.timeoutSeconds),
94874
93064
  state: {
94875
93065
  turnCount: 0,
@@ -94893,7 +93083,7 @@ async function runTurnLoop2(session2, prompt, opts) {
94893
93083
  session2.running = undefined;
94894
93084
  }
94895
93085
  }
94896
- var STAGE3 = "acp-sdk", DEFAULT_MAX_INTERACTIONS2 = 10;
93086
+ var STAGE3 = "acp", DEFAULT_MAX_INTERACTIONS = 10;
94897
93087
  var init_turn_loop2 = __esm(() => {
94898
93088
  init_src();
94899
93089
  init_errors4();
@@ -94907,7 +93097,7 @@ var init_turn_loop2 = __esm(() => {
94907
93097
  init_stream_bridge();
94908
93098
  });
94909
93099
 
94910
- // src/agents/acp-sdk/complete.ts
93100
+ // src/agents/acp/complete.ts
94911
93101
  function sessionOptsFor(adapterName, options, timeoutMs) {
94912
93102
  return {
94913
93103
  agentName: adapterName,
@@ -94962,7 +93152,7 @@ async function runComplete(adapterName, agent, prompt, options) {
94962
93152
  }
94963
93153
  const session2 = await openClassified(name, agent, sessionOptsFor(adapterName, options, timeoutMs));
94964
93154
  try {
94965
- const result = await runTurnLoop2(session2, prompt, {
93155
+ const result = await runTurnLoop(session2, prompt, {
94966
93156
  interactionHandler: NO_OP_INTERACTION_HANDLER2,
94967
93157
  maxInteractions: 1,
94968
93158
  ...options.signal === undefined ? {} : { signal: options.signal }
@@ -94989,7 +93179,7 @@ async function openClassified(name, agent, opts) {
94989
93179
  throw new SessionFailureError(failure3.message, failure3.adapterFailure);
94990
93180
  }
94991
93181
  }
94992
- var STAGE4 = "acp-sdk", DEFAULT_COMPLETE_TIMEOUT_MS = 120000, MS_PER_SECOND = 1000;
93182
+ var STAGE4 = "acp", DEFAULT_COMPLETE_TIMEOUT_MS = 120000, MS_PER_SECOND = 1000;
94993
93183
  var init_complete2 = __esm(() => {
94994
93184
  init_src();
94995
93185
  init_errors4();
@@ -95003,11 +93193,11 @@ var init_complete2 = __esm(() => {
95003
93193
  init_turn_loop2();
95004
93194
  });
95005
93195
 
95006
- // src/agents/acp-sdk/entries.ts
95007
- function acpSdkEntry(agentName) {
93196
+ // src/agents/acp/entries.ts
93197
+ function acpEntry(agentName) {
95008
93198
  return Object.hasOwn(ENTRIES, agentName) ? ENTRIES[agentName] : undefined;
95009
93199
  }
95010
- var ENTRIES, ACP_SDK_AGENT_NAMES, UNSUPPORTED_ENTRY;
93200
+ var ENTRIES, ACP_AGENT_NAMES2, UNSUPPORTED_ENTRY;
95011
93201
  var init_entries = __esm(() => {
95012
93202
  ENTRIES = Object.freeze({
95013
93203
  claude: {
@@ -95046,7 +93236,7 @@ var init_entries = __esm(() => {
95046
93236
  maxContextTokens: 128000
95047
93237
  }
95048
93238
  });
95049
- ACP_SDK_AGENT_NAMES = new Set(Object.keys(ENTRIES));
93239
+ ACP_AGENT_NAMES2 = new Set(Object.keys(ENTRIES));
95050
93240
  UNSUPPORTED_ENTRY = Object.freeze({
95051
93241
  binary: "",
95052
93242
  displayName: "ACP Agent",
@@ -95055,7 +93245,7 @@ var init_entries = __esm(() => {
95055
93245
  });
95056
93246
  });
95057
93247
 
95058
- // src/agents/acp-sdk/adapter.ts
93248
+ // src/agents/acp/adapter.ts
95059
93249
  function notifyEstablished(opts, protocolIds, name) {
95060
93250
  if (opts.onSessionEstablished === undefined || protocolIds === undefined)
95061
93251
  return;
@@ -95069,7 +93259,7 @@ function notifyEstablished(opts, protocolIds, name) {
95069
93259
  }
95070
93260
  }
95071
93261
 
95072
- class AcpSdkAgentAdapter {
93262
+ class AcpAgentAdapter {
95073
93263
  name;
95074
93264
  displayName;
95075
93265
  binary;
@@ -95077,7 +93267,7 @@ class AcpSdkAgentAdapter {
95077
93267
  entry;
95078
93268
  live = new Map;
95079
93269
  constructor(agentName) {
95080
- this.entry = acpSdkEntry(agentName);
93270
+ this.entry = acpEntry(agentName);
95081
93271
  const shown2 = this.entry ?? UNSUPPORTED_ENTRY;
95082
93272
  this.name = agentName;
95083
93273
  this.displayName = shown2.displayName;
@@ -95089,10 +93279,10 @@ class AcpSdkAgentAdapter {
95089
93279
  };
95090
93280
  }
95091
93281
  async isInstalled() {
95092
- return this.entry !== undefined && _acpSdkDeps.launchCandidateKind(this.entry.agent) !== undefined;
93282
+ return this.entry !== undefined && _acpDeps.launchCandidateKind(this.entry.agent) !== undefined;
95093
93283
  }
95094
93284
  launchNote() {
95095
- if (this.entry === undefined || _acpSdkDeps.launchCandidateKind(this.entry.agent) !== "npx")
93285
+ if (this.entry === undefined || _acpDeps.launchCandidateKind(this.entry.agent) !== "npx")
95096
93286
  return;
95097
93287
  return `Only the npx fallback can launch ACP agent "${this.name}"; the first run downloads it inside the startup deadline`;
95098
93288
  }
@@ -95130,7 +93320,7 @@ class AcpSdkAgentAdapter {
95130
93320
  return session2.handle;
95131
93321
  }
95132
93322
  async sendTurn(handle2, prompt, opts) {
95133
- return runTurnLoop2(this.sessionFor(handle2.id), prompt, opts);
93323
+ return runTurnLoop(this.sessionFor(handle2.id), prompt, opts);
95134
93324
  }
95135
93325
  async closeSession(handle2) {
95136
93326
  const session2 = this.live.get(handle2.id);
@@ -95155,12 +93345,12 @@ class AcpSdkAgentAdapter {
95155
93345
  requireEntry(sessionName) {
95156
93346
  if (this.entry !== undefined)
95157
93347
  return this.entry;
95158
- throw new NaxError(`Agent "${this.name}" has no ACP launcher; it cannot run on agent.acp.transport "sdk"`, "ACP_AGENT_UNSUPPORTED", { stage: STAGE5, agentName: this.name, sessionName });
93348
+ throw new NaxError(`Agent "${this.name}" has no ACP launcher, so it cannot run as an ACP agent`, "ACP_AGENT_UNSUPPORTED", { stage: STAGE5, agentName: this.name, sessionName });
95159
93349
  }
95160
93350
  async requireWorkdir(sessionName, workdir) {
95161
- if (await _acpSdkDeps.cwdExists(workdir))
93351
+ if (await _acpDeps.cwdExists(workdir))
95162
93352
  return;
95163
- throw new NaxError(`[acp-sdk] Session cwd does not exist: ${workdir} \u2014 cannot start agent "${this.name}". If this is a new package for the feature, ensure its directory is created before the run.`, "SESSION_CWD_MISSING", { stage: "open-session", agentName: this.name, cwd: workdir, sessionName });
93353
+ throw new NaxError(`[acp] Session cwd does not exist: ${workdir} \u2014 cannot start agent "${this.name}". If this is a new package for the feature, ensure its directory is created before the run.`, "SESSION_CWD_MISSING", { stage: "open-session", agentName: this.name, cwd: workdir, sessionName });
95164
93354
  }
95165
93355
  sessionFor(id) {
95166
93356
  const session2 = this.live.get(id);
@@ -95172,8 +93362,8 @@ class AcpSdkAgentAdapter {
95172
93362
  });
95173
93363
  }
95174
93364
  }
95175
- var STAGE5 = "acp-sdk";
95176
- var init_adapter2 = __esm(() => {
93365
+ var STAGE5 = "acp";
93366
+ var init_adapter = __esm(() => {
95177
93367
  init_errors4();
95178
93368
  init_logger2();
95179
93369
  init_env();
@@ -95184,9 +93374,17 @@ var init_adapter2 = __esm(() => {
95184
93374
  init_turn_loop2();
95185
93375
  });
95186
93376
 
95187
- // src/agents/acp-sdk/index.ts
95188
- var init_acp_sdk = __esm(() => {
95189
- init_adapter2();
93377
+ // src/agents/acp/index.ts
93378
+ var exports_acp = {};
93379
+ __export(exports_acp, {
93380
+ ACP_AGENT_NAMES: () => ACP_AGENT_NAMES2,
93381
+ AcpAgentAdapter: () => AcpAgentAdapter,
93382
+ _acpDeps: () => _acpDeps,
93383
+ acpEntry: () => acpEntry,
93384
+ discardAcpSessionLeftover: () => discardAcpSessionLeftover
93385
+ });
93386
+ var init_acp2 = __esm(() => {
93387
+ init_adapter();
95190
93388
  init_entries();
95191
93389
  init_session3();
95192
93390
  });
@@ -95246,11 +93444,11 @@ var init_native_agent = __esm(() => {
95246
93444
  });
95247
93445
 
95248
93446
  // src/agents/registry.ts
95249
- function acpAdapterFor(name, transport) {
95250
- return transport === "sdk" ? new AcpSdkAgentAdapter(name) : new AcpAgentAdapter(name);
93447
+ function acpAdapterFor(name) {
93448
+ return new AcpAgentAdapter(name);
95251
93449
  }
95252
93450
  function adapterFor(name) {
95253
- return name === NATIVE_AGENT_NAME ? new NativeAgentAdapter : acpAdapterFor(name, DEFAULT_ACP_TRANSPORT);
93451
+ return name === NATIVE_AGENT_NAME ? new NativeAgentAdapter : acpAdapterFor(name);
95254
93452
  }
95255
93453
  function buildAdapterList() {
95256
93454
  return [...Array.from(_registryTestAdapters.values()), ...KNOWN_AGENT_NAMES.map(adapterFor)];
@@ -95268,14 +93466,10 @@ function createAgentRegistry(config2) {
95268
93466
  const adapterCache = new Map;
95269
93467
  const protocol = config2.agent?.protocol ?? DEFAULT_AGENT_PROTOCOL;
95270
93468
  logger?.info("agents", `Agent protocol: ${protocol}`, { protocol, hasConfig: !!config2.agent });
95271
- const transport = config2.agent?.acp?.transport ?? DEFAULT_ACP_TRANSPORT;
95272
- if (transport !== DEFAULT_ACP_TRANSPORT) {
95273
- logger?.info("agents", `ACP transport: ${transport} (S4b development key)`, { transport });
95274
- }
95275
93469
  function cachedAdapter(name) {
95276
93470
  let adapter = adapterCache.get(name);
95277
93471
  if (adapter === undefined) {
95278
- adapter = name === NATIVE_AGENT_NAME ? new NativeAgentAdapter(undefined, config2.agent?.native?.catalogOverrides ?? []) : acpAdapterFor(name, transport);
93472
+ adapter = name === NATIVE_AGENT_NAME ? new NativeAgentAdapter(undefined, config2.agent?.native?.catalogOverrides ?? []) : acpAdapterFor(name);
95279
93473
  adapterCache.set(name, adapter);
95280
93474
  logger?.debug("agents", `Created ${adapter.constructor.name} for ${name}`, { name });
95281
93475
  }
@@ -95312,8 +93506,7 @@ var init_registry6 = __esm(() => {
95312
93506
  init_src();
95313
93507
  init_config();
95314
93508
  init_logger2();
95315
- init_adapter();
95316
- init_acp_sdk();
93509
+ init_acp2();
95317
93510
  init_native_agent();
95318
93511
  KNOWN_AGENT_NAMES = ["claude", "codex", "opencode", "gemini", "aider", "pi", NATIVE_AGENT_NAME];
95319
93512
  _registryTestAdapters = new Map;
@@ -95938,7 +94131,6 @@ var init_utils = __esm(() => {
95938
94131
  // src/agents/index.ts
95939
94132
  var init_agents = __esm(() => {
95940
94133
  init_src();
95941
- init_acp();
95942
94134
  init_complete_exception_classifier();
95943
94135
  init_cost();
95944
94136
  init_manager2();
@@ -95954,8 +94146,7 @@ var init_agents = __esm(() => {
95954
94146
  // src/cli/agents.ts
95955
94147
  async function agentsListCommand(config2, _workdir) {
95956
94148
  const acpReachable = (config2.agent?.protocol ?? DEFAULT_AGENT_PROTOCOL) !== "native";
95957
- const transport = config2.agent?.acp?.transport ?? DEFAULT_ACP_TRANSPORT;
95958
- const adapters = acpReachable ? Array.from(ACP_SDK_AGENT_NAMES).map((name) => acpAdapterFor(name, transport)) : [];
94149
+ const adapters = acpReachable ? Array.from(ACP_AGENT_NAMES2).map((name) => acpAdapterFor(name)) : [];
95959
94150
  const defaultAgent = resolveDefaultAgent(config2);
95960
94151
  const acpVersions = await Promise.all(adapters.map(async (agent) => ({
95961
94152
  name: agent.name,
@@ -96023,7 +94214,7 @@ var _cliAgentsDeps;
96023
94214
  var init_agents2 = __esm(() => {
96024
94215
  init_src();
96025
94216
  init_agents();
96026
- init_acp_sdk();
94217
+ init_acp2();
96027
94218
  init_native_agent();
96028
94219
  init_version_detection();
96029
94220
  init_config();
@@ -97065,7 +95256,6 @@ var init_config_descriptions = __esm(() => {
97065
95256
  agent: "Agent protocol configuration (ACP-003)",
97066
95257
  "agent.protocol": "Protocol for agent communication: 'acp' (default) | 'native' | 'hybrid'",
97067
95258
  "agent.acp": "ACP agent transport settings (claude, codex, opencode, gemini, pi)",
97068
- "agent.acp.transport": "How ACP agents are driven: 'sdk' (default) uses @nathapp/nax-agent-acp in-process; 'acpx' shells out to the acpx CLI (S4b development key; S4b-5 removes acpx and this key)",
97069
95259
  "agent.native.instructionFileName": "Repository instruction filename (default: AGENTS.md). Custom Markdown basenames load without AGENTS/CLAUDE fallbacks.",
97070
95260
  "agent.native.catalogOverrides": "Native only. Explicit catalog entries for model ids newer than the bundled pi-ai snapshot, plus optional provider-wide baseUrl/headers.",
97071
95261
  "agent.native.catalogOverrides.baseUrl": "Provider-wide endpoint redirect. WARNING: applies to EVERY model of that provider, bundled ones included, and the provider's stored credential is sent to this host. https only (http allowed for localhost/127.0.0.1); credentials in the URL are rejected.",
@@ -97087,7 +95277,7 @@ var init_config_descriptions = __esm(() => {
97087
95277
  "agent.spinBreaker.stopAfterNoProgressSeconds": "Seconds with no new call key after which a repeat run ends the turn so a slow spin stops as fail-spin before the tool-call-only idle watchdog cancels it as fail-stale (default: half agent.idleWatchdog.toolCallOnlyIdleTimeoutSeconds, 900 when that watchdog is off; 0 disables) (nax#2017)",
97088
95278
  "quality.testing": "Hermetic test enforcement \u2014 per-package overridable (ENH-010)",
97089
95279
  "quality.testing.hermetic": "Inject hermetic test requirement into prompts \u2014 never call real external services in tests (default: true)",
97090
- "quality.testing.externalBoundaries": "Project-specific CLI tools/clients to mock (e.g. ['claude', 'acpx', 'redis'])",
95280
+ "quality.testing.externalBoundaries": "Project-specific CLI tools/clients to mock (e.g. ['claude', 'gh', 'redis'])",
97091
95281
  "quality.testing.mockGuidance": "Project-specific mocking guidance injected verbatim into the prompt",
97092
95282
  mcp: "External MCP servers the native agent may call tools on (client only; nax is not an MCP server)",
97093
95283
  "mcp.servers": "Map of server id to server block. The id is the tool-name namespace: a tool named search_graph on server codebase-memory is advertised as codebase-memory__search_graph",
@@ -107976,7 +106166,7 @@ var init_escalation = __esm(() => {
107976
106166
  });
107977
106167
 
107978
106168
  // src/execution/lock.ts
107979
- import { randomUUID as randomUUID16 } from "crypto";
106169
+ import { randomUUID as randomUUID15 } from "crypto";
107980
106170
  import { rename as rename6, unlink as unlink6 } from "fs/promises";
107981
106171
  import { hostname as hostname3 } from "os";
107982
106172
  import path14 from "path";
@@ -108020,7 +106210,7 @@ function parseHolder(raw) {
108020
106210
  }
108021
106211
  }
108022
106212
  async function publishFileAtomically(targetPath, content) {
108023
- const tempPath = `${targetPath}.${randomUUID16()}.tmp`;
106213
+ const tempPath = `${targetPath}.${randomUUID15()}.tmp`;
108024
106214
  try {
108025
106215
  await Bun.write(tempPath, content);
108026
106216
  const fs = await import("fs");
@@ -110783,7 +108973,7 @@ var init_scope_files = __esm(() => {
110783
108973
  });
110784
108974
 
110785
108975
  // src/pipeline/stages/context.ts
110786
- import { randomUUID as randomUUID17 } from "crypto";
108976
+ import { randomUUID as randomUUID16 } from "crypto";
110787
108977
  import { join as join99 } from "path";
110788
108978
  async function runV2Path(ctx) {
110789
108979
  const logger = getLogger2();
@@ -111024,7 +109214,7 @@ var init_context3 = __esm(() => {
111024
109214
  createOrchestrator: createDefaultOrchestrator,
111025
109215
  loadPlugins: loadPluginProviders,
111026
109216
  v1FeatureProvider: () => new FeatureContextProvider,
111027
- uuid: () => randomUUID17(),
109217
+ uuid: () => randomUUID16(),
111028
109218
  readDigest: readDigestFile,
111029
109219
  writeDigest: writeDigestFile,
111030
109220
  loadFeatureManifests,
@@ -111847,52 +110037,9 @@ var exports_merge_conflict_rectify = {};
111847
110037
  __export(exports_merge_conflict_rectify, {
111848
110038
  _mergeRectifyDeps: () => _mergeRectifyDeps,
111849
110039
  buildRectificationPipelineContext: () => buildRectificationPipelineContext,
111850
- closeStaleAcpSession: () => closeStaleAcpSession,
111851
- evictStaleSessions: () => evictStaleSessions,
111852
110040
  rectifyConflictedStory: () => rectifyConflictedStory,
111853
110041
  rectifyMergeFailure: () => rectifyMergeFailure
111854
110042
  });
111855
- async function closeStaleAcpSession(worktreePath, sessionName) {
111856
- const logger = getSafeLogger2();
111857
- try {
111858
- const cmd = ["acpx", "--cwd", worktreePath, "claude", "sessions", "close", sessionName];
111859
- logger?.debug("parallel", "Closing stale ACP session before rectification", { sessionName });
111860
- const proc = _mergeRectifyDeps.typedSpawn(cmd, {
111861
- stdout: "pipe",
111862
- stderr: "pipe",
111863
- detached: true
111864
- });
111865
- let timedOut = false;
111866
- await new Promise((resolve39) => {
111867
- let settled2 = false;
111868
- const finish = () => {
111869
- if (settled2)
111870
- return;
111871
- settled2 = true;
111872
- clearTimeout(timer);
111873
- resolve39();
111874
- };
111875
- const timer = setTimeout(() => {
111876
- timedOut = true;
111877
- try {
111878
- _mergeRectifyDeps.killProcessGroup(proc.pid, "SIGKILL");
111879
- } catch {}
111880
- finish();
111881
- }, _mergeRectifyDeps.timeoutMs);
111882
- proc.exited.then(finish, finish);
111883
- });
111884
- if (timedOut) {
111885
- logger?.debug("parallel", "Stale ACP session eviction timed out \u2014 swallowed (best-effort)", {
111886
- sessionName,
111887
- timeoutMs: _mergeRectifyDeps.timeoutMs
111888
- });
111889
- }
111890
- } catch {}
111891
- }
111892
- async function evictStaleSessions(worktreePath) {
111893
- const cwd = worktreePath ?? ".";
111894
- await closeStaleAcpSession(cwd, "nax-evict-stale-sentinel");
111895
- }
111896
110043
  function rectifyMergeFailure(storyId, cost, mergeResult) {
111897
110044
  return {
111898
110045
  success: false,
@@ -111945,7 +110092,11 @@ async function rectifyConflictedStory(options) {
111945
110092
  storyId,
111946
110093
  role: "main"
111947
110094
  });
111948
- await closeStaleAcpSession(worktreePath, staleSessionName);
110095
+ const transcriptDir = deriveNativeTranscriptDir({
110096
+ featureName: prd.feature,
110097
+ transcriptRoot: pipelineContextBase.runtime.outputDir
110098
+ });
110099
+ await _mergeRectifyDeps.discardStaleSession(transcriptDir, staleSessionName).catch(() => {});
111949
110100
  const story = prd.userStories.find((s) => s.id === storyId);
111950
110101
  if (!story) {
111951
110102
  return { success: false, storyId, cost: 0, finalConflict: false, pipelineFailure: true };
@@ -111992,18 +110143,18 @@ async function rectifyConflictedStory(options) {
111992
110143
  return { success: false, storyId, cost: 0, finalConflict: false, pipelineFailure: true };
111993
110144
  }
111994
110145
  }
111995
- var STALE_SESSION_CLOSE_TIMEOUT_MS = 3000, _mergeRectifyDeps;
110146
+ var _mergeRectifyDeps;
111996
110147
  var init_merge_conflict_rectify = __esm(() => {
111997
110148
  init_internal();
111998
- init_bun_deps();
111999
110149
  init_logger2();
110150
+ init_manager_deps();
112000
110151
  init_worktree();
112001
110152
  init_parallel_worker();
112002
110153
  _mergeRectifyDeps = {
112003
- typedSpawn,
112004
- spawn: typedSpawn,
112005
- killProcessGroup,
112006
- timeoutMs: STALE_SESSION_CLOSE_TIMEOUT_MS
110154
+ discardStaleSession: async (transcriptDir, name) => {
110155
+ const { discardAcpSessionLeftover: discardAcpSessionLeftover2 } = await Promise.resolve().then(() => (init_acp2(), exports_acp));
110156
+ await discardAcpSessionLeftover2(transcriptDir, name);
110157
+ }
112007
110158
  };
112008
110159
  });
112009
110160
 
@@ -113937,7 +112088,7 @@ var init_run_cleanup = __esm(() => {
113937
112088
  });
113938
112089
 
113939
112090
  // src/execution/session-manager-runtime.ts
113940
- async function closePhysicalSession2(descriptor, agentGetFn, force, signal) {
112091
+ async function closePhysicalSession(descriptor, agentGetFn, force, signal) {
113941
112092
  if (!descriptor.handle)
113942
112093
  return;
113943
112094
  const adapter = agentGetFn?.(descriptor.agent);
@@ -113956,7 +112107,7 @@ async function closeStorylessSession(sessionManager, descriptor, agentGetFn, opt
113956
112107
  } catch {}
113957
112108
  }
113958
112109
  const force = opts?.force === true || descriptor.state === "FAILED";
113959
- await closePhysicalSession2(descriptor, agentGetFn, force, opts?.signal);
112110
+ await closePhysicalSession(descriptor, agentGetFn, force, opts?.signal);
113960
112111
  return 1;
113961
112112
  }
113962
112113
  function getStorylessCloseChain(state) {
@@ -113979,7 +112130,7 @@ async function closeStorySessions(sessionManager, storyId, agentGetFn, opts) {
113979
112130
  const closedSessions = sessionManager.closeStory(storyId);
113980
112131
  for (const descriptor of closedSessions) {
113981
112132
  const force = opts?.force === true || descriptor.state === "FAILED";
113982
- await closePhysicalSession2(descriptor, agentGetFn, force, opts?.signal);
112133
+ await closePhysicalSession(descriptor, agentGetFn, force, opts?.signal);
113983
112134
  }
113984
112135
  return closedSessions.length;
113985
112136
  }
@@ -113996,7 +112147,7 @@ async function failAndClose(sessionManager, sessionId, agentGetFn) {
113996
112147
  }
113997
112148
  const failed3 = sessionManager.get(sessionId);
113998
112149
  if (failed3) {
113999
- await closePhysicalSession2(failed3, agentGetFn, true);
112150
+ await closePhysicalSession(failed3, agentGetFn, true);
114000
112151
  }
114001
112152
  }
114002
112153
  async function closeAllRunSessions(sessionManager, agentGetFn, opts) {
@@ -118999,14 +117150,14 @@ var init_gitignore_reconcile = __esm(() => {
118999
117150
  });
119000
117151
 
119001
117152
  // src/review/language-commands.ts
119002
- function resolveLanguageCommand(language, check2, which3) {
117153
+ function resolveLanguageCommand(language, check2, which2) {
119003
117154
  const languageTable = LANGUAGE_COMMANDS[language];
119004
117155
  if (!languageTable)
119005
117156
  return null;
119006
117157
  const entry2 = languageTable[check2];
119007
117158
  if (!entry2)
119008
117159
  return null;
119009
- const binaryPath = which3(entry2.binary);
117160
+ const binaryPath = which2(entry2.binary);
119010
117161
  if (!binaryPath)
119011
117162
  return null;
119012
117163
  return entry2.command;
@@ -129141,7 +127292,7 @@ async function checkCanonicalRulesLint(workdir) {
129141
127292
  let ruleCount = 0;
129142
127293
  try {
129143
127294
  for (const root of roots) {
129144
- const rules = await _checkCanonicalRulesDeps.loadCanonicalRules(root);
127295
+ const rules = await _checkCanonicalRulesDeps.loadCanonicalRules(root, { onInvalidFrontmatter: "throw" });
129145
127296
  ruleCount += rules.length;
129146
127297
  }
129147
127298
  } catch (err) {
@@ -129155,6 +127306,16 @@ async function checkCanonicalRulesLint(workdir) {
129155
127306
  message: `Canonical rules lint failed (${err.violations.length} violation(s)): ${detail}`
129156
127307
  };
129157
127308
  }
127309
+ if (err instanceof RulesFrontmatterLintError) {
127310
+ const first = err.issues[0];
127311
+ const detail = first ? `${first.file}: ${first.message}` : "unknown location";
127312
+ return {
127313
+ name: "canonical-rules-lint",
127314
+ tier: "blocker",
127315
+ passed: false,
127316
+ message: `Canonical rules lint failed (${err.issues.length} file(s) with invalid frontmatter): ${detail}`
127317
+ };
127318
+ }
129158
127319
  return {
129159
127320
  name: "canonical-rules-lint",
129160
127321
  tier: "blocker",
@@ -129503,10 +127664,10 @@ async function findMissingNativeCredentials(config2) {
129503
127664
  function describeMissingNativeCredentials(missing) {
129504
127665
  const uses = missing.map(({ provider: provider2, tiers }) => `${provider2} (${tiers.map((tier) => `models.native.${tier}`).join(", ")})`).join("; ");
129505
127666
  const logins = missing.map(({ provider: provider2 }) => `nax auth login ${provider2}`).join(" / ");
129506
- 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.`;
127667
+ 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 ACP agent.`;
129507
127668
  }
129508
127669
  function describeCredentialFault(error48) {
129509
- return `The default native agent's credentials could not be read (${error48.code}): ${error48.message} ` + `Fix the credential source, or set agent.default "claude" to use an acpx agent.`;
127670
+ return `The default native agent's credentials could not be read (${error48.code}): ${error48.message} ` + `Fix the credential source, or set agent.default "claude" to use an ACP agent.`;
129510
127671
  }
129511
127672
  async function checkNativeCredentials(config2) {
129512
127673
  try {
@@ -131767,6 +129928,69 @@ function collectCanonicalRuleRoots(workdir, globCanonicalRuleFiles = _rulesLintD
131767
129928
  }
131768
129929
  return [...roots].sort();
131769
129930
  }
129931
+ async function loadRootForLint(root, deps) {
129932
+ try {
129933
+ const rules = await deps.loadCanonicalRules(root, { onInvalidFrontmatter: "throw" });
129934
+ return { kind: "loaded", rules };
129935
+ } catch (err) {
129936
+ if (err instanceof RulesFrontmatterLintError)
129937
+ return { kind: "frontmatter-errors", issues: err.issues };
129938
+ return { kind: "failed", cause: err };
129939
+ }
129940
+ }
129941
+ function reportFrontmatterIssues(issues, root, logger) {
129942
+ for (const issue2 of issues) {
129943
+ logger.error("rules-lint", "Canonical rule frontmatter is invalid \u2014 nax will ignore this file at run time", {
129944
+ file: issue2.file,
129945
+ error: issue2.message,
129946
+ root,
129947
+ code: "INVALID_FRONTMATTER"
129948
+ });
129949
+ }
129950
+ }
129951
+ function lintRulesForRoot(rules, root, isSinglePackageRepo, deps, logger) {
129952
+ let warningCount = 0;
129953
+ for (const rule of rules) {
129954
+ for (const warning of rule.warnings ?? []) {
129955
+ warningCount++;
129956
+ logger.warn("rules-lint", `Rule frontmatter warning: ${warning}`, {
129957
+ file: rule.path ?? rule.fileName,
129958
+ root
129959
+ });
129960
+ }
129961
+ for (const pattern of rule.appliesTo ?? []) {
129962
+ const globResult = deps.globHasMatch(pattern, root);
129963
+ if (globResult === "match")
129964
+ continue;
129965
+ warningCount++;
129966
+ if (globResult === "unknown") {
129967
+ logger.warn("rules-lint", "Canonical rule appliesTo glob could not be verified within the dead-glob scan cap", {
129968
+ file: rule.path ?? rule.fileName,
129969
+ pattern,
129970
+ root,
129971
+ code: "GLOB_SCAN_UNKNOWN"
129972
+ });
129973
+ continue;
129974
+ }
129975
+ logger.warn("rules-lint", "Canonical rule appliesTo glob matches no files in the linted repository", {
129976
+ file: rule.path ?? rule.fileName,
129977
+ pattern,
129978
+ root
129979
+ });
129980
+ }
129981
+ if (isSinglePackageRepo && rule.paths && rule.paths.length > 0) {
129982
+ warningCount++;
129983
+ logger.warn("rules-lint", "Canonical rule declares paths: but the repository has no workspace packages \u2014 paths: is inert in single-package repos. Use appliesTo: for file globs.", {
129984
+ file: join135(root, CANONICAL_RULES_DIR, rule.path ?? rule.fileName),
129985
+ code: "INERT_PATHS",
129986
+ paths: rule.paths,
129987
+ root,
129988
+ warningCount
129989
+ });
129990
+ }
129991
+ }
129992
+ return warningCount;
129993
+ }
131770
129994
  async function rulesLintCommand(options, deps = _rulesLintDeps) {
131771
129995
  const workdir = options.dir ?? process.cwd();
131772
129996
  const roots = collectCanonicalRuleRoots(workdir, deps.globCanonicalRuleFiles);
@@ -131779,52 +130003,24 @@ async function rulesLintCommand(options, deps = _rulesLintDeps) {
131779
130003
  }
131780
130004
  let totalRuleFiles = 0;
131781
130005
  let warningCount = 0;
130006
+ const frontmatterIssues = [];
131782
130007
  const failedRoots = [];
131783
130008
  for (const root of roots) {
131784
- let rules;
131785
- try {
131786
- rules = await deps.loadCanonicalRules(root);
131787
- } catch (err) {
131788
- failedRoots.push({ root, cause: err });
130009
+ const load = await loadRootForLint(root, deps);
130010
+ if (load.kind === "failed") {
130011
+ failedRoots.push({ root, cause: load.cause });
131789
130012
  continue;
131790
130013
  }
131791
- totalRuleFiles += rules.length;
131792
- for (const rule of rules) {
131793
- for (const warning of rule.warnings ?? []) {
131794
- warningCount++;
131795
- logger.warn("rules-lint", `Rule frontmatter warning: ${warning}`, {
131796
- file: rule.path ?? rule.fileName,
131797
- root
131798
- });
131799
- }
131800
- for (const pattern of rule.appliesTo ?? []) {
131801
- const globResult = deps.globHasMatch(pattern, root);
131802
- if (globResult === "match")
131803
- continue;
131804
- warningCount++;
131805
- if (globResult === "unknown") {
131806
- logger.warn("rules-lint", "Canonical rule appliesTo glob could not be verified within the dead-glob scan cap", { file: rule.path ?? rule.fileName, pattern, root, code: "GLOB_SCAN_UNKNOWN" });
131807
- continue;
131808
- }
131809
- logger.warn("rules-lint", "Canonical rule appliesTo glob matches no files in the linted repository", {
131810
- file: rule.path ?? rule.fileName,
131811
- pattern,
131812
- root
131813
- });
131814
- }
131815
- if (isSinglePackageRepo && rule.paths && rule.paths.length > 0) {
131816
- warningCount++;
131817
- logger.warn("rules-lint", "Canonical rule declares paths: but the repository has no workspace packages \u2014 paths: is inert in single-package repos. Use appliesTo: for file globs.", {
131818
- file: join135(root, CANONICAL_RULES_DIR, rule.path ?? rule.fileName),
131819
- code: "INERT_PATHS",
131820
- paths: rule.paths,
131821
- root,
131822
- warningCount
131823
- });
131824
- }
130014
+ if (load.kind === "frontmatter-errors") {
130015
+ reportFrontmatterIssues(load.issues, root, logger);
130016
+ frontmatterIssues.push(...load.issues);
130017
+ continue;
131825
130018
  }
130019
+ const rules = load.rules;
130020
+ totalRuleFiles += rules.length;
130021
+ warningCount += lintRulesForRoot(rules, root, isSinglePackageRepo, deps, logger);
131826
130022
  }
131827
- if (totalRuleFiles === 0 && failedRoots.length === 0) {
130023
+ if (totalRuleFiles === 0 && failedRoots.length === 0 && frontmatterIssues.length === 0) {
131828
130024
  warningCount++;
131829
130025
  logger.warn("rules-lint", "Canonical rules store is empty \u2014 no rule files found across any rule root. Run `nax rules migrate` to seed the store.", { code: "EMPTY_STORE", roots: roots.length });
131830
130026
  }
@@ -131836,6 +130032,9 @@ async function rulesLintCommand(options, deps = _rulesLintDeps) {
131836
130032
  causes: failedRoots.map((f) => f.cause)
131837
130033
  });
131838
130034
  }
130035
+ if (frontmatterIssues.length > 0) {
130036
+ throw new NaxError(`Canonical rules lint failed: ${frontmatterIssues.length} rule file(s) have invalid frontmatter. nax ignores these at run time; fix them so the rules actually load.`, "RULES_LINT_FAILED", { stage: "rules-lint", errorCount: frontmatterIssues.length, issues: frontmatterIssues });
130037
+ }
131839
130038
  const scopeLabel = roots.length === 1 ? "repo root" : `${roots.length} rule roots`;
131840
130039
  if (warningCount > 0) {
131841
130040
  console.log(`[WARN] Canonical rules lint completed with ${warningCount} warning(s) (${totalRuleFiles} file(s) across ${scopeLabel}).`);
@@ -146207,12 +144406,7 @@ __export(exports_preflight, {
146207
144406
  function parseCompareList(input) {
146208
144407
  return input.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
146209
144408
  }
146210
- function contestantTransport(profileData, base2) {
146211
- const agent = profileData.agent;
146212
- const transport = agent?.acp?.transport;
146213
- return transport === "acpx" || transport === "sdk" ? transport : base2;
146214
- }
146215
- async function validateContestants(names, projectRoot, deps = _preflightDeps, baseTransport = DEFAULT_ACP_TRANSPORT) {
144409
+ async function validateContestants(names, projectRoot, deps = _preflightDeps) {
146216
144410
  const hasAcpAdapterEntry = deps.hasAcpAdapterEntry ?? _preflightDeps.hasAcpAdapterEntry;
146217
144411
  const loadProfileFn = deps.loadProfile ?? _preflightDeps.loadProfile;
146218
144412
  if (!hasAcpAdapterEntry || !loadProfileFn) {
@@ -146248,7 +144442,7 @@ async function validateContestants(names, projectRoot, deps = _preflightDeps, ba
146248
144442
  });
146249
144443
  continue;
146250
144444
  }
146251
- if (!await deps.isInstalled(resolvedAgent, contestantTransport(resolvedProfileData, baseTransport))) {
144445
+ if (!await deps.isInstalled(resolvedAgent)) {
146252
144446
  errors5.push({
146253
144447
  agent: name,
146254
144448
  reason: "dnf-not-installed",
@@ -146334,15 +144528,15 @@ var BAKEOFF_BRANCH_PREFIX = "nax/bakeoff-", _preflightDeps;
146334
144528
  var init_preflight = __esm(() => {
146335
144529
  init_internal();
146336
144530
  init_agents();
146337
- init_acp_sdk();
144531
+ init_acp2();
146338
144532
  init_config();
146339
144533
  init_profile();
146340
144534
  init_errors4();
146341
144535
  init_logger2();
146342
144536
  init_git2();
146343
144537
  _preflightDeps = {
146344
- isInstalled: (agentName, transport) => acpAdapterFor(agentName, transport).isInstalled(),
146345
- hasAcpAdapterEntry: (name) => ACP_SDK_AGENT_NAMES.has(name),
144538
+ isInstalled: (agentName) => acpAdapterFor(agentName).isInstalled(),
144539
+ hasAcpAdapterEntry: (name) => ACP_AGENT_NAMES2.has(name),
146346
144540
  loadProfile: (profileName, projectRoot) => loadProfile(profileName, projectRoot)
146347
144541
  };
146348
144542
  });
@@ -146523,7 +144717,7 @@ async function persistBakeoffResult(result2, outputDir) {
146523
144717
  async function runBakeoff(options, deps = {}) {
146524
144718
  const merged2 = { ..._coordinatorDeps, ...deps };
146525
144719
  await reclaimStaleBakeoffBranches(options.projectRoot);
146526
- const { validAgents, errors: errors5, profileData } = await merged2.validateContestants(options.agents, options.projectRoot, undefined, options.config.agent?.acp?.transport ?? DEFAULT_ACP_TRANSPORT);
144720
+ const { validAgents, errors: errors5, profileData } = await merged2.validateContestants(options.agents, options.projectRoot);
146527
144721
  const results = [];
146528
144722
  for (const agent of validAgents) {
146529
144723
  const contestantOptions = {