@iletai/nzb 1.4.5 → 1.4.7
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.
- package/dist/copilot/orchestrator.js +27 -18
- package/dist/store/db.js +4 -2
- package/dist/telegram/bot.js +17 -8
- package/dist/telegram/handlers/media.js +4 -1
- package/package.json +68 -67
|
@@ -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
|
|
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)
|
|
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 ||
|
|
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) => {
|
|
@@ -355,7 +362,8 @@ function isRecoverableError(err) {
|
|
|
355
362
|
const msg = err instanceof Error ? err.message : String(err);
|
|
356
363
|
return /timeout|disconnect|connection|EPIPE|ECONNRESET|ECONNREFUSED|socket|closed|ENOENT|spawn|not found|expired|stale/i.test(msg);
|
|
357
364
|
}
|
|
358
|
-
|
|
365
|
+
const MAX_AUTO_CONTINUE = 3;
|
|
366
|
+
export async function sendToOrchestrator(prompt, source, callback, onToolEvent, onUsage, _autoContinueCount = 0) {
|
|
359
367
|
const sourceLabel = source.type === "telegram" ? "telegram" : source.type === "tui" ? "tui" : "background";
|
|
360
368
|
logMessage("in", sourceLabel, prompt);
|
|
361
369
|
// Tag the prompt with its source channel
|
|
@@ -377,7 +385,15 @@ export async function sendToOrchestrator(prompt, source, callback, onToolEvent,
|
|
|
377
385
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
378
386
|
try {
|
|
379
387
|
const finalContent = await new Promise((resolve, reject) => {
|
|
380
|
-
const item = {
|
|
388
|
+
const item = {
|
|
389
|
+
prompt: taggedPrompt,
|
|
390
|
+
callback,
|
|
391
|
+
onToolEvent,
|
|
392
|
+
onUsage,
|
|
393
|
+
sourceChannel,
|
|
394
|
+
resolve,
|
|
395
|
+
reject,
|
|
396
|
+
};
|
|
381
397
|
if (source.type === "background") {
|
|
382
398
|
// Background results go to the back of the queue
|
|
383
399
|
messageQueue.push(item);
|
|
@@ -416,21 +432,14 @@ export async function sendToOrchestrator(prompt, source, callback, onToolEvent,
|
|
|
416
432
|
catch {
|
|
417
433
|
/* best-effort */
|
|
418
434
|
}
|
|
419
|
-
callback(finalContent, true, { assistantLogId });
|
|
435
|
+
await callback(finalContent, true, { assistantLogId });
|
|
420
436
|
// Auto-continue: if the response was cut short by timeout, automatically
|
|
421
437
|
// send a follow-up "Continue" message so the user doesn't have to
|
|
422
|
-
if (finalContent.includes("⏱ Response was cut short (timeout)")
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (source.type === "telegram") {
|
|
426
|
-
try {
|
|
427
|
-
const { sendProactiveMessage } = await import("../telegram/bot.js");
|
|
428
|
-
await sendProactiveMessage("🔄 Auto-continuing...");
|
|
429
|
-
}
|
|
430
|
-
catch { }
|
|
431
|
-
}
|
|
438
|
+
if (finalContent.includes("⏱ Response was cut short (timeout)") &&
|
|
439
|
+
_autoContinueCount < MAX_AUTO_CONTINUE) {
|
|
440
|
+
console.log(`[nzb] Auto-continuing after timeout (${_autoContinueCount + 1}/${MAX_AUTO_CONTINUE})…`);
|
|
432
441
|
await sleep(1000);
|
|
433
|
-
void sendToOrchestrator("Continue from where you left off. Do not repeat what was already said.", source, callback, onToolEvent, onUsage);
|
|
442
|
+
void sendToOrchestrator("Continue from where you left off. Do not repeat what was already said.", source, callback, onToolEvent, onUsage, _autoContinueCount + 1);
|
|
434
443
|
}
|
|
435
444
|
return;
|
|
436
445
|
}
|
|
@@ -454,7 +463,7 @@ export async function sendToOrchestrator(prompt, source, callback, onToolEvent,
|
|
|
454
463
|
continue;
|
|
455
464
|
}
|
|
456
465
|
console.error(`[nzb] Error processing message: ${msg}`);
|
|
457
|
-
callback(`Error: ${msg}`, true);
|
|
466
|
+
await callback(`Error: ${msg}`, true);
|
|
458
467
|
return;
|
|
459
468
|
}
|
|
460
469
|
}
|
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
|
|
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
|
-
`)
|
|
150
|
+
`)
|
|
151
|
+
.all(row.id, row.id, row.id);
|
|
150
152
|
if (rows.length === 0)
|
|
151
153
|
return undefined;
|
|
152
154
|
return rows
|
package/dist/telegram/bot.js
CHANGED
|
@@ -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")
|
|
166
|
-
.text("
|
|
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(() => {
|
|
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 {
|
|
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
|
-
//
|
|
477
|
-
|
|
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 {
|
|
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, {
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
2
|
+
"name": "@iletai/nzb",
|
|
3
|
+
"version": "1.4.7",
|
|
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
|
}
|