@nathapp/nax 0.54.3 → 0.54.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +93 -42
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -22348,7 +22348,7 @@ var package_default;
22348
22348
  var init_package = __esm(() => {
22349
22349
  package_default = {
22350
22350
  name: "@nathapp/nax",
22351
- version: "0.54.3",
22351
+ version: "0.54.4",
22352
22352
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
22353
22353
  type: "module",
22354
22354
  bin: {
@@ -22425,8 +22425,8 @@ var init_version = __esm(() => {
22425
22425
  NAX_VERSION = package_default.version;
22426
22426
  NAX_COMMIT = (() => {
22427
22427
  try {
22428
- if (/^[0-9a-f]{6,10}$/.test("5acee1f"))
22429
- return "5acee1f";
22428
+ if (/^[0-9a-f]{6,10}$/.test("d0da600"))
22429
+ return "d0da600";
22430
22430
  } catch {}
22431
22431
  try {
22432
22432
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -23144,28 +23144,38 @@ class TelegramInteractionPlugin {
23144
23144
  if (!this.botToken || !this.chatId) {
23145
23145
  throw new Error("Telegram plugin not initialized");
23146
23146
  }
23147
- const text = this.formatMessage(request);
23147
+ const header = this.buildHeader(request);
23148
23148
  const keyboard = this.buildKeyboard(request);
23149
+ const body = this.buildBody(request);
23150
+ const chunks = this.splitText(body, MAX_MESSAGE_CHARS - header.length - 10);
23149
23151
  try {
23150
- const response = await fetch(`https://api.telegram.org/bot${this.botToken}/sendMessage`, {
23151
- method: "POST",
23152
- headers: { "Content-Type": "application/json" },
23153
- body: JSON.stringify({
23154
- chat_id: this.chatId,
23155
- text,
23156
- reply_markup: keyboard ? { inline_keyboard: keyboard } : undefined,
23157
- parse_mode: "Markdown"
23158
- })
23159
- });
23160
- if (!response.ok) {
23161
- const errorBody = await response.text().catch(() => "");
23162
- throw new Error(`Telegram API error (${response.status}): ${errorBody || response.statusText}`);
23163
- }
23164
- const data = await response.json();
23165
- if (!data.ok) {
23166
- throw new Error(`Telegram API returned ok=false: ${JSON.stringify(data)}`);
23152
+ const sentIds = [];
23153
+ for (let i = 0;i < chunks.length; i++) {
23154
+ const isLast = i === chunks.length - 1;
23155
+ const partLabel = chunks.length > 1 ? `[${i + 1}/${chunks.length}] ` : "";
23156
+ const text = `${header}
23157
+ ${partLabel}${chunks[i]}`;
23158
+ const response = await fetch(`https://api.telegram.org/bot${this.botToken}/sendMessage`, {
23159
+ method: "POST",
23160
+ headers: { "Content-Type": "application/json" },
23161
+ body: JSON.stringify({
23162
+ chat_id: this.chatId,
23163
+ text,
23164
+ reply_markup: isLast && keyboard ? { inline_keyboard: keyboard } : undefined,
23165
+ parse_mode: "Markdown"
23166
+ })
23167
+ });
23168
+ if (!response.ok) {
23169
+ const errorBody = await response.text().catch(() => "");
23170
+ throw new Error(`Telegram API error (${response.status}): ${errorBody || response.statusText}`);
23171
+ }
23172
+ const data = await response.json();
23173
+ if (!data.ok) {
23174
+ throw new Error(`Telegram API returned ok=false: ${JSON.stringify(data)}`);
23175
+ }
23176
+ sentIds.push(data.result.message_id);
23167
23177
  }
23168
- this.pendingMessages.set(request.id, data.result.message_id);
23178
+ this.pendingMessages.set(request.id, sentIds);
23169
23179
  } catch (err) {
23170
23180
  const msg = err instanceof Error ? err.message : String(err);
23171
23181
  throw new Error(`Failed to send Telegram message: ${msg}`);
@@ -23202,10 +23212,9 @@ class TelegramInteractionPlugin {
23202
23212
  await this.sendTimeoutMessage(requestId);
23203
23213
  this.pendingMessages.delete(requestId);
23204
23214
  }
23205
- formatMessage(request) {
23215
+ buildHeader(request) {
23206
23216
  const emoji3 = this.getStageEmoji(request.stage);
23207
23217
  let text = `${emoji3} *${request.stage.toUpperCase()}*
23208
-
23209
23218
  `;
23210
23219
  text += `*Feature:* ${request.featureName}
23211
23220
  `;
@@ -23214,11 +23223,15 @@ class TelegramInteractionPlugin {
23214
23223
  `;
23215
23224
  }
23216
23225
  text += `
23217
- ${request.summary}
23226
+ `;
23227
+ return text;
23228
+ }
23229
+ buildBody(request) {
23230
+ let text = `${this.sanitizeMarkdown(request.summary)}
23218
23231
  `;
23219
23232
  if (request.detail) {
23220
23233
  text += `
23221
- ${request.detail}
23234
+ ${this.sanitizeMarkdown(request.detail)}
23222
23235
  `;
23223
23236
  }
23224
23237
  if (request.options && request.options.length > 0) {
@@ -23226,8 +23239,8 @@ ${request.detail}
23226
23239
  *Options:*
23227
23240
  `;
23228
23241
  for (const opt of request.options) {
23229
- const desc = opt.description ? ` \u2014 ${opt.description}` : "";
23230
- text += ` \u2022 ${opt.label}${desc}
23242
+ const desc = opt.description ? ` - ${this.sanitizeMarkdown(opt.description)}` : "";
23243
+ text += ` - ${opt.label}${desc}
23231
23244
  `;
23232
23245
  }
23233
23246
  }
@@ -23238,6 +23251,30 @@ ${request.detail}
23238
23251
  }
23239
23252
  return text;
23240
23253
  }
23254
+ sanitizeMarkdown(text) {
23255
+ return text.replace(/\\(?=[_*`\[])/g, "\\\\").replace(/_/g, "\\_").replace(/`/g, "\\`").replace(/\*/g, "\\*").replace(/\[/g, "\\[");
23256
+ }
23257
+ splitText(text, maxChars) {
23258
+ if (text.length <= maxChars)
23259
+ return [text];
23260
+ const chunks = [];
23261
+ let remaining = text;
23262
+ while (remaining.length > maxChars) {
23263
+ const slice = remaining.slice(0, maxChars);
23264
+ const lastNewline = slice.lastIndexOf(`
23265
+ `);
23266
+ if (lastNewline > maxChars * 0.5) {
23267
+ chunks.push(remaining.slice(0, lastNewline));
23268
+ remaining = remaining.slice(lastNewline + 1);
23269
+ } else {
23270
+ chunks.push(slice);
23271
+ remaining = remaining.slice(maxChars);
23272
+ }
23273
+ }
23274
+ if (remaining.length > 0)
23275
+ chunks.push(remaining);
23276
+ return chunks;
23277
+ }
23241
23278
  buildKeyboard(request) {
23242
23279
  switch (request.type) {
23243
23280
  case "confirm":
@@ -23345,8 +23382,11 @@ ${request.detail}
23345
23382
  };
23346
23383
  }
23347
23384
  if (update.message?.text) {
23348
- const messageId = this.pendingMessages.get(requestId);
23349
- if (!messageId)
23385
+ const messageIds = this.pendingMessages.get(requestId);
23386
+ if (!messageIds)
23387
+ return null;
23388
+ const replyToId = update.message.reply_to_message?.message_id;
23389
+ if (replyToId !== undefined && !messageIds.includes(replyToId))
23350
23390
  return null;
23351
23391
  return {
23352
23392
  requestId,
@@ -23372,20 +23412,20 @@ ${request.detail}
23372
23412
  } catch {}
23373
23413
  }
23374
23414
  async sendTimeoutMessage(requestId) {
23375
- const messageId = this.pendingMessages.get(requestId);
23376
- if (!messageId || !this.botToken || !this.chatId) {
23415
+ const messageIds = this.pendingMessages.get(requestId);
23416
+ if (!messageIds || !this.botToken || !this.chatId) {
23377
23417
  this.pendingMessages.delete(requestId);
23378
23418
  return;
23379
23419
  }
23420
+ const lastId = messageIds[messageIds.length - 1];
23380
23421
  try {
23381
23422
  await fetch(`https://api.telegram.org/bot${this.botToken}/editMessageText`, {
23382
23423
  method: "POST",
23383
23424
  headers: { "Content-Type": "application/json" },
23384
23425
  body: JSON.stringify({
23385
23426
  chat_id: this.chatId,
23386
- message_id: messageId,
23387
- text: "\u23F1 *EXPIRED* \u2014 Interaction timed out",
23388
- parse_mode: "Markdown"
23427
+ message_id: lastId,
23428
+ text: "\u23F1 EXPIRED \u2014 Interaction timed out"
23389
23429
  })
23390
23430
  });
23391
23431
  } catch {} finally {
@@ -23393,7 +23433,7 @@ ${request.detail}
23393
23433
  }
23394
23434
  }
23395
23435
  }
23396
- var TelegramConfigSchema;
23436
+ var MAX_MESSAGE_CHARS = 4000, TelegramConfigSchema;
23397
23437
  var init_telegram = __esm(() => {
23398
23438
  init_zod();
23399
23439
  TelegramConfigSchema = exports_external.object({
@@ -34481,7 +34521,7 @@ __export(exports_parallel_executor_rectify, {
34481
34521
  });
34482
34522
  import path15 from "path";
34483
34523
  async function rectifyConflictedStory(options) {
34484
- const { storyId, workdir, config: config2, hooks, pluginRegistry, prd, eventEmitter } = options;
34524
+ const { storyId, workdir, config: config2, hooks, pluginRegistry, prd, eventEmitter, agentGetFn } = options;
34485
34525
  const logger = getSafeLogger();
34486
34526
  logger?.info("parallel", "Rectifying story on updated base", { storyId, attempt: "rectification" });
34487
34527
  try {
@@ -34513,7 +34553,8 @@ async function rectifyConflictedStory(options) {
34513
34553
  hooks,
34514
34554
  plugins: pluginRegistry,
34515
34555
  storyStartTime: new Date().toISOString(),
34516
- routing
34556
+ routing,
34557
+ agentGetFn
34517
34558
  };
34518
34559
  const pipelineResult = await runPipeline2(defaultPipeline2, pipelineContext, eventEmitter);
34519
34560
  const cost = pipelineResult.context.agentResult?.estimatedCost ?? 0;
@@ -34549,7 +34590,7 @@ var init_parallel_executor_rectify = __esm(() => {
34549
34590
  // src/execution/parallel-executor-rectification-pass.ts
34550
34591
  async function runRectificationPass(conflictedStories, options, prd, rectifyConflictedStory2) {
34551
34592
  const logger = getSafeLogger();
34552
- const { workdir, config: config2, hooks, pluginRegistry, eventEmitter } = options;
34593
+ const { workdir, config: config2, hooks, pluginRegistry, eventEmitter, agentGetFn } = options;
34553
34594
  const rectify = rectifyConflictedStory2 || (async (opts) => {
34554
34595
  const { rectifyConflictedStory: importedRectify } = await Promise.resolve().then(() => (init_parallel_executor_rectify(), exports_parallel_executor_rectify));
34555
34596
  return importedRectify(opts);
@@ -34570,7 +34611,8 @@ async function runRectificationPass(conflictedStories, options, prd, rectifyConf
34570
34611
  hooks,
34571
34612
  pluginRegistry,
34572
34613
  prd,
34573
- eventEmitter
34614
+ eventEmitter,
34615
+ agentGetFn
34574
34616
  });
34575
34617
  additionalCost += result.cost;
34576
34618
  if (result.success) {
@@ -68907,11 +68949,19 @@ function validateStory(raw, index, allIds) {
68907
68949
  ...contextFiles.length > 0 ? { contextFiles } : {}
68908
68950
  };
68909
68951
  }
68952
+ function sanitizeInvalidEscapes(text) {
68953
+ let result = text.replace(/\\x([0-9a-fA-F]{1,2})/g, (_, hex3) => `\\u00${hex3.padStart(2, "0")}`);
68954
+ result = result.replace(/\\u([0-9a-fA-F]{1,3})(?![0-9a-fA-F])/g, (_, digits) => `\\u${digits.padStart(4, "0")}`);
68955
+ result = result.replace(/\\u(?![0-9a-fA-F])/g, "\\");
68956
+ result = result.replace(/\\([^"\\\/bfnrtu])/g, "$1");
68957
+ return result;
68958
+ }
68910
68959
  function parseRawString(text) {
68911
68960
  const extracted = extractJsonFromMarkdown(text);
68912
68961
  const cleaned = stripTrailingCommas(extracted);
68962
+ const sanitized = sanitizeInvalidEscapes(cleaned);
68913
68963
  try {
68914
- return JSON.parse(cleaned);
68964
+ return JSON.parse(sanitized);
68915
68965
  } catch (err) {
68916
68966
  const parseErr = err;
68917
68967
  throw new Error(`[schema] Failed to parse JSON: ${parseErr.message}`, { cause: parseErr });
@@ -72036,7 +72086,8 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
72036
72086
  allStoryMetrics,
72037
72087
  pluginRegistry,
72038
72088
  formatterMode: options.formatterMode,
72039
- headless: options.headless
72089
+ headless: options.headless,
72090
+ agentGetFn: options.agentGetFn
72040
72091
  }, prd);
72041
72092
  prd = parallelResult.prd;
72042
72093
  totalCost = parallelResult.totalCost;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.54.3",
3
+ "version": "0.54.4",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {