arisa 4.0.0 → 4.0.2
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/package.json
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createPiRuntime, hasProviderAuth, supportsProviderOAuth } from "./pi-runtime.js";
|
|
2
|
+
|
|
3
|
+
const authInvalidatedPatterns = [
|
|
4
|
+
/authentication token has been invalidated/i,
|
|
5
|
+
/token (?:has been )?invalidated/i,
|
|
6
|
+
/try signing in again/i,
|
|
7
|
+
/auth(?:entication)? token (?:expired|revoked|invalid)/i
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const missingAuthPatterns = [
|
|
11
|
+
/no auth found/i,
|
|
12
|
+
/auth(?:entication)? .*missing/i
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export function getErrorMessage(error) {
|
|
16
|
+
return error instanceof Error ? error.message : String(error);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getPiAuthIssue(error) {
|
|
20
|
+
const message = getErrorMessage(error);
|
|
21
|
+
if (!message) return null;
|
|
22
|
+
|
|
23
|
+
if (authInvalidatedPatterns.some((pattern) => pattern.test(message))) {
|
|
24
|
+
return { kind: "invalidated-token", message };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (missingAuthPatterns.some((pattern) => pattern.test(message))) {
|
|
28
|
+
return { kind: "missing-auth", message };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getPiAuthStatus(config) {
|
|
35
|
+
const runtime = createPiRuntime({
|
|
36
|
+
provider: config.pi.provider,
|
|
37
|
+
apiKey: config.pi.apiKey
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
provider: config.pi.provider,
|
|
42
|
+
model: config.pi.model,
|
|
43
|
+
hasApiKey: Boolean(config.pi.apiKey),
|
|
44
|
+
hasStoredAuth: hasProviderAuth(config.pi.provider, runtime),
|
|
45
|
+
supportsOAuth: supportsProviderOAuth(config.pi.provider, runtime)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function buildPiAuthTelegramMessage({ config, issue = null }) {
|
|
50
|
+
const status = getPiAuthStatus(config);
|
|
51
|
+
const lines = [
|
|
52
|
+
issue
|
|
53
|
+
? `Pi authentication needs attention for ${status.provider}/${status.model}.`
|
|
54
|
+
: `Pi authentication status for ${status.provider}/${status.model}.`
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
if (issue?.kind === "invalidated-token") {
|
|
58
|
+
lines.push("The provider says the current authentication token was invalidated.");
|
|
59
|
+
} else if (issue?.kind === "missing-auth") {
|
|
60
|
+
lines.push("Arisa could not find usable authentication for this provider.");
|
|
61
|
+
} else {
|
|
62
|
+
lines.push(`Stored auth: ${status.hasStoredAuth ? "detected" : "not detected"}.`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (issue?.message) {
|
|
66
|
+
lines.push(`Details: ${issue.message}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (status.hasApiKey) {
|
|
70
|
+
lines.push("A Pi API key is configured, but the provider rejected the current request. Update the key and restart Arisa.");
|
|
71
|
+
} else if (status.supportsOAuth) {
|
|
72
|
+
lines.push("For now, re-run `arisa --bootstrap` on the host and complete Pi login again.");
|
|
73
|
+
} else {
|
|
74
|
+
lines.push("This provider needs a Pi API key. Re-run `arisa --bootstrap`, provide a key, and restart Arisa.");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
lines.push("Telegram-based renewal is not wired yet, but this /auth path is ready for that flow.");
|
|
78
|
+
return lines.join("\n");
|
|
79
|
+
}
|
|
@@ -64,7 +64,12 @@ export async function createApp({ logger, runtimeOverrides, webhookUrl, setHttpR
|
|
|
64
64
|
return {
|
|
65
65
|
async start() {
|
|
66
66
|
logger?.log("app", `validating Pi model ${config.pi.provider}/${config.pi.model}`);
|
|
67
|
-
|
|
67
|
+
try {
|
|
68
|
+
await agentManager.validatePiAgent();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
await bot.notifyPiAuthIssue?.(error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
68
73
|
await toolProcessSupervisor.start();
|
|
69
74
|
logger?.log("app", "starting Telegram bot");
|
|
70
75
|
try {
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { authorizeChat } from "./auth.js";
|
|
4
4
|
import { captureIncomingArtifact } from "./media.js";
|
|
5
5
|
import { renderTelegramHtml } from "./text-format.js";
|
|
6
|
+
import { buildPiAuthTelegramMessage, getErrorMessage, getPiAuthIssue } from "../../core/agent/auth-flow.js";
|
|
6
7
|
import { normalizeArtifactForReasoning, shouldNormalizeArtifactToText } from "../../core/artifacts/normalize-for-reasoning.js";
|
|
7
8
|
|
|
8
9
|
const slowPromptNoticeMs = 300_000;
|
|
@@ -185,6 +186,7 @@ function sessionEventLogMessage(event) {
|
|
|
185
186
|
|
|
186
187
|
async function collectText(session, prompt, { logger, chatId, onSlowPrompt } = {}) {
|
|
187
188
|
let text = "";
|
|
189
|
+
let assistantErrorMessage = "";
|
|
188
190
|
let shouldSeparateAssistantMessage = false;
|
|
189
191
|
let slowPromptTimer = null;
|
|
190
192
|
const unsubscribe = session.subscribe((event) => {
|
|
@@ -198,6 +200,9 @@ async function collectText(session, prompt, { logger, chatId, onSlowPrompt } = {
|
|
|
198
200
|
}
|
|
199
201
|
text += event.assistantMessageEvent.delta;
|
|
200
202
|
}
|
|
203
|
+
if (event.type === "message_end" && event.message?.stopReason === "error") {
|
|
204
|
+
assistantErrorMessage = event.message.errorMessage || "assistant message ended with error";
|
|
205
|
+
}
|
|
201
206
|
const logMessage = sessionEventLogMessage(event);
|
|
202
207
|
if (logMessage) logger?.log("agent", `chat ${chatId} ${logMessage}`);
|
|
203
208
|
});
|
|
@@ -218,6 +223,10 @@ async function collectText(session, prompt, { logger, chatId, onSlowPrompt } = {
|
|
|
218
223
|
unsubscribe();
|
|
219
224
|
}
|
|
220
225
|
|
|
226
|
+
if (assistantErrorMessage) {
|
|
227
|
+
throw new Error(assistantErrorMessage);
|
|
228
|
+
}
|
|
229
|
+
|
|
221
230
|
return text.trim();
|
|
222
231
|
}
|
|
223
232
|
|
|
@@ -237,8 +246,31 @@ async function withTyping(ctx, work) {
|
|
|
237
246
|
export async function createTelegramBot({ config, artifactStore, toolRegistry, taskStore, agentManager, saveConfig, updateConfig, logger, webhookUrl, setHttpRequestHandler }) {
|
|
238
247
|
const bot = new Bot(config.telegram.token);
|
|
239
248
|
const perChatState = new Map();
|
|
249
|
+
const notifiedPromptErrors = new WeakSet();
|
|
240
250
|
let taskTimer = null;
|
|
241
251
|
|
|
252
|
+
function wasPromptErrorNotified(error) {
|
|
253
|
+
return error instanceof Error && notifiedPromptErrors.has(error);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function markPromptErrorNotified(error) {
|
|
257
|
+
if (error instanceof Error) notifiedPromptErrors.add(error);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function notifyPiAuthIssueIfNeeded(chatId, error) {
|
|
261
|
+
const issue = getPiAuthIssue(error);
|
|
262
|
+
if (!issue) return false;
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
await bot.api.sendMessage(chatId, buildPiAuthTelegramMessage({ config, issue }));
|
|
266
|
+
markPromptErrorNotified(error);
|
|
267
|
+
return true;
|
|
268
|
+
} catch (notifyError) {
|
|
269
|
+
logger?.error("telegram", `auth issue notice failed for chat ${chatId}: ${getErrorMessage(notifyError)}`);
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
242
274
|
function getIncomingChatMeta(ctx) {
|
|
243
275
|
return {
|
|
244
276
|
languageCode: ctx.from?.language_code || "",
|
|
@@ -358,8 +390,9 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
358
390
|
logger?.log("telegram", `prompt dispatch for chat ${chatId}`);
|
|
359
391
|
await processPromptForChat({ chatId, prompt: currentPrompt, ctx: currentCtx });
|
|
360
392
|
} catch (error) {
|
|
361
|
-
const message =
|
|
393
|
+
const message = getErrorMessage(error);
|
|
362
394
|
logger?.error("telegram", `${label} failed for chat ${chatId}: ${message}`);
|
|
395
|
+
await notifyPiAuthIssueIfNeeded(chatId, error);
|
|
363
396
|
throw error;
|
|
364
397
|
} finally {
|
|
365
398
|
currentCtx = null;
|
|
@@ -493,6 +526,12 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
493
526
|
await handleNewCommand(ctx);
|
|
494
527
|
});
|
|
495
528
|
|
|
529
|
+
bot.command("auth", async (ctx) => {
|
|
530
|
+
const auth = await authorizeChat({ config, chatId: ctx.chat.id, saveConfig, chatMeta: getIncomingChatMeta(ctx) });
|
|
531
|
+
if (!auth.ok) return;
|
|
532
|
+
await ctx.reply(buildPiAuthTelegramMessage({ config }));
|
|
533
|
+
});
|
|
534
|
+
|
|
496
535
|
bot.on("message", async (ctx) => {
|
|
497
536
|
const auth = await authorizeChat({ config, chatId: ctx.chat.id, saveConfig, chatMeta: getIncomingChatMeta(ctx) });
|
|
498
537
|
if (!auth.ok) return;
|
|
@@ -505,8 +544,11 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
505
544
|
} catch (error) {
|
|
506
545
|
const chatState = getChatState(ctx.chat.id);
|
|
507
546
|
chatState.processing = false;
|
|
508
|
-
|
|
509
|
-
|
|
547
|
+
if (wasPromptErrorNotified(error)) return;
|
|
548
|
+
const issue = getPiAuthIssue(error);
|
|
549
|
+
await ctx.reply(issue
|
|
550
|
+
? buildPiAuthTelegramMessage({ config, issue })
|
|
551
|
+
: getErrorMessage(error));
|
|
510
552
|
}
|
|
511
553
|
});
|
|
512
554
|
|
|
@@ -534,7 +576,8 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
534
576
|
}
|
|
535
577
|
}
|
|
536
578
|
await bot.api.setMyCommands([
|
|
537
|
-
{ command: "new", description: "Start a new chat context" }
|
|
579
|
+
{ command: "new", description: "Start a new chat context" },
|
|
580
|
+
{ command: "auth", description: "Show Pi authentication status" }
|
|
538
581
|
]);
|
|
539
582
|
if (!taskTimer) {
|
|
540
583
|
taskTimer = setInterval(() => {
|
|
@@ -572,6 +615,14 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
572
615
|
try {
|
|
573
616
|
bot.stop();
|
|
574
617
|
} catch {}
|
|
618
|
+
},
|
|
619
|
+
|
|
620
|
+
async notifyPiAuthIssue(error) {
|
|
621
|
+
let notified = false;
|
|
622
|
+
for (const chatId of config.telegram.authorizedChatIds || []) {
|
|
623
|
+
notified = await notifyPiAuthIssueIfNeeded(chatId, error) || notified;
|
|
624
|
+
}
|
|
625
|
+
return notified;
|
|
575
626
|
}
|
|
576
627
|
};
|
|
577
628
|
}
|