arisa 4.3.5 → 5.1.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/AGENTS.md +21 -19
- package/README.md +30 -9
- package/package.json +6 -2
- package/pnpm-workspace.yaml +1 -0
- package/src/core/agent/agent-manager.js +288 -29
- package/src/core/agent/auth-flow.js +12 -8
- package/src/core/agent/model-selection.js +54 -14
- package/src/core/agent/model-speed.js +59 -0
- package/src/core/config/config-defaults.js +56 -4
- package/src/core/config/config-store.js +5 -1
- package/src/core/conversation/conversation-history-store.js +142 -0
- package/src/core/tasks/task-store.js +16 -0
- package/src/core/tools/daemon-health.js +11 -2
- package/src/core/tools/daemon-processes.js +92 -2
- package/src/core/tools/daemon-runtime.js +4 -2
- package/src/core/tools/ipc-client.js +15 -3
- package/src/core/tools/tool-registry.js +42 -2
- package/src/core/tools/tool-usage-store.js +59 -0
- package/src/index.js +61 -6
- package/src/runtime/arisa-capabilities.js +45 -1
- package/src/runtime/bootstrap.js +3 -2
- package/src/runtime/create-app.js +49 -11
- package/src/runtime/doctor.js +365 -0
- package/src/runtime/log-viewer.js +165 -0
- package/src/runtime/paths.js +8 -1
- package/src/runtime/report-format.js +51 -0
- package/src/runtime/service-manager.js +106 -8
- package/src/runtime/tool-process-supervisor.js +107 -10
- package/src/runtime/tool-usage-report.js +10 -0
- package/src/runtime/update-manager.js +206 -0
- package/src/transport/telegram/bot.js +633 -91
- package/src/transport/telegram/model-picker.js +28 -2
- package/test/agent-tool-policy.test.js +26 -1
- package/test/auth-flow.test.js +28 -2
- package/test/capabilities-security.test.js +37 -0
- package/test/context-and-task-bounds.test.js +280 -0
- package/test/daemon-runtime.test.js +130 -2
- package/test/dependency-warnings.test.js +17 -0
- package/test/doctor.test.js +111 -0
- package/test/log-viewer.test.js +90 -0
- package/test/model-selection.test.js +125 -2
- package/test/paths.test.js +16 -0
- package/test/pi-compaction.test.js +43 -0
- package/test/service-manager.test.js +237 -0
- package/test/task-store.test.js +31 -0
- package/test/telegram-text-artifact.test.js +36 -1
- package/test/tool-registry-run.test.js +21 -0
- package/test/tool-usage.test.js +37 -0
- package/test/update-manager.test.js +87 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getChatToolUsageFile } from "../../runtime/paths.js";
|
|
4
|
+
|
|
5
|
+
function emptyUsage() {
|
|
6
|
+
return { version: 1, tools: {} };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async function readUsage(file) {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(await readFile(file, "utf8"));
|
|
12
|
+
return parsed?.version === 1 && parsed.tools && typeof parsed.tools === "object"
|
|
13
|
+
? parsed
|
|
14
|
+
: emptyUsage();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error?.code === "ENOENT") return emptyUsage();
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function writeUsage(file, usage) {
|
|
22
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
23
|
+
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
24
|
+
await writeFile(temporary, `${JSON.stringify(usage, null, 2)}\n`, "utf8");
|
|
25
|
+
await rename(temporary, file);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class ToolUsageStore {
|
|
29
|
+
constructor({ resolveFile = getChatToolUsageFile } = {}) {
|
|
30
|
+
this.resolveFile = resolveFile;
|
|
31
|
+
this.queues = new Map();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async record(chatId, toolName) {
|
|
35
|
+
if (chatId == null || chatId === "") return;
|
|
36
|
+
const key = String(chatId);
|
|
37
|
+
const previous = this.queues.get(key) || Promise.resolve();
|
|
38
|
+
const current = previous.catch(() => {}).then(async () => {
|
|
39
|
+
const file = this.resolveFile(chatId);
|
|
40
|
+
const usage = await readUsage(file);
|
|
41
|
+
const count = Number(usage.tools[toolName]?.count) || 0;
|
|
42
|
+
usage.tools[toolName] = { count: count + 1 };
|
|
43
|
+
await writeUsage(file, usage);
|
|
44
|
+
});
|
|
45
|
+
this.queues.set(key, current);
|
|
46
|
+
try {
|
|
47
|
+
await current;
|
|
48
|
+
} finally {
|
|
49
|
+
if (this.queues.get(key) === current) this.queues.delete(key);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async counts(chatId) {
|
|
54
|
+
if (chatId == null || chatId === "") return {};
|
|
55
|
+
await (this.queues.get(String(chatId)) || Promise.resolve()).catch(() => {});
|
|
56
|
+
const usage = await readUsage(this.resolveFile(chatId));
|
|
57
|
+
return Object.fromEntries(Object.entries(usage.tools).map(([name, value]) => [name, Number(value?.count) || 0]));
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { bootstrapIfNeeded } from "./runtime/bootstrap.js";
|
|
4
|
-
import { createApp } from "./runtime/create-app.js";
|
|
4
|
+
import { applyRuntimeOverrides, createApp } from "./runtime/create-app.js";
|
|
5
|
+
import { loadConfig } from "./core/config/config-store.js";
|
|
5
6
|
import { createLogger } from "./runtime/logger.js";
|
|
6
|
-
import { getServiceStatus, registerServiceProcess, startService, stopService, unregisterServiceProcess } from "./runtime/service-manager.js";
|
|
7
|
+
import { getServiceStatus, handoffServiceRestart, registerServiceProcess, restartService, startService, stopService, unregisterServiceProcess } from "./runtime/service-manager.js";
|
|
7
8
|
import { flushArisaHome } from "./runtime/flush.js";
|
|
9
|
+
import { readPackageVersion, showServiceLogs } from "./runtime/log-viewer.js";
|
|
8
10
|
import { arisaPackageDir } from "./runtime/paths.js";
|
|
9
11
|
|
|
10
12
|
process.env.ARISA_PACKAGE_DIR = arisaPackageDir;
|
|
@@ -69,17 +71,18 @@ function toNestedOverrides(nestedFlags) {
|
|
|
69
71
|
|
|
70
72
|
function toServiceRunnerArgs(nestedFlags) {
|
|
71
73
|
const args = [];
|
|
72
|
-
const
|
|
74
|
+
const serviceSafeAgentFlags = [
|
|
73
75
|
"pi.provider",
|
|
74
76
|
"pi.model",
|
|
75
77
|
"pi.workspaceDir",
|
|
76
78
|
"pi.tools",
|
|
77
79
|
"pi.excludeTools",
|
|
78
80
|
"pi.shellPath",
|
|
79
|
-
"pi.shellTimeoutMs"
|
|
81
|
+
"pi.shellTimeoutMs",
|
|
82
|
+
"pi.speed"
|
|
80
83
|
];
|
|
81
84
|
|
|
82
|
-
for (const flag of
|
|
85
|
+
for (const flag of serviceSafeAgentFlags) {
|
|
83
86
|
if (nestedFlags[flag]) {
|
|
84
87
|
args.push(`--${flag}`, nestedFlags[flag]);
|
|
85
88
|
}
|
|
@@ -111,12 +114,21 @@ process.once("SIGINT", () => {
|
|
|
111
114
|
});
|
|
112
115
|
|
|
113
116
|
async function startRuntimeApp() {
|
|
114
|
-
const app = await createApp({
|
|
117
|
+
const app = await createApp({
|
|
118
|
+
logger,
|
|
119
|
+
runtimeOverrides,
|
|
120
|
+
requestRestart: () => handoffServiceRestart({
|
|
121
|
+
verbose,
|
|
122
|
+
cliArgs: toServiceRunnerArgs(cli.nestedFlags)
|
|
123
|
+
})
|
|
124
|
+
});
|
|
115
125
|
activeApp = app;
|
|
116
126
|
await app.start();
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
async function startBackgroundService() {
|
|
130
|
+
const persistedConfig = await loadConfig();
|
|
131
|
+
applyRuntimeOverrides(persistedConfig, runtimeOverrides);
|
|
120
132
|
const result = await startService({ verbose, cliArgs: toServiceRunnerArgs(cli.nestedFlags) });
|
|
121
133
|
if (!result.ok) {
|
|
122
134
|
console.log(`Arisa is already running in background (pid ${result.pid}).`);
|
|
@@ -127,6 +139,31 @@ async function startBackgroundService() {
|
|
|
127
139
|
return result;
|
|
128
140
|
}
|
|
129
141
|
|
|
142
|
+
async function restartBackgroundService() {
|
|
143
|
+
const persistedConfig = await loadConfig();
|
|
144
|
+
applyRuntimeOverrides(persistedConfig, runtimeOverrides);
|
|
145
|
+
const result = await restartService({
|
|
146
|
+
verbose,
|
|
147
|
+
cliArgs: toServiceRunnerArgs(cli.nestedFlags),
|
|
148
|
+
shutdownTimeoutMs: persistedConfig.service.shutdownTimeoutMs,
|
|
149
|
+
shutdownPollIntervalMs: persistedConfig.service.shutdownPollIntervalMs
|
|
150
|
+
});
|
|
151
|
+
if (!result.ok) {
|
|
152
|
+
if (result.reason === "already-running") {
|
|
153
|
+
console.log(`Arisa could not be restarted because another instance is running (pid ${result.pid}).`);
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`Arisa could not be restarted: ${result.reason || "unknown service error"}`);
|
|
157
|
+
}
|
|
158
|
+
if (result.wasRunning) {
|
|
159
|
+
console.log(`Arisa restarted in background (pid ${result.pid}; previous pid ${result.previousPid}).`);
|
|
160
|
+
} else {
|
|
161
|
+
console.log(`Arisa started in background (pid ${result.pid}); it was not running.`);
|
|
162
|
+
}
|
|
163
|
+
console.log(`Log file: ${result.logFile}`);
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
130
167
|
async function runForeground() {
|
|
131
168
|
const hasRuntimePiOverrides = Boolean(
|
|
132
169
|
runtimeOverrides?.pi?.model
|
|
@@ -197,6 +234,16 @@ async function main() {
|
|
|
197
234
|
return;
|
|
198
235
|
}
|
|
199
236
|
|
|
237
|
+
if (command === "restart") {
|
|
238
|
+
const bootstrapResult = await bootstrapIfNeeded({ force: forceBootstrap });
|
|
239
|
+
if (bootstrapResult.configCreated && bootstrapResult.viaTelegram && !bootstrapResult.startInBackground) {
|
|
240
|
+
console.log("Config saved. Arisa was not started in background.");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
await restartBackgroundService();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
200
247
|
if (command === "status") {
|
|
201
248
|
const status = await getServiceStatus();
|
|
202
249
|
if (!status.running) {
|
|
@@ -207,6 +254,14 @@ async function main() {
|
|
|
207
254
|
return;
|
|
208
255
|
}
|
|
209
256
|
|
|
257
|
+
if (command === "log") {
|
|
258
|
+
await showServiceLogs({
|
|
259
|
+
version: await readPackageVersion(),
|
|
260
|
+
follow: !cli.flags["no-follow"]
|
|
261
|
+
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
210
265
|
if (command === "flush") {
|
|
211
266
|
const status = await getServiceStatus();
|
|
212
267
|
if (status.running) {
|
|
@@ -41,10 +41,35 @@ function normalizeLimit(limit) {
|
|
|
41
41
|
return Math.min(value, 100);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export function createArisaCapabilities({ artifactStore, taskStore, agentManager } = {}) {
|
|
44
|
+
export function createArisaCapabilities({ artifactStore, taskStore, toolRegistry, agentManager } = {}) {
|
|
45
45
|
async function dispatch({ method, toolName, chatId = null, params = {} } = {}) {
|
|
46
46
|
const scopedToolName = requireToolName(toolName);
|
|
47
47
|
|
|
48
|
+
if (method === "tools.list") {
|
|
49
|
+
await toolRegistry.load();
|
|
50
|
+
return toolRegistry.listWithRuntime(chatId);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (method === "tools.help") {
|
|
54
|
+
await toolRegistry.load();
|
|
55
|
+
return toolRegistry.help(requireString(params.name, "name"));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (method === "tools.skills") {
|
|
59
|
+
await toolRegistry.load();
|
|
60
|
+
return toolRegistry.resolveSkills(requireString(params.name, "name"));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (method === "tools.setConfig") {
|
|
64
|
+
await toolRegistry.load();
|
|
65
|
+
return toolRegistry.setConfig(
|
|
66
|
+
requireString(params.name, "name"),
|
|
67
|
+
requireString(params.field, "field"),
|
|
68
|
+
requireString(params.value, "value"),
|
|
69
|
+
requireChatId(chatId, method)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
48
73
|
if (method === "tools.run") {
|
|
49
74
|
if (!agentManager?.runTool) {
|
|
50
75
|
throw new Error("tools.run requires agentManager");
|
|
@@ -90,6 +115,20 @@ export function createArisaCapabilities({ artifactStore, taskStore, agentManager
|
|
|
90
115
|
return artifactStore.forChat(scopedChatId).get(requireString(params.artifactId, "artifactId"));
|
|
91
116
|
}
|
|
92
117
|
|
|
118
|
+
if (method === "artifacts.deliver") {
|
|
119
|
+
const scopedChatId = requireChatId(chatId, method);
|
|
120
|
+
const artifactId = requireString(params.artifactId, "artifactId");
|
|
121
|
+
const artifact = await artifactStore.forChat(scopedChatId).get(artifactId);
|
|
122
|
+
if (!artifact?.path) throw new Error(`Artifact not found or has no file: ${artifactId}`);
|
|
123
|
+
if (!agentManager?.deliverArtifact) throw new Error("artifact delivery is unavailable");
|
|
124
|
+
return agentManager.deliverArtifact({
|
|
125
|
+
chatId: scopedChatId,
|
|
126
|
+
artifact,
|
|
127
|
+
caption: params.caption,
|
|
128
|
+
method: params.method
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
93
132
|
if (method === "tasks.add") {
|
|
94
133
|
const scopedChatId = requireChatId(chatId, method);
|
|
95
134
|
return taskStore.add(params.task || {}, {
|
|
@@ -118,6 +157,11 @@ export function createArisaCapabilities({ artifactStore, taskStore, agentManager
|
|
|
118
157
|
return taskStore.cancel(taskId);
|
|
119
158
|
}
|
|
120
159
|
|
|
160
|
+
if (method === "tasks.cancelAll") {
|
|
161
|
+
const scopedChatId = requireChatId(chatId, method);
|
|
162
|
+
return taskStore.cancelAll({ chatId: scopedChatId });
|
|
163
|
+
}
|
|
164
|
+
|
|
121
165
|
if (method === "agent.enqueueEvent") {
|
|
122
166
|
const scopedChatId = requireChatId(chatId, method);
|
|
123
167
|
return taskStore.add({
|
package/src/runtime/bootstrap.js
CHANGED
|
@@ -7,6 +7,7 @@ import { Bot } from "grammy";
|
|
|
7
7
|
import { createPiOAuthLogin } from "../core/agent/pi-auth-login.js";
|
|
8
8
|
import { createPiRuntime, formatPiModelOption, hasProviderAuth, listPiProviders, listProviderModels, supportsProviderOAuth } from "../core/agent/pi-runtime.js";
|
|
9
9
|
import { applyConfigDefaults, telegramConfigDefaults } from "../core/config/config-defaults.js";
|
|
10
|
+
import { prepareConfigForSave } from "../core/config/config-store.js";
|
|
10
11
|
import { buildDeviceCodeTelegramMessage } from "../transport/telegram/device-code-message.js";
|
|
11
12
|
import { buildPagedInlineKeyboard } from "../transport/telegram/paged-inline-keyboard.js";
|
|
12
13
|
import { configFile, ensureArisaHome } from "./paths.js";
|
|
@@ -29,7 +30,7 @@ async function exists(file) {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function buildConfig({ telegramApiKey, telegramMaxChatIds, authorizedChatIds = [], chatMeta = {}, provider, model, piApiKey }) {
|
|
33
|
+
export function buildConfig({ telegramApiKey, telegramMaxChatIds, authorizedChatIds = [], chatMeta = {}, provider, model, piApiKey }) {
|
|
33
34
|
return applyConfigDefaults({
|
|
34
35
|
telegram: {
|
|
35
36
|
token: telegramApiKey,
|
|
@@ -602,7 +603,7 @@ export async function bootstrapIfNeeded({ force = false } = {}) {
|
|
|
602
603
|
result = await collectCliBootstrapChoices({ telegramApiKey, rl, ask });
|
|
603
604
|
}
|
|
604
605
|
|
|
605
|
-
await writeFile(configFile, `${JSON.stringify(result.config, null, 2)}\n`, "utf8");
|
|
606
|
+
await writeFile(configFile, `${JSON.stringify(prepareConfigForSave(result.config), null, 2)}\n`, "utf8");
|
|
606
607
|
console.log(`\nConfig saved to ${configFile}\n`);
|
|
607
608
|
return {
|
|
608
609
|
configCreated: true,
|
|
@@ -8,6 +8,10 @@ import { createTelegramBot } from "../transport/telegram/bot.js";
|
|
|
8
8
|
import { createToolProcessSupervisor } from "./tool-process-supervisor.js";
|
|
9
9
|
import { createArisaCapabilities } from "./arisa-capabilities.js";
|
|
10
10
|
import { createIpcServer } from "./ipc/ipc-server.js";
|
|
11
|
+
import { getAgentConfig } from "../core/agent/model-selection.js";
|
|
12
|
+
import { normalizeModelSpeed } from "../core/agent/model-speed.js";
|
|
13
|
+
import { runDoctor } from "./doctor.js";
|
|
14
|
+
import { checkForUpdates, formatUpdateReport } from "./update-manager.js";
|
|
11
15
|
|
|
12
16
|
function normalizeString(value) {
|
|
13
17
|
const text = String(value ?? "").trim();
|
|
@@ -51,10 +55,14 @@ function splitModelOverride(modelOverride) {
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
export function applyRuntimeOverrides(config, runtimeOverrides) {
|
|
58
|
+
const unsupportedNamespaces = Object.keys(runtimeOverrides || {}).filter((name) => name !== "pi");
|
|
59
|
+
if (unsupportedNamespaces.length) {
|
|
60
|
+
throw new Error(`Unsupported runtime override namespace: ${unsupportedNamespaces.join(", ")}`);
|
|
61
|
+
}
|
|
54
62
|
const piRuntimeOverrides = runtimeOverrides?.pi || {};
|
|
55
63
|
const pi = {};
|
|
56
|
-
const providerOverride = normalizeString(
|
|
57
|
-
const modelOverride = normalizeString(
|
|
64
|
+
const providerOverride = normalizeString(piRuntimeOverrides.provider);
|
|
65
|
+
const modelOverride = normalizeString(piRuntimeOverrides.model);
|
|
58
66
|
|
|
59
67
|
if (providerOverride || modelOverride) {
|
|
60
68
|
const splitOverride = modelOverride ? splitModelOverride(modelOverride) : null;
|
|
@@ -77,6 +85,7 @@ export function applyRuntimeOverrides(config, runtimeOverrides) {
|
|
|
77
85
|
|
|
78
86
|
const shellTimeoutMs = normalizePositiveInteger(piRuntimeOverrides.shellTimeoutMs);
|
|
79
87
|
if (shellTimeoutMs) pi.shellTimeoutMs = shellTimeoutMs;
|
|
88
|
+
if (piRuntimeOverrides.speed !== undefined) pi.speed = normalizeModelSpeed(piRuntimeOverrides.speed);
|
|
80
89
|
|
|
81
90
|
if (!Object.keys(pi).length) return config;
|
|
82
91
|
|
|
@@ -89,32 +98,56 @@ export function applyRuntimeOverrides(config, runtimeOverrides) {
|
|
|
89
98
|
};
|
|
90
99
|
}
|
|
91
100
|
|
|
92
|
-
export async function createApp({ logger, runtimeOverrides } = {}) {
|
|
101
|
+
export async function createApp({ logger, runtimeOverrides, requestRestart } = {}) {
|
|
102
|
+
if (typeof requestRestart !== "function") {
|
|
103
|
+
throw new Error("createApp requires a service restart handoff");
|
|
104
|
+
}
|
|
93
105
|
logger?.log("app", "loading config");
|
|
94
106
|
const persistedConfig = await loadConfig();
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
const overriddenConfig = applyRuntimeOverrides(persistedConfig, runtimeOverrides);
|
|
108
|
+
const config = overriddenConfig;
|
|
109
|
+
const activeConfig = getAgentConfig(config);
|
|
110
|
+
const persistedActiveConfig = getAgentConfig(persistedConfig);
|
|
111
|
+
if (activeConfig.provider !== persistedActiveConfig.provider || activeConfig.model !== persistedActiveConfig.model) {
|
|
112
|
+
logger?.log("app", `applying runtime model override: ${persistedActiveConfig.provider}/${persistedActiveConfig.model} -> ${activeConfig.provider}/${activeConfig.model}`);
|
|
98
113
|
}
|
|
99
114
|
|
|
100
115
|
const artifactStore = new ArtifactStore();
|
|
101
|
-
const toolProcessSupervisor = createToolProcessSupervisor({ logger, policy: config.daemons });
|
|
102
116
|
const toolRegistry = new ToolRegistry({ logger });
|
|
117
|
+
const toolProcessSupervisor = createToolProcessSupervisor({ logger, policy: config.daemons, toolRegistry });
|
|
103
118
|
const taskStore = new TaskStore();
|
|
104
119
|
await toolRegistry.load();
|
|
105
120
|
logger?.log("app", `loaded ${toolRegistry.list().length} tools`);
|
|
106
121
|
|
|
107
122
|
const agentManager = new AgentManager({ config, artifactStore, toolRegistry, taskStore, logger });
|
|
108
|
-
const arisaCapabilities = createArisaCapabilities({ artifactStore, taskStore, agentManager });
|
|
123
|
+
const arisaCapabilities = createArisaCapabilities({ artifactStore, taskStore, toolRegistry, agentManager });
|
|
109
124
|
const ipcServer = createIpcServer({ capabilities: arisaCapabilities, logger });
|
|
110
|
-
const bot = await createTelegramBot({
|
|
125
|
+
const bot = await createTelegramBot({
|
|
126
|
+
config,
|
|
127
|
+
artifactStore,
|
|
128
|
+
toolRegistry,
|
|
129
|
+
taskStore,
|
|
130
|
+
agentManager,
|
|
131
|
+
saveConfig,
|
|
132
|
+
updateConfig,
|
|
133
|
+
doctor: () => runDoctor({
|
|
134
|
+
agentManager,
|
|
135
|
+
toolProcessSupervisor,
|
|
136
|
+
daemonPolicy: config.daemons,
|
|
137
|
+
doctorPolicy: config.doctor,
|
|
138
|
+
logger
|
|
139
|
+
}),
|
|
140
|
+
checkUpdates: async (chatId) => formatUpdateReport(await checkForUpdates({ chatId, toolRegistry })),
|
|
141
|
+
requestRestart,
|
|
142
|
+
logger
|
|
143
|
+
});
|
|
111
144
|
|
|
112
145
|
return {
|
|
113
146
|
async start() {
|
|
114
|
-
logger?.log("app", `validating Pi model ${
|
|
147
|
+
logger?.log("app", `validating Pi model ${activeConfig.provider}/${activeConfig.model}`);
|
|
115
148
|
let skipAgentStartupPrompts = false;
|
|
116
149
|
try {
|
|
117
|
-
await agentManager.
|
|
150
|
+
await agentManager.validateAgent();
|
|
118
151
|
} catch (error) {
|
|
119
152
|
const issue = getPiAuthIssue(error);
|
|
120
153
|
if (!issue) {
|
|
@@ -129,6 +162,10 @@ export async function createApp({ logger, runtimeOverrides } = {}) {
|
|
|
129
162
|
try {
|
|
130
163
|
await ipcServer.start();
|
|
131
164
|
ipcStarted = true;
|
|
165
|
+
const recoveredTasks = await taskStore.recoverInterrupted();
|
|
166
|
+
if (recoveredTasks.length) {
|
|
167
|
+
logger?.log("tasks", `recovered ${recoveredTasks.length} interrupted task(s)`);
|
|
168
|
+
}
|
|
132
169
|
await toolProcessSupervisor.start();
|
|
133
170
|
supervisorStarted = true;
|
|
134
171
|
logger?.log("app", "starting Telegram bot");
|
|
@@ -142,6 +179,7 @@ export async function createApp({ logger, runtimeOverrides } = {}) {
|
|
|
142
179
|
|
|
143
180
|
async stop() {
|
|
144
181
|
await bot.stop?.();
|
|
182
|
+
await agentManager.close();
|
|
145
183
|
await toolProcessSupervisor.stop();
|
|
146
184
|
await ipcServer.stop();
|
|
147
185
|
}
|