@iletai/nzb 1.4.5 → 1.4.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.
@@ -215,9 +215,11 @@ async function createOrResumeSession() {
215
215
  const recentHistory = getRecentConversation(10);
216
216
  if (recentHistory) {
217
217
  console.log(`[nzb] Injecting recent conversation context into new session (non-blocking)`);
218
- session.sendAndWait({
218
+ session
219
+ .sendAndWait({
219
220
  prompt: `[System: Session recovered] Your previous session was lost. Here's the recent conversation for context — do NOT respond to these messages, just absorb the context silently:\n\n${recentHistory}\n\n(End of recovery context. Wait for the next real message.)`,
220
- }, 20_000).catch((err) => {
221
+ }, 20_000)
222
+ .catch((err) => {
221
223
  console.log(`[nzb] Context recovery injection failed (non-fatal): ${err instanceof Error ? err.message : err}`);
222
224
  });
223
225
  }
@@ -262,7 +264,12 @@ async function executeOnSession(prompt, callback, onToolEvent, onUsage) {
262
264
  const unsubToolStart = session.on("tool.execution_start", (event) => {
263
265
  const toolName = event?.data?.toolName || event?.data?.name || "tool";
264
266
  const args = event?.data?.arguments;
265
- const detail = args?.description || args?.command?.slice(0, 80) || args?.intent || args?.pattern || args?.prompt?.slice(0, 80) || undefined;
267
+ const detail = args?.description ||
268
+ args?.command?.slice(0, 80) ||
269
+ args?.intent ||
270
+ args?.pattern ||
271
+ args?.prompt?.slice(0, 80) ||
272
+ undefined;
266
273
  onToolEvent?.({ type: "tool_start", toolName, detail });
267
274
  });
268
275
  const unsubToolDone = session.on("tool.execution_complete", (event) => {
@@ -377,7 +384,15 @@ export async function sendToOrchestrator(prompt, source, callback, onToolEvent,
377
384
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
378
385
  try {
379
386
  const finalContent = await new Promise((resolve, reject) => {
380
- const item = { prompt: taggedPrompt, callback, onToolEvent, onUsage, sourceChannel, resolve, reject };
387
+ const item = {
388
+ prompt: taggedPrompt,
389
+ callback,
390
+ onToolEvent,
391
+ onUsage,
392
+ sourceChannel,
393
+ resolve,
394
+ reject,
395
+ };
381
396
  if (source.type === "background") {
382
397
  // Background results go to the back of the queue
383
398
  messageQueue.push(item);
@@ -416,7 +431,7 @@ export async function sendToOrchestrator(prompt, source, callback, onToolEvent,
416
431
  catch {
417
432
  /* best-effort */
418
433
  }
419
- callback(finalContent, true, { assistantLogId });
434
+ await callback(finalContent, true, { assistantLogId });
420
435
  // Auto-continue: if the response was cut short by timeout, automatically
421
436
  // send a follow-up "Continue" message so the user doesn't have to
422
437
  if (finalContent.includes("⏱ Response was cut short (timeout)")) {
@@ -454,7 +469,7 @@ export async function sendToOrchestrator(prompt, source, callback, onToolEvent,
454
469
  continue;
455
470
  }
456
471
  console.error(`[nzb] Error processing message: ${msg}`);
457
- callback(`Error: ${msg}`, true);
472
+ await callback(`Error: ${msg}`, true);
458
473
  return;
459
474
  }
460
475
  }
package/dist/store/db.js CHANGED
@@ -136,7 +136,8 @@ export function getConversationContext(telegramMsgId) {
136
136
  if (!row)
137
137
  return undefined;
138
138
  // Fetch 4 rows before + the target + 4 rows after (handles ID gaps from pruning)
139
- const rows = db.prepare(`
139
+ const rows = db
140
+ .prepare(`
140
141
  SELECT role, content, source, ts FROM (
141
142
  SELECT * FROM conversation_log WHERE id < ? ORDER BY id DESC LIMIT 4
142
143
  )
@@ -146,7 +147,8 @@ export function getConversationContext(telegramMsgId) {
146
147
  SELECT role, content, source, ts FROM (
147
148
  SELECT * FROM conversation_log WHERE id > ? ORDER BY id ASC LIMIT 4
148
149
  )
149
- `).all(row.id, row.id, row.id);
150
+ `)
151
+ .all(row.id, row.id, row.id);
150
152
  if (rows.length === 0)
151
153
  return undefined;
152
154
  return rows
@@ -162,8 +162,11 @@ export function createBot() {
162
162
  registerCallbackHandlers(bot);
163
163
  // Persistent reply keyboard — quick actions always visible below chat input
164
164
  const replyKeyboard = new Keyboard()
165
- .text("📊 Status").text("❌ Cancel").row()
166
- .text("🧠 Memory").text("🔄 Restart")
165
+ .text("📊 Status")
166
+ .text(" Cancel")
167
+ .row()
168
+ .text("🧠 Memory")
169
+ .text("🔄 Restart")
167
170
  .resized()
168
171
  .persistent();
169
172
  // /start and /help — with inline menu + reply keyboard
@@ -305,7 +308,9 @@ export function createBot() {
305
308
  });
306
309
  bot.hears("🔄 Restart", async (ctx) => {
307
310
  await ctx.reply("Restarting NZB...");
308
- setTimeout(() => { restartDaemon().catch(console.error); }, 500);
311
+ setTimeout(() => {
312
+ restartDaemon().catch(console.error);
313
+ }, 500);
309
314
  });
310
315
  // Handle all text messages — progressive streaming with tool event feedback
311
316
  bot.on("message:text", async (ctx) => {
@@ -318,7 +323,9 @@ export function createBot() {
318
323
  try {
319
324
  await ctx.react("👀");
320
325
  }
321
- catch { /* reactions may not be available */ }
326
+ catch {
327
+ /* reactions may not be available */
328
+ }
322
329
  // Typing indicator — keeps sending "typing" action every 4s until the final
323
330
  // response is delivered. We use bot.api directly for reliability, and await the
324
331
  // first call so the user sees typing immediately before any async work begins.
@@ -473,8 +480,8 @@ export function createBot() {
473
480
  const assistantLogId = meta?.assistantLogId;
474
481
  const elapsed = ((Date.now() - handlerStartTime) / 1000).toFixed(1);
475
482
  void logInfo(`✅ Response done (${elapsed}s, ${toolHistory.length} tools, ${text.length} chars)`);
476
- // Wait for in-flight edits to finish before sending the final response
477
- void editChain.then(async () => {
483
+ // Return the edit chain so callers can await final delivery
484
+ return editChain.then(async () => {
478
485
  // Format error messages with a distinct visual
479
486
  const isError = text.startsWith("Error:");
480
487
  if (isError) {
@@ -500,7 +507,7 @@ export function createBot() {
500
507
  }
501
508
  let textWithMeta = text;
502
509
  if (usageInfo) {
503
- const fmtTokens = (n) => n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n);
510
+ const fmtTokens = (n) => (n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n));
504
511
  const parts = [];
505
512
  if (usageInfo.model)
506
513
  parts.push(usageInfo.model);
@@ -620,7 +627,9 @@ export function createBot() {
620
627
  try {
621
628
  await bot.api.setMessageReaction(chatId, userMessageId, [{ type: "emoji", emoji: "👍" }]);
622
629
  }
623
- catch { /* reactions may not be available */ }
630
+ catch {
631
+ /* reactions may not be available */
632
+ }
624
633
  });
625
634
  }
626
635
  else {
@@ -169,7 +169,10 @@ export function registerMediaHandlers(bot) {
169
169
  }
170
170
  sendToOrchestrator(voiceReplyContext + prompt, { type: "telegram", chatId, messageId: userMessageId }, (text, done, meta) => {
171
171
  if (done)
172
- void sendFormattedReply(bot, chatId, text, { replyTo: userMessageId, assistantLogId: meta?.assistantLogId });
172
+ void sendFormattedReply(bot, chatId, text, {
173
+ replyTo: userMessageId,
174
+ assistantLogId: meta?.assistantLogId,
175
+ });
173
176
  });
174
177
  }
175
178
  catch (err) {
package/package.json CHANGED
@@ -1,69 +1,70 @@
1
1
  {
2
- "name": "@iletai/nzb",
3
- "version": "1.4.5",
4
- "description": "NZB — a personal AI assistant for developers, built on the GitHub Copilot SDK",
5
- "bin": {
6
- "nzb": "dist/cli.js"
7
- },
8
- "publishConfig": {
9
- "access": "public"
10
- },
11
- "files": [
12
- "dist/**/*.js",
13
- "scripts/fix-esm-imports.cjs",
14
- "skills/",
15
- "README.md"
16
- ],
17
- "scripts": {
18
- "build": "tsc",
19
- "test": "vitest run",
20
- "test:watch": "vitest",
21
- "postinstall": "node scripts/fix-esm-imports.cjs",
22
- "daemon": "tsx src/daemon.ts",
23
- "tui": "tsx src/tui/index.ts",
24
- "dev": "tsx --watch src/cli.ts start",
25
- "format": "prettier --write .",
26
- "format:check": "prettier --check .",
27
- "prepublishOnly": "npm run build"
28
- },
29
- "engines": {
30
- "node": ">=18"
31
- },
32
- "keywords": [
33
- "copilot",
34
- "telegram",
35
- "orchestrator",
36
- "ai",
37
- "cli"
38
- ],
39
- "author": "iletai",
40
- "license": "MIT",
41
- "repository": {
42
- "type": "git",
43
- "url": "git+https://github.com/iletai/AI-Agent-Assistant.git"
44
- },
45
- "homepage": "https://github.com/iletai/AI-Agent-Assistant#readme",
46
- "bugs": {
47
- "url": "https://github.com/iletai/AI-Agent-Assistant/issues"
48
- },
49
- "type": "module",
50
- "dependencies": {
51
- "@github/copilot-sdk": "^0.1.26",
52
- "@grammyjs/auto-retry": "^2.0.2",
53
- "@grammyjs/menu": "^1.3.1",
54
- "better-sqlite3": "^12.6.2",
55
- "dotenv": "^17.3.1",
56
- "express": "^5.2.1",
57
- "grammy": "^1.40.0",
58
- "zod": "^4.3.6"
59
- },
60
- "devDependencies": {
61
- "@types/better-sqlite3": "^7.6.13",
62
- "@types/express": "^5.0.6",
63
- "@types/node": "^25.3.0",
64
- "prettier": "^3.8.1",
65
- "tsx": "^4.21.0",
66
- "typescript": "^5.9.3",
67
- "vitest": "^4.1.0"
68
- }
2
+ "name": "@iletai/nzb",
3
+ "version": "1.4.6",
4
+ "description": "NZB — a personal AI assistant for developers, built on the GitHub Copilot SDK",
5
+ "bin": {
6
+ "nzb": "dist/cli.js"
7
+ },
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "files": [
12
+ "dist/**/*.js",
13
+ "scripts/fix-esm-imports.cjs",
14
+ "skills/",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "postinstall": "node scripts/fix-esm-imports.cjs",
22
+ "daemon": "tsx src/daemon.ts",
23
+ "tui": "tsx src/tui/index.ts",
24
+ "dev": "tsx --watch src/cli.ts start",
25
+ "format": "prettier --write .",
26
+ "format:check": "prettier --check .",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "keywords": [
33
+ "copilot",
34
+ "telegram",
35
+ "orchestrator",
36
+ "ai",
37
+ "cli"
38
+ ],
39
+ "author": "iletai",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/iletai/AI-Agent-Assistant.git"
44
+ },
45
+ "homepage": "https://github.com/iletai/AI-Agent-Assistant#readme",
46
+ "bugs": {
47
+ "url": "https://github.com/iletai/AI-Agent-Assistant/issues"
48
+ },
49
+ "type": "module",
50
+ "dependencies": {
51
+ "@github/copilot-sdk": "^0.1.26",
52
+ "@grammyjs/auto-retry": "^2.0.2",
53
+ "@grammyjs/menu": "^1.3.1",
54
+ "better-sqlite3": "^12.6.2",
55
+ "dotenv": "^17.3.1",
56
+ "express": "^5.2.1",
57
+ "grammy": "^1.40.0",
58
+ "zod": "^4.3.6"
59
+ },
60
+ "devDependencies": {
61
+ "@types/better-sqlite3": "^7.6.13",
62
+ "@types/express": "^5.0.6",
63
+ "@types/node": "^25.3.0",
64
+ "@vitest/coverage-v8": "^4.1.0",
65
+ "prettier": "^3.8.1",
66
+ "tsx": "^4.21.0",
67
+ "typescript": "^5.9.3",
68
+ "vitest": "^4.1.0"
69
+ }
69
70
  }