@axon-assistant/core 2026.8.23
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/README.md +52 -0
- package/dist/Daemon.d.ts +119 -0
- package/dist/Daemon.d.ts.map +1 -0
- package/dist/PermissionHub.d.ts +23 -0
- package/dist/PermissionHub.d.ts.map +1 -0
- package/dist/WsSession.d.ts +32 -0
- package/dist/WsSession.d.ts.map +1 -0
- package/dist/auth.d.ts +49 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +12379 -0
- package/dist/commands.d.ts +41 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11717 -0
- package/dist/plugin-host.d.ts +2 -0
- package/dist/plugin-host.d.ts.map +1 -0
- package/dist/plugin-host.js +1870 -0
- package/package.json +65 -0
|
@@ -0,0 +1,1870 @@
|
|
|
1
|
+
// ../core/dist/logger.js
|
|
2
|
+
import { pino } from "pino";
|
|
3
|
+
var logger = pino(
|
|
4
|
+
{
|
|
5
|
+
level: process.env["AXON_LOG_LEVEL"] ?? "info",
|
|
6
|
+
redact: {
|
|
7
|
+
paths: [
|
|
8
|
+
"apiKey",
|
|
9
|
+
"token",
|
|
10
|
+
"secret",
|
|
11
|
+
"password",
|
|
12
|
+
"*.apiKey",
|
|
13
|
+
"*.token",
|
|
14
|
+
"*.secret",
|
|
15
|
+
"*.password",
|
|
16
|
+
"headers.authorization"
|
|
17
|
+
],
|
|
18
|
+
censor: "[\u0441\u043A\u0440\u044B\u0442\u043E]"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
// Логи — в stderr. stdout остаётся под вывод самой программы: демон печатает
|
|
22
|
+
// туда строку рукопожатия для родительского процесса, и лог, попавший в тот
|
|
23
|
+
// же поток, читается как эта строка.
|
|
24
|
+
process.stderr
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// ../core/dist/storage/sqlite.js
|
|
28
|
+
import { createRequire } from "node:module";
|
|
29
|
+
var require2 = createRequire(import.meta.url);
|
|
30
|
+
function requireSqlite() {
|
|
31
|
+
const original = process.emitWarning;
|
|
32
|
+
process.emitWarning = (warning, ...rest) => {
|
|
33
|
+
const text = typeof warning === "string" ? warning : warning.message;
|
|
34
|
+
if (text.includes("SQLite is an experimental feature"))
|
|
35
|
+
return;
|
|
36
|
+
original(warning, ...rest);
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
return require2("node:sqlite");
|
|
40
|
+
} finally {
|
|
41
|
+
process.emitWarning = original;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
var { DatabaseSync } = requireSqlite();
|
|
45
|
+
|
|
46
|
+
// ../core/dist/memory/Observations.js
|
|
47
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
48
|
+
|
|
49
|
+
// ../core/dist/storage/Backup.js
|
|
50
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
51
|
+
var require3 = createRequire2(import.meta.url);
|
|
52
|
+
|
|
53
|
+
// ../protocol/dist/primitives.js
|
|
54
|
+
import { z } from "zod";
|
|
55
|
+
var zTimestamp = z.string().datetime();
|
|
56
|
+
var zId = z.string().uuid();
|
|
57
|
+
var zExternalId = z.string().min(1).max(256);
|
|
58
|
+
var zSeq = z.number().int().nonnegative();
|
|
59
|
+
var zCursor = zSeq;
|
|
60
|
+
|
|
61
|
+
// ../protocol/dist/errors.js
|
|
62
|
+
import { z as z2 } from "zod";
|
|
63
|
+
var zErrorCode = z2.enum([
|
|
64
|
+
/** Кадр не прошёл валидацию схемы. */
|
|
65
|
+
"bad_request",
|
|
66
|
+
/** Токен устройства отсутствует, истёк или отозван. */
|
|
67
|
+
"unauthorized",
|
|
68
|
+
/** Токен валиден, но у устройства нет нужного scope. */
|
|
69
|
+
"forbidden",
|
|
70
|
+
/** Разговор / сообщение / устройство не найдены. */
|
|
71
|
+
"not_found",
|
|
72
|
+
/** Команда неизвестна этой версии ядра. */
|
|
73
|
+
"unknown_command",
|
|
74
|
+
/** Major протокола клиента не совпал с ядром. */
|
|
75
|
+
"protocol_mismatch",
|
|
76
|
+
/** Клиент просит события, которые уже вычищены компактификацией журнала. */
|
|
77
|
+
"cursor_too_old",
|
|
78
|
+
/** Операция отменена пользователем или обрывом соединения. */
|
|
79
|
+
"cancelled",
|
|
80
|
+
/** Упёрлись в лимит: бюджет токенов, размер файла, частота запросов. */
|
|
81
|
+
"limit_exceeded",
|
|
82
|
+
/** Ошибка внешнего провайдера (AI, умный дом, telegram). */
|
|
83
|
+
"upstream_failed",
|
|
84
|
+
/** Всё остальное. */
|
|
85
|
+
"internal"
|
|
86
|
+
]);
|
|
87
|
+
var zProtocolError = z2.object({
|
|
88
|
+
code: zErrorCode,
|
|
89
|
+
/** Человекочитаемое описание. Не парсить. */
|
|
90
|
+
message: z2.string(),
|
|
91
|
+
/** Дополнительные поля под конкретный код: `{ retryAfterMs }`, `{ requiredScope }` и т.п. */
|
|
92
|
+
details: z2.record(z2.unknown()).optional(),
|
|
93
|
+
/** Есть ли смысл повторить запрос. Клиент не должен угадывать это по коду. */
|
|
94
|
+
retryable: z2.boolean().default(false)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ../protocol/dist/domain.js
|
|
98
|
+
import { z as z3 } from "zod";
|
|
99
|
+
var zContentPart = z3.discriminatedUnion("type", [
|
|
100
|
+
z3.object({
|
|
101
|
+
type: z3.literal("text"),
|
|
102
|
+
text: z3.string()
|
|
103
|
+
}),
|
|
104
|
+
z3.object({
|
|
105
|
+
type: z3.literal("blob"),
|
|
106
|
+
blobId: zId,
|
|
107
|
+
mime: z3.string(),
|
|
108
|
+
bytes: z3.number().int().nonnegative(),
|
|
109
|
+
name: z3.string().optional()
|
|
110
|
+
})
|
|
111
|
+
]);
|
|
112
|
+
var zRole = z3.enum(["user", "assistant", "tool", "system"]);
|
|
113
|
+
var zUsage = z3.object({
|
|
114
|
+
inputTokens: z3.number().int().nonnegative(),
|
|
115
|
+
/** Прочитано из кэша промпта. */
|
|
116
|
+
cachedInputTokens: z3.number().int().nonnegative().default(0),
|
|
117
|
+
/** Записано в кэш промпта. */
|
|
118
|
+
cacheWriteTokens: z3.number().int().nonnegative().default(0),
|
|
119
|
+
outputTokens: z3.number().int().nonnegative(),
|
|
120
|
+
/** Оценка стоимости по тарифу провайдера на момент вызова. */
|
|
121
|
+
costUsd: z3.number().nonnegative().optional(),
|
|
122
|
+
provider: z3.string(),
|
|
123
|
+
model: z3.string()
|
|
124
|
+
});
|
|
125
|
+
var zRiskTier = z3.enum(["safe", "sensitive", "dangerous"]);
|
|
126
|
+
var zToolInfo = z3.object({
|
|
127
|
+
name: z3.string(),
|
|
128
|
+
title: z3.string(),
|
|
129
|
+
description: z3.string(),
|
|
130
|
+
tier: zRiskTier,
|
|
131
|
+
/** JSON Schema аргументов. Клиенту нужна для отрисовки карточки вызова. */
|
|
132
|
+
parameters: z3.record(z3.unknown()),
|
|
133
|
+
/** Кто предоставил инструмент: `builtin` или id плагина. */
|
|
134
|
+
source: z3.string(),
|
|
135
|
+
enabled: z3.boolean()
|
|
136
|
+
});
|
|
137
|
+
var zProviderInfo = z3.object({
|
|
138
|
+
id: z3.string(),
|
|
139
|
+
title: z3.string(),
|
|
140
|
+
/** Нужен ли API-ключ. Локальные модели и провайдеры плагинов обходятся без него. */
|
|
141
|
+
requiresKey: z3.boolean(),
|
|
142
|
+
/** Под каким именем ключ лежит в секретах ядра. */
|
|
143
|
+
secretKey: z3.string(),
|
|
144
|
+
defaultModel: z3.string(),
|
|
145
|
+
defaultBaseUrl: z3.string().optional(),
|
|
146
|
+
supportsPromptCache: z3.boolean(),
|
|
147
|
+
/** Куда идти за ключом — показывается в онбординге. */
|
|
148
|
+
keyUrl: z3.string().optional(),
|
|
149
|
+
/** Готов ли к работе: ключ задан либо не требуется. */
|
|
150
|
+
configured: z3.boolean(),
|
|
151
|
+
/** `builtin` или `plugin:<id>`. */
|
|
152
|
+
source: z3.string(),
|
|
153
|
+
/** Известные модели. У встроенных провайдеров пусто — там модель вводится руками. */
|
|
154
|
+
models: z3.array(z3.object({
|
|
155
|
+
id: z3.string(),
|
|
156
|
+
name: z3.string().optional(),
|
|
157
|
+
contextTokens: z3.number().int().positive().optional()
|
|
158
|
+
})).default([])
|
|
159
|
+
});
|
|
160
|
+
var zToolCall = z3.object({
|
|
161
|
+
/** id приходит от провайдера, поэтому не UUID. */
|
|
162
|
+
id: zExternalId,
|
|
163
|
+
name: z3.string(),
|
|
164
|
+
arguments: z3.record(z3.unknown())
|
|
165
|
+
});
|
|
166
|
+
var zToolResult = z3.discriminatedUnion("ok", [
|
|
167
|
+
z3.object({
|
|
168
|
+
ok: z3.literal(true),
|
|
169
|
+
preview: z3.string(),
|
|
170
|
+
truncated: z3.boolean().default(false),
|
|
171
|
+
/** Полный вывод, если он не поместился в preview. */
|
|
172
|
+
fullBlobId: zId.optional(),
|
|
173
|
+
durationMs: z3.number().int().nonnegative()
|
|
174
|
+
}),
|
|
175
|
+
z3.object({
|
|
176
|
+
ok: z3.literal(false),
|
|
177
|
+
error: z3.string(),
|
|
178
|
+
durationMs: z3.number().int().nonnegative()
|
|
179
|
+
})
|
|
180
|
+
]);
|
|
181
|
+
var zContextPart = z3.object({
|
|
182
|
+
key: z3.string(),
|
|
183
|
+
label: z3.string(),
|
|
184
|
+
tokens: z3.number().int().nonnegative(),
|
|
185
|
+
cached: z3.boolean(),
|
|
186
|
+
detail: z3.string().optional()
|
|
187
|
+
});
|
|
188
|
+
var zContextReport = z3.object({
|
|
189
|
+
parts: z3.array(zContextPart),
|
|
190
|
+
totalTokens: z3.number().int().nonnegative(),
|
|
191
|
+
cacheableTokens: z3.number().int().nonnegative(),
|
|
192
|
+
provider: z3.string().optional(),
|
|
193
|
+
model: z3.string().optional(),
|
|
194
|
+
supportsPromptCache: z3.boolean()
|
|
195
|
+
});
|
|
196
|
+
var zMessage = z3.object({
|
|
197
|
+
id: zId,
|
|
198
|
+
conversationId: zId,
|
|
199
|
+
role: zRole,
|
|
200
|
+
parts: z3.array(zContentPart),
|
|
201
|
+
/** Заполняется у assistant-сообщения, которое запросило инструменты. */
|
|
202
|
+
toolCalls: z3.array(zToolCall).optional(),
|
|
203
|
+
/** Заполняется у tool-сообщения — к какому вызову относится результат. */
|
|
204
|
+
toolCallId: zExternalId.optional(),
|
|
205
|
+
createdAt: zTimestamp,
|
|
206
|
+
/** Расход на генерацию именно этого сообщения. */
|
|
207
|
+
usage: zUsage.optional()
|
|
208
|
+
});
|
|
209
|
+
var zConversation = z3.object({
|
|
210
|
+
id: zId,
|
|
211
|
+
title: z3.string(),
|
|
212
|
+
createdAt: zTimestamp,
|
|
213
|
+
updatedAt: zTimestamp,
|
|
214
|
+
archived: z3.boolean().default(false),
|
|
215
|
+
/** Сколько токенов суммарно съел разговор за всё время. Видно пользователю. */
|
|
216
|
+
totalTokens: z3.number().int().nonnegative().default(0)
|
|
217
|
+
});
|
|
218
|
+
var zRunState = z3.enum(["running", "done", "failed", "cancelled"]);
|
|
219
|
+
var zStopReason = z3.enum([
|
|
220
|
+
/** Модель закончила ответ сама. */
|
|
221
|
+
"end_turn",
|
|
222
|
+
/** Упёрлись в потолок итераций «модель → инструмент → модель». */
|
|
223
|
+
"max_iterations",
|
|
224
|
+
/** Исчерпан бюджет токенов на прогон. */
|
|
225
|
+
"budget_exhausted",
|
|
226
|
+
/** Пользователь нажал стоп. */
|
|
227
|
+
"cancelled",
|
|
228
|
+
/** Разрешение на инструмент не выдано. */
|
|
229
|
+
"permission_denied",
|
|
230
|
+
/** Модель отказалась отвечать (сработали классификаторы безопасности). */
|
|
231
|
+
"refusal",
|
|
232
|
+
/** Провайдер или инструмент упали. */
|
|
233
|
+
"error"
|
|
234
|
+
]);
|
|
235
|
+
var zPermissionRequest = z3.object({
|
|
236
|
+
id: zId,
|
|
237
|
+
runId: zId,
|
|
238
|
+
toolName: z3.string(),
|
|
239
|
+
tier: zRiskTier,
|
|
240
|
+
/** Почему агент считает, что это нужно — показывается пользователю. */
|
|
241
|
+
reason: z3.string(),
|
|
242
|
+
arguments: z3.record(z3.unknown()),
|
|
243
|
+
/** До какого момента запрос имеет смысл; после — считается протухшим. */
|
|
244
|
+
expiresAt: zTimestamp
|
|
245
|
+
});
|
|
246
|
+
var zPermissionDecision = z3.enum([
|
|
247
|
+
"allow_once",
|
|
248
|
+
"allow_always",
|
|
249
|
+
"deny_once",
|
|
250
|
+
"deny_always",
|
|
251
|
+
/** Никто не ответил до expiresAt. */
|
|
252
|
+
"expired"
|
|
253
|
+
]);
|
|
254
|
+
var zScope = z3.enum([
|
|
255
|
+
"chat.read",
|
|
256
|
+
"chat.write",
|
|
257
|
+
"tools.safe",
|
|
258
|
+
"tools.sensitive",
|
|
259
|
+
"tools.dangerous",
|
|
260
|
+
"settings.write",
|
|
261
|
+
"devices.manage"
|
|
262
|
+
]);
|
|
263
|
+
var zDevicePlatform = z3.enum(["desktop", "mobile", "web", "telegram", "cli"]);
|
|
264
|
+
var zDevice = z3.object({
|
|
265
|
+
id: zId,
|
|
266
|
+
name: z3.string(),
|
|
267
|
+
platform: zDevicePlatform,
|
|
268
|
+
scopes: z3.array(zScope),
|
|
269
|
+
pairedAt: zTimestamp,
|
|
270
|
+
lastSeenAt: zTimestamp.optional()
|
|
271
|
+
});
|
|
272
|
+
var zFact = z3.object({
|
|
273
|
+
id: zId,
|
|
274
|
+
key: z3.string(),
|
|
275
|
+
value: z3.string(),
|
|
276
|
+
/** Откуда факт взялся: `user` — сказал явно, `inferred` — агент вывел сам. */
|
|
277
|
+
origin: z3.enum(["user", "inferred"]),
|
|
278
|
+
createdAt: zTimestamp,
|
|
279
|
+
updatedAt: zTimestamp
|
|
280
|
+
});
|
|
281
|
+
var zObservation = z3.object({
|
|
282
|
+
id: zId,
|
|
283
|
+
text: z3.string(),
|
|
284
|
+
/**
|
|
285
|
+
* О чём наблюдение. Нужен не для красоты: разные виды стареют с разной
|
|
286
|
+
* скоростью — привычка живёт годами, а настроение недели две.
|
|
287
|
+
*/
|
|
288
|
+
kind: z3.enum(["habit", "preference", "context", "mood", "relationship"]),
|
|
289
|
+
/** Вес на момент записи. Наружу отдаётся как есть, затухание считает ядро. */
|
|
290
|
+
weight: z3.number(),
|
|
291
|
+
/** Сколько раз подтверждалось. В отличие от веса, не убывает. */
|
|
292
|
+
hits: z3.number().int().nonnegative(),
|
|
293
|
+
createdAt: zTimestamp,
|
|
294
|
+
updatedAt: zTimestamp,
|
|
295
|
+
/** Когда в последний раз подтвердилось. От него считается затухание. */
|
|
296
|
+
lastSeenAt: zTimestamp
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// ../protocol/dist/impulse.js
|
|
300
|
+
import { z as z4 } from "zod";
|
|
301
|
+
var zTimeOfDay = z4.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "\u0412\u0440\u0435\u043C\u044F \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 \u0427\u0427:\u041C\u041C");
|
|
302
|
+
var zImpulse = z4.object({
|
|
303
|
+
enabled: z4.boolean().default(false),
|
|
304
|
+
/** Сколько раз за сутки агент может написать первым. */
|
|
305
|
+
maxPerDay: z4.number().int().min(1).max(12).default(3),
|
|
306
|
+
/** Сколько ждать между двумя своими сообщениями. */
|
|
307
|
+
minGapMinutes: z4.number().int().min(30).max(24 * 60).default(180),
|
|
308
|
+
/**
|
|
309
|
+
* Сколько человек должен молчать, чтобы к нему имело смысл обращаться.
|
|
310
|
+
*
|
|
311
|
+
* Написать тому, кто ответил пять минут назад, — не инициатива, а помеха:
|
|
312
|
+
* он и так здесь, и если бы хотел продолжить, продолжил бы сам.
|
|
313
|
+
*/
|
|
314
|
+
idleMinutes: z4.number().int().min(5).max(24 * 60).default(45),
|
|
315
|
+
/** Начало тишины. По умолчанию ночь. */
|
|
316
|
+
quietFrom: zTimeOfDay.default("23:00"),
|
|
317
|
+
quietTo: zTimeOfDay.default("09:00")
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// ../protocol/dist/persona.js
|
|
321
|
+
import { z as z5 } from "zod";
|
|
322
|
+
var zPersonaPreset = z5.enum(["calm", "warm", "blunt", "custom"]);
|
|
323
|
+
var zPersona = z5.object({
|
|
324
|
+
/**
|
|
325
|
+
* Как зовут агента. Пусто — имени ещё нет.
|
|
326
|
+
*
|
|
327
|
+
* Умолчания здесь нет намеренно. Подставь любое, даже название программы, —
|
|
328
|
+
* и агент представится им в первом же разговоре, то есть ответит на вопрос
|
|
329
|
+
* «как тебя зовут» раньше, чем человек успел на него ответить. Пустая
|
|
330
|
+
* строка — это не «забыли заполнить», а честное «пока никак».
|
|
331
|
+
*/
|
|
332
|
+
name: z5.string().max(40).default(""),
|
|
333
|
+
preset: zPersonaPreset.default("calm"),
|
|
334
|
+
/** Как зовут человека. Пусто — агент не выдумывает обращение. */
|
|
335
|
+
userName: z5.string().max(40).default(""),
|
|
336
|
+
/** На «ты» или на «вы». Влияет и на глаголы, поэтому это не косметика. */
|
|
337
|
+
address: z5.enum(["\u0442\u044B", "\u0432\u044B"]).default("\u0442\u044B"),
|
|
338
|
+
/**
|
|
339
|
+
* Юмор отдельной ручкой, а не частью характера: спокойный человек бывает и
|
|
340
|
+
* с иронией, и без неё, а вот выкрутить это в пресете невозможно — пресетов
|
|
341
|
+
* стало бы вдвое больше.
|
|
342
|
+
*/
|
|
343
|
+
humor: z5.enum(["none", "dry", "playful"]).default("dry"),
|
|
344
|
+
/** Длина ответа по умолчанию. */
|
|
345
|
+
verbosity: z5.enum(["short", "normal", "detailed"]).default("normal"),
|
|
346
|
+
/**
|
|
347
|
+
* Насколько агент лезет со своим мнением и следующим шагом внутри разговора.
|
|
348
|
+
* Это не про то, пишет ли он первым, — за это отвечает планировщик.
|
|
349
|
+
*/
|
|
350
|
+
initiative: z5.enum(["low", "normal", "high"]).default("normal"),
|
|
351
|
+
emoji: z5.boolean().default(false),
|
|
352
|
+
/** Свободное дополнение поверх собранного характера. */
|
|
353
|
+
custom: z5.string().max(4e3).default(""),
|
|
354
|
+
/**
|
|
355
|
+
* Знакомство состоялось.
|
|
356
|
+
*
|
|
357
|
+
* Пока флага нет, агент получает задание познакомиться — узнать, как
|
|
358
|
+
* обращаться к человеку и как его называть самого. Хранить это флагом, а не
|
|
359
|
+
* выводить из «имя не пустое», важно: человек мог сознательно оставить
|
|
360
|
+
* умолчания, и переспрашивать его об этом каждый разговор — худшее, что
|
|
361
|
+
* можно сделать со знакомством.
|
|
362
|
+
*/
|
|
363
|
+
configured: z5.boolean().default(false)
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// ../protocol/dist/plugins.js
|
|
367
|
+
import { z as z6 } from "zod";
|
|
368
|
+
var zPluginId = z6.string().min(1).max(40).regex(/^[a-z0-9][a-z0-9_-]*$/, "\u0422\u043E\u043B\u044C\u043A\u043E \u0441\u0442\u0440\u043E\u0447\u043D\u044B\u0435 \u043B\u0430\u0442\u0438\u043D\u0441\u043A\u0438\u0435 \u0431\u0443\u043A\u0432\u044B, \u0446\u0438\u0444\u0440\u044B, \u0434\u0435\u0444\u0438\u0441 \u0438 \u043F\u043E\u0434\u0447\u0451\u0440\u043A\u0438\u0432\u0430\u043D\u0438\u0435");
|
|
369
|
+
var zPluginPermission = z6.enum([
|
|
370
|
+
/** Читает и пишет файлы вне своей папки. */
|
|
371
|
+
"fs",
|
|
372
|
+
/** Ходит в сеть. */
|
|
373
|
+
"net",
|
|
374
|
+
/** Запускает процессы. */
|
|
375
|
+
"shell",
|
|
376
|
+
/** Читает секреты ядра — только свои, из собственного пространства имён. */
|
|
377
|
+
"secrets",
|
|
378
|
+
/** Подписывается на журнал: видит переписку. */
|
|
379
|
+
"journal"
|
|
380
|
+
]);
|
|
381
|
+
var zMcpTransport = z6.discriminatedUnion("type", [
|
|
382
|
+
z6.object({
|
|
383
|
+
type: z6.literal("stdio"),
|
|
384
|
+
command: z6.string().min(1),
|
|
385
|
+
args: z6.array(z6.string()).default([]),
|
|
386
|
+
env: z6.record(z6.string()).default({}),
|
|
387
|
+
cwd: z6.string().optional()
|
|
388
|
+
}),
|
|
389
|
+
z6.object({
|
|
390
|
+
type: z6.literal("http"),
|
|
391
|
+
url: z6.string().url(),
|
|
392
|
+
headers: z6.record(z6.string()).default({})
|
|
393
|
+
})
|
|
394
|
+
]);
|
|
395
|
+
var zPluginSettingField = z6.object({
|
|
396
|
+
key: z6.string().min(1).max(60),
|
|
397
|
+
label: z6.string().max(120),
|
|
398
|
+
description: z6.string().max(400).optional(),
|
|
399
|
+
/**
|
|
400
|
+
* `secret` хранится шифрованным и не отдаётся обратно клиенту — только
|
|
401
|
+
* признак «задано». Всё остальное лежит в обычных настройках.
|
|
402
|
+
*/
|
|
403
|
+
type: z6.enum(["text", "secret", "number", "boolean", "select", "path", "textarea"]),
|
|
404
|
+
options: z6.array(z6.object({ value: z6.string(), label: z6.string() })).optional(),
|
|
405
|
+
default: z6.unknown().optional(),
|
|
406
|
+
required: z6.boolean().default(false),
|
|
407
|
+
placeholder: z6.string().max(200).optional(),
|
|
408
|
+
/**
|
|
409
|
+
* Показывать поле, только когда другое имеет заданное значение.
|
|
410
|
+
*
|
|
411
|
+
* Без этого плагин с двумя способами подключения вываливает поля обоих
|
|
412
|
+
* сразу, и человек заполняет половину впустую. Условие простое до
|
|
413
|
+
* неприличия — одно поле, одно значение: сложнее бывает нужно редко, а
|
|
414
|
+
* язык выражений в манифесте плагина означал бы интерпретатор в ядре.
|
|
415
|
+
*/
|
|
416
|
+
visibleWhen: z6.object({ key: z6.string(), equals: z6.unknown() }).optional()
|
|
417
|
+
});
|
|
418
|
+
var zPluginSettingSection = z6.object({
|
|
419
|
+
title: z6.string().max(120),
|
|
420
|
+
description: z6.string().max(600).optional(),
|
|
421
|
+
fields: z6.array(z6.string()).default([])
|
|
422
|
+
});
|
|
423
|
+
var zPluginAction = z6.object({
|
|
424
|
+
name: z6.string().min(1).max(60),
|
|
425
|
+
label: z6.string().max(120),
|
|
426
|
+
description: z6.string().max(400).optional(),
|
|
427
|
+
/** Раздел, в котором показать кнопку. Пусто — внизу страницы. */
|
|
428
|
+
section: z6.string().max(120).optional(),
|
|
429
|
+
/** Спросить подтверждение перед вызовом. Для необратимого. */
|
|
430
|
+
confirm: z6.string().max(300).optional()
|
|
431
|
+
});
|
|
432
|
+
var zPluginJob = z6.object({
|
|
433
|
+
name: z6.string().min(1).max(60),
|
|
434
|
+
/** Период в секундах. Минимум минута: чаще — это не расписание, а цикл. */
|
|
435
|
+
everySeconds: z6.number().int().min(60),
|
|
436
|
+
/** Запускать сразу при активации, не дожидаясь первого периода. */
|
|
437
|
+
immediate: z6.boolean().default(false)
|
|
438
|
+
});
|
|
439
|
+
var zPluginManifest = z6.object({
|
|
440
|
+
id: zPluginId,
|
|
441
|
+
name: z6.string().min(1).max(80),
|
|
442
|
+
description: z6.string().max(500).default(""),
|
|
443
|
+
version: z6.string().max(40).default("0.0.0"),
|
|
444
|
+
author: z6.string().max(120).optional(),
|
|
445
|
+
homepage: z6.string().max(300).optional(),
|
|
446
|
+
/** Версия API плагинов, на которую он рассчитан. */
|
|
447
|
+
api: z6.number().int().positive().default(1),
|
|
448
|
+
/** Точка входа. Нет — плагин без кода: только MCP-серверы и скиллы. */
|
|
449
|
+
main: z6.string().max(200).optional(),
|
|
450
|
+
permissions: z6.array(zPluginPermission).default([]),
|
|
451
|
+
settings: z6.array(zPluginSettingField).default([]),
|
|
452
|
+
sections: z6.array(zPluginSettingSection).default([]),
|
|
453
|
+
actions: z6.array(zPluginAction).default([]),
|
|
454
|
+
/** Папка со скиллами (*.md) относительно корня плагина. */
|
|
455
|
+
skills: z6.string().max(200).optional(),
|
|
456
|
+
mcpServers: z6.record(zMcpTransport).default({}),
|
|
457
|
+
jobs: z6.array(zPluginJob).default([])
|
|
458
|
+
});
|
|
459
|
+
var zPluginStatus = z6.enum(["disabled", "starting", "ready", "failed", "needs_setup"]);
|
|
460
|
+
var zPluginOrigin = z6.object({
|
|
461
|
+
type: z6.enum(["catalog", "git", "link", "builtin", "archive"]),
|
|
462
|
+
/** Идентификатор в каталоге, URL репозитория, путь на диске или имя архива. */
|
|
463
|
+
ref: z6.string()
|
|
464
|
+
});
|
|
465
|
+
var zMcpServerInfo = z6.object({
|
|
466
|
+
name: z6.string(),
|
|
467
|
+
status: zPluginStatus,
|
|
468
|
+
error: z6.string().optional(),
|
|
469
|
+
toolCount: z6.number().int().nonnegative().default(0)
|
|
470
|
+
});
|
|
471
|
+
var zSkillInfo = z6.object({
|
|
472
|
+
/** Уникально в пределах ядра: `<pluginId>/<file>`. */
|
|
473
|
+
id: z6.string(),
|
|
474
|
+
name: z6.string(),
|
|
475
|
+
description: z6.string(),
|
|
476
|
+
/** Оценка размера тела в токенах — видно, во что обойдётся раскрытие. */
|
|
477
|
+
tokens: z6.number().int().nonnegative().default(0),
|
|
478
|
+
enabled: z6.boolean().default(true)
|
|
479
|
+
});
|
|
480
|
+
var zPluginInfo = z6.object({
|
|
481
|
+
id: zPluginId,
|
|
482
|
+
name: z6.string(),
|
|
483
|
+
description: z6.string(),
|
|
484
|
+
version: z6.string(),
|
|
485
|
+
author: z6.string().optional(),
|
|
486
|
+
homepage: z6.string().optional(),
|
|
487
|
+
origin: zPluginOrigin,
|
|
488
|
+
permissions: z6.array(zPluginPermission).default([]),
|
|
489
|
+
/** Форма настроек — интерфейс рисует её по этому описанию. */
|
|
490
|
+
settings: z6.array(zPluginSettingField).default([]),
|
|
491
|
+
sections: z6.array(zPluginSettingSection).default([]),
|
|
492
|
+
actions: z6.array(zPluginAction).default([]),
|
|
493
|
+
/**
|
|
494
|
+
* Текущие значения. Секреты сюда не попадают: вместо значения — `true`,
|
|
495
|
+
* если задано. Иначе токен уехал бы на каждое устройство в снапшоте.
|
|
496
|
+
*/
|
|
497
|
+
settingValues: z6.record(z6.unknown()).default({}),
|
|
498
|
+
/** Хочет ли пользователь, чтобы плагин работал. */
|
|
499
|
+
enabled: z6.boolean(),
|
|
500
|
+
/** Работает ли он на самом деле. */
|
|
501
|
+
status: zPluginStatus,
|
|
502
|
+
/** Почему не работает. Текст для человека, не стектрейс. */
|
|
503
|
+
error: z6.string().optional(),
|
|
504
|
+
/** Что плагин принёс в ядро. Пусто, пока он не запустился. */
|
|
505
|
+
tools: z6.array(z6.object({ name: z6.string(), title: z6.string(), tier: zRiskTier })).default([]),
|
|
506
|
+
skills: z6.array(zSkillInfo).default([]),
|
|
507
|
+
mcpServers: z6.array(zMcpServerInfo).default([]),
|
|
508
|
+
providers: z6.array(z6.string()).default([]),
|
|
509
|
+
jobs: z6.array(z6.object({
|
|
510
|
+
name: z6.string(),
|
|
511
|
+
everySeconds: z6.number().int().positive(),
|
|
512
|
+
lastRunAt: zTimestamp.optional(),
|
|
513
|
+
lastError: z6.string().optional()
|
|
514
|
+
})).default([]),
|
|
515
|
+
installedAt: zTimestamp,
|
|
516
|
+
updatedAt: zTimestamp
|
|
517
|
+
});
|
|
518
|
+
var zCatalogEntry = z6.object({
|
|
519
|
+
id: zPluginId,
|
|
520
|
+
name: z6.string(),
|
|
521
|
+
description: z6.string(),
|
|
522
|
+
tags: z6.array(z6.string()).default([]),
|
|
523
|
+
homepage: z6.string().optional(),
|
|
524
|
+
permissions: z6.array(zPluginPermission).default([]),
|
|
525
|
+
/** Что спросить у человека до установки: токены, пути, ключи. */
|
|
526
|
+
setup: z6.array(zPluginSettingField).default([]),
|
|
527
|
+
install: z6.discriminatedUnion("type", [
|
|
528
|
+
z6.object({ type: z6.literal("git"), url: z6.string(), ref: z6.string().optional() }),
|
|
529
|
+
/**
|
|
530
|
+
* Плагин-обёртка вокруг MCP-сервера: ядро соберёт манифест само.
|
|
531
|
+
* `${setup.ключ}` в аргументах и окружении подставляется из ответов.
|
|
532
|
+
*/
|
|
533
|
+
z6.object({ type: z6.literal("mcp"), transport: zMcpTransport })
|
|
534
|
+
])
|
|
535
|
+
});
|
|
536
|
+
var zPluginSource = z6.discriminatedUnion("type", [
|
|
537
|
+
z6.object({
|
|
538
|
+
type: z6.literal("catalog"),
|
|
539
|
+
id: zPluginId,
|
|
540
|
+
/** Ответы на `setup` из записи каталога. */
|
|
541
|
+
values: z6.record(z6.string()).default({})
|
|
542
|
+
}),
|
|
543
|
+
z6.object({
|
|
544
|
+
type: z6.literal("git"),
|
|
545
|
+
url: z6.string().min(1),
|
|
546
|
+
ref: z6.string().optional()
|
|
547
|
+
}),
|
|
548
|
+
/**
|
|
549
|
+
* Свой MCP-сервер, которого нет в каталоге.
|
|
550
|
+
*
|
|
551
|
+
* Главный способ поставить что-то в Axon, а не запасной: MCP-серверов
|
|
552
|
+
* существуют сотни, и каталог из шести проверенных — это закладки, а не
|
|
553
|
+
* граница возможного. Ядро соберёт вокруг конфигурации плагин-обёртку само.
|
|
554
|
+
*/
|
|
555
|
+
z6.object({
|
|
556
|
+
type: z6.literal("mcp"),
|
|
557
|
+
name: zPluginId,
|
|
558
|
+
/** Как показать сервер в списке. Пусто — возьмём имя. */
|
|
559
|
+
title: z6.string().max(80).optional(),
|
|
560
|
+
transport: zMcpTransport
|
|
561
|
+
}),
|
|
562
|
+
/**
|
|
563
|
+
* Папка на машине ядра. Подключается по месту, а не копируется — это режим
|
|
564
|
+
* разработки: правишь файлы, перезапускаешь плагин, видишь результат.
|
|
565
|
+
*/
|
|
566
|
+
z6.object({ type: z6.literal("link"), path: z6.string().min(1) }),
|
|
567
|
+
/**
|
|
568
|
+
* Архив, загруженный человеком. Едет блобом, как вложение к сообщению:
|
|
569
|
+
* гонять мегабайты через WebSocket, у которого своя очередь кадров, — верный
|
|
570
|
+
* способ подвесить всё остальное на время загрузки.
|
|
571
|
+
*/
|
|
572
|
+
z6.object({ type: z6.literal("archive"), blobId: zId, name: z6.string().max(200).optional() })
|
|
573
|
+
]);
|
|
574
|
+
|
|
575
|
+
// ../protocol/dist/routines.js
|
|
576
|
+
import { z as z7 } from "zod";
|
|
577
|
+
var zTimeOfDay2 = z7.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "\u0412\u0440\u0435\u043C\u044F \u0437\u0430\u0434\u0430\u0451\u0442\u0441\u044F \u043A\u0430\u043A \u0427\u0427:\u041C\u041C, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 09:30");
|
|
578
|
+
var zSchedule = z7.discriminatedUnion("kind", [
|
|
579
|
+
/**
|
|
580
|
+
* Через равные промежутки. Минимум пять минут: всё, что чаще, — это уже не
|
|
581
|
+
* расписание, а фоновый цикл, и для него есть плагины.
|
|
582
|
+
*/
|
|
583
|
+
z7.object({
|
|
584
|
+
kind: z7.literal("interval"),
|
|
585
|
+
everyMinutes: z7.number().int().min(5).max(60 * 24 * 30)
|
|
586
|
+
}),
|
|
587
|
+
z7.object({ kind: z7.literal("daily"), time: zTimeOfDay2 }),
|
|
588
|
+
z7.object({
|
|
589
|
+
kind: z7.literal("weekly"),
|
|
590
|
+
/** 0 — воскресенье, как в JS. */
|
|
591
|
+
days: z7.array(z7.number().int().min(0).max(6)).min(1),
|
|
592
|
+
time: zTimeOfDay2
|
|
593
|
+
}),
|
|
594
|
+
/** Разовое напоминание. После срабатывания рутина выключается сама. */
|
|
595
|
+
z7.object({ kind: z7.literal("once"), at: zTimestamp }),
|
|
596
|
+
/** Только вручную: кнопкой «Запустить сейчас». */
|
|
597
|
+
z7.object({ kind: z7.literal("manual") })
|
|
598
|
+
]);
|
|
599
|
+
var zCondition = z7.object({
|
|
600
|
+
/** Что проверяем: имя переменной или строка с подстановками `${…}`. */
|
|
601
|
+
left: z7.string().max(2e3),
|
|
602
|
+
op: z7.enum([
|
|
603
|
+
"equals",
|
|
604
|
+
"notEquals",
|
|
605
|
+
"contains",
|
|
606
|
+
"notContains",
|
|
607
|
+
"matches",
|
|
608
|
+
"empty",
|
|
609
|
+
"notEmpty",
|
|
610
|
+
"greaterThan",
|
|
611
|
+
"lessThan"
|
|
612
|
+
]),
|
|
613
|
+
/** С чем сравниваем. Для `empty` и `notEmpty` не нужно. */
|
|
614
|
+
right: z7.string().max(2e3).optional()
|
|
615
|
+
});
|
|
616
|
+
var NAME = new RegExp("^\\p{L}[\\p{L}\\p{N}_]*$", "u");
|
|
617
|
+
var zOutputVar = z7.string().regex(NAME, "\u0418\u043C\u044F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439: \u0431\u0443\u043A\u0432\u0430, \u0434\u0430\u043B\u044C\u0448\u0435 \u0431\u0443\u043A\u0432\u044B, \u0446\u0438\u0444\u0440\u044B \u0438 \u043F\u043E\u0434\u0447\u0451\u0440\u043A\u0438\u0432\u0430\u043D\u0438\u0435").max(40).optional();
|
|
618
|
+
var zRoutineStep = z7.lazy(() => z7.discriminatedUnion("kind", [
|
|
619
|
+
z7.object({
|
|
620
|
+
kind: z7.literal("tool"),
|
|
621
|
+
tool: z7.string().min(1).max(80),
|
|
622
|
+
args: z7.record(z7.unknown()).default({}),
|
|
623
|
+
outputVar: zOutputVar,
|
|
624
|
+
continueOnError: z7.boolean().optional()
|
|
625
|
+
}),
|
|
626
|
+
z7.object({
|
|
627
|
+
kind: z7.literal("prompt"),
|
|
628
|
+
prompt: z7.string().min(1).max(8e3),
|
|
629
|
+
outputVar: zOutputVar,
|
|
630
|
+
maxTokens: z7.number().int().positive().max(4e3).optional()
|
|
631
|
+
}),
|
|
632
|
+
z7.object({
|
|
633
|
+
kind: z7.literal("extract"),
|
|
634
|
+
from: z7.string().min(1).max(2e3),
|
|
635
|
+
fields: z7.array(z7.object({ name: z7.string().min(1).max(40), description: z7.string().max(300) })).min(1).max(12),
|
|
636
|
+
outputVar: zOutputVar
|
|
637
|
+
}),
|
|
638
|
+
z7.object({
|
|
639
|
+
kind: z7.literal("agent"),
|
|
640
|
+
task: z7.string().min(1).max(4e3),
|
|
641
|
+
allowTools: z7.array(z7.string()).optional(),
|
|
642
|
+
budgetTokens: z7.number().int().positive().optional(),
|
|
643
|
+
outputVar: zOutputVar
|
|
644
|
+
}),
|
|
645
|
+
z7.object({
|
|
646
|
+
kind: z7.literal("if"),
|
|
647
|
+
condition: zCondition,
|
|
648
|
+
then: z7.array(zRoutineStep).max(40),
|
|
649
|
+
otherwise: z7.array(zRoutineStep).max(40).optional()
|
|
650
|
+
}),
|
|
651
|
+
z7.object({
|
|
652
|
+
kind: z7.literal("foreach"),
|
|
653
|
+
source: z7.string().min(1).max(2e3),
|
|
654
|
+
steps: z7.array(zRoutineStep).max(40),
|
|
655
|
+
limit: z7.number().int().positive().max(500).optional()
|
|
656
|
+
}),
|
|
657
|
+
z7.object({
|
|
658
|
+
kind: z7.literal("set"),
|
|
659
|
+
name: z7.string().regex(NAME).max(40),
|
|
660
|
+
value: z7.string().max(8e3)
|
|
661
|
+
}),
|
|
662
|
+
z7.object({
|
|
663
|
+
kind: z7.literal("notify"),
|
|
664
|
+
title: z7.string().min(1).max(200),
|
|
665
|
+
body: z7.string().max(1e3).optional()
|
|
666
|
+
}),
|
|
667
|
+
z7.object({ kind: z7.literal("message"), text: z7.string().min(1).max(2e4) }),
|
|
668
|
+
z7.object({
|
|
669
|
+
kind: z7.literal("remember"),
|
|
670
|
+
key: z7.string().min(1).max(200),
|
|
671
|
+
value: z7.string().min(1).max(4e3)
|
|
672
|
+
}),
|
|
673
|
+
z7.object({ kind: z7.literal("wait"), seconds: z7.number().int().min(1).max(3600) }),
|
|
674
|
+
z7.object({ kind: z7.literal("stop"), reason: z7.string().max(500).optional() })
|
|
675
|
+
]));
|
|
676
|
+
var zRoutineStatus = z7.enum(["ok", "failed", "skipped", "running"]);
|
|
677
|
+
var zRoutine = z7.object({
|
|
678
|
+
id: zId,
|
|
679
|
+
name: z7.string().min(1).max(120),
|
|
680
|
+
/** Что делает рутина — одной фразой, для списка. */
|
|
681
|
+
description: z7.string().max(500).default(""),
|
|
682
|
+
/**
|
|
683
|
+
* Исходное описание словами. Хранится, чтобы рутину можно было пересобрать
|
|
684
|
+
* после появления новых инструментов или правки текста.
|
|
685
|
+
*/
|
|
686
|
+
source: z7.string().max(8e3).default(""),
|
|
687
|
+
steps: z7.array(zRoutineStep).max(60).default([]),
|
|
688
|
+
schedule: zSchedule,
|
|
689
|
+
enabled: z7.boolean(),
|
|
690
|
+
/**
|
|
691
|
+
* Разговор, в который пишутся шаги `message`. Заводится при первом прогоне и
|
|
692
|
+
* живёт дальше: результат рутины — обычная переписка, которую можно открыть,
|
|
693
|
+
* прочитать и продолжить руками.
|
|
694
|
+
*/
|
|
695
|
+
conversationId: zId.optional(),
|
|
696
|
+
/**
|
|
697
|
+
* Потолок токенов на прогон. Обязателен: за фоновой задачей никто не
|
|
698
|
+
* смотрит, и перерасход обнаруживается по счёту, а не сразу.
|
|
699
|
+
*/
|
|
700
|
+
budgetTokens: z7.number().int().positive(),
|
|
701
|
+
/**
|
|
702
|
+
* Инструменты сверх безопасных, разрешённые этой рутине. Рядом нет человека,
|
|
703
|
+
* который подтвердит опасное действие, и подтверждать его молча нельзя.
|
|
704
|
+
*/
|
|
705
|
+
allowTools: z7.array(z7.string()).default([]),
|
|
706
|
+
notify: z7.boolean().default(true),
|
|
707
|
+
createdAt: zTimestamp,
|
|
708
|
+
updatedAt: zTimestamp,
|
|
709
|
+
nextRunAt: zTimestamp.optional(),
|
|
710
|
+
lastRunAt: zTimestamp.optional(),
|
|
711
|
+
lastStatus: zRoutineStatus.optional(),
|
|
712
|
+
lastSummary: z7.string().optional()
|
|
713
|
+
});
|
|
714
|
+
var zStepLog = z7.object({
|
|
715
|
+
/** Путь до шага: «2» — третий шаг, «2.1» — второй шаг внутри него. */
|
|
716
|
+
path: z7.string(),
|
|
717
|
+
kind: z7.string(),
|
|
718
|
+
ok: z7.boolean(),
|
|
719
|
+
/** Что шаг вернул, обрезанное до разумного. */
|
|
720
|
+
output: z7.string().optional(),
|
|
721
|
+
error: z7.string().optional(),
|
|
722
|
+
durationMs: z7.number().int().nonnegative(),
|
|
723
|
+
/** Сколько токенов стоил шаг. У большинства шагов — ноль. */
|
|
724
|
+
tokens: z7.number().int().nonnegative().default(0)
|
|
725
|
+
});
|
|
726
|
+
var zRoutineRun = z7.object({
|
|
727
|
+
id: z7.number().int().positive(),
|
|
728
|
+
routineId: zId,
|
|
729
|
+
startedAt: zTimestamp,
|
|
730
|
+
finishedAt: zTimestamp.optional(),
|
|
731
|
+
status: zRoutineStatus,
|
|
732
|
+
trigger: z7.enum(["schedule", "manual"]),
|
|
733
|
+
steps: z7.array(zStepLog).default([]),
|
|
734
|
+
summary: z7.string().default(""),
|
|
735
|
+
tokens: z7.number().int().nonnegative().default(0)
|
|
736
|
+
});
|
|
737
|
+
var zRoutineCompileReq = z7.object({
|
|
738
|
+
source: z7.string().min(1).max(8e3),
|
|
739
|
+
/** Инструменты, которые компилятору разрешено использовать в шагах. */
|
|
740
|
+
allowTools: z7.array(z7.string()).default([])
|
|
741
|
+
});
|
|
742
|
+
var zRoutineCompileRes = z7.object({
|
|
743
|
+
name: z7.string(),
|
|
744
|
+
description: z7.string(),
|
|
745
|
+
steps: z7.array(zRoutineStep),
|
|
746
|
+
schedule: zSchedule,
|
|
747
|
+
/** Замечания компилятора: чего он не смог, что заменил. */
|
|
748
|
+
warnings: z7.array(z7.string()).default([])
|
|
749
|
+
});
|
|
750
|
+
var zRoutineCreateReq = z7.object({
|
|
751
|
+
name: z7.string().min(1).max(120),
|
|
752
|
+
description: z7.string().max(500).default(""),
|
|
753
|
+
source: z7.string().max(8e3).default(""),
|
|
754
|
+
steps: z7.array(zRoutineStep).max(60),
|
|
755
|
+
schedule: zSchedule,
|
|
756
|
+
budgetTokens: z7.number().int().positive().default(2e4),
|
|
757
|
+
allowTools: z7.array(z7.string()).default([]),
|
|
758
|
+
notify: z7.boolean().default(true)
|
|
759
|
+
});
|
|
760
|
+
var zRoutineUpdateReq = z7.object({
|
|
761
|
+
id: zId,
|
|
762
|
+
name: z7.string().min(1).max(120).optional(),
|
|
763
|
+
description: z7.string().max(500).optional(),
|
|
764
|
+
source: z7.string().max(8e3).optional(),
|
|
765
|
+
steps: z7.array(zRoutineStep).max(60).optional(),
|
|
766
|
+
schedule: zSchedule.optional(),
|
|
767
|
+
enabled: z7.boolean().optional(),
|
|
768
|
+
budgetTokens: z7.number().int().positive().optional(),
|
|
769
|
+
allowTools: z7.array(z7.string()).optional(),
|
|
770
|
+
notify: z7.boolean().optional()
|
|
771
|
+
});
|
|
772
|
+
var zRoutineRunsReq = z7.object({
|
|
773
|
+
routineId: zId,
|
|
774
|
+
limit: z7.number().int().positive().max(50).default(10)
|
|
775
|
+
});
|
|
776
|
+
var zRoutineRunsRes = z7.object({ runs: z7.array(zRoutineRun) });
|
|
777
|
+
|
|
778
|
+
// ../protocol/dist/events.js
|
|
779
|
+
import { z as z8 } from "zod";
|
|
780
|
+
var zJournalEvent = z8.discriminatedUnion("type", [
|
|
781
|
+
// ── Разговоры ──
|
|
782
|
+
z8.object({ type: z8.literal("conversation.created"), conversation: zConversation }),
|
|
783
|
+
z8.object({ type: z8.literal("conversation.renamed"), id: zId, title: z8.string() }),
|
|
784
|
+
z8.object({ type: z8.literal("conversation.archived"), id: zId, archived: z8.boolean() }),
|
|
785
|
+
z8.object({ type: z8.literal("conversation.deleted"), id: zId }),
|
|
786
|
+
// ── Сообщения ──
|
|
787
|
+
z8.object({ type: z8.literal("message.created"), message: zMessage }),
|
|
788
|
+
/**
|
|
789
|
+
* Сообщение изменено задним числом: правка пользователя или подстановка
|
|
790
|
+
* итогового текста после стрима. Клиент заменяет содержимое целиком.
|
|
791
|
+
*/
|
|
792
|
+
z8.object({ type: z8.literal("message.amended"), message: zMessage }),
|
|
793
|
+
z8.object({ type: z8.literal("message.deleted"), id: zId, conversationId: zId }),
|
|
794
|
+
// ── Прогоны агента ──
|
|
795
|
+
z8.object({
|
|
796
|
+
type: z8.literal("run.started"),
|
|
797
|
+
runId: zId,
|
|
798
|
+
conversationId: zId,
|
|
799
|
+
/** Сколько токенов прогону разрешено сжечь. null — без потолка. */
|
|
800
|
+
budgetTokens: z8.number().int().positive().nullable()
|
|
801
|
+
}),
|
|
802
|
+
z8.object({
|
|
803
|
+
type: z8.literal("run.finished"),
|
|
804
|
+
runId: zId,
|
|
805
|
+
conversationId: zId,
|
|
806
|
+
stopReason: zStopReason,
|
|
807
|
+
/** Суммарный расход за весь прогон, включая все итерации с инструментами. */
|
|
808
|
+
usage: zUsage.optional(),
|
|
809
|
+
iterations: z8.number().int().nonnegative()
|
|
810
|
+
}),
|
|
811
|
+
z8.object({
|
|
812
|
+
type: z8.literal("run.failed"),
|
|
813
|
+
runId: zId,
|
|
814
|
+
conversationId: zId,
|
|
815
|
+
error: z8.string()
|
|
816
|
+
}),
|
|
817
|
+
// ── Инструменты ──
|
|
818
|
+
z8.object({
|
|
819
|
+
type: z8.literal("tool_call.started"),
|
|
820
|
+
runId: zId,
|
|
821
|
+
conversationId: zId,
|
|
822
|
+
call: zToolCall
|
|
823
|
+
}),
|
|
824
|
+
z8.object({
|
|
825
|
+
type: z8.literal("tool_call.finished"),
|
|
826
|
+
runId: zId,
|
|
827
|
+
conversationId: zId,
|
|
828
|
+
callId: zExternalId,
|
|
829
|
+
result: zToolResult
|
|
830
|
+
}),
|
|
831
|
+
// ── Разрешения ──
|
|
832
|
+
z8.object({ type: z8.literal("permission.requested"), request: zPermissionRequest }),
|
|
833
|
+
z8.object({
|
|
834
|
+
type: z8.literal("permission.resolved"),
|
|
835
|
+
requestId: zId,
|
|
836
|
+
decision: zPermissionDecision,
|
|
837
|
+
/** Какое устройство ответило. Пусто, если решение принято по правилу или по таймауту. */
|
|
838
|
+
deviceId: zId.optional()
|
|
839
|
+
}),
|
|
840
|
+
// ── Память ──
|
|
841
|
+
z8.object({ type: z8.literal("fact.upserted"), fact: zFact }),
|
|
842
|
+
z8.object({ type: z8.literal("fact.forgotten"), id: zId }),
|
|
843
|
+
z8.object({ type: z8.literal("observation.noticed"), observation: zObservation }),
|
|
844
|
+
z8.object({ type: z8.literal("observation.forgotten"), id: zId }),
|
|
845
|
+
// ── Инициатива ──
|
|
846
|
+
/**
|
|
847
|
+
* Агент написал первым.
|
|
848
|
+
*
|
|
849
|
+
* Отдельное событие, а не просто сообщение в разговоре: клиент должен
|
|
850
|
+
* показать уведомление, а по `message.created` отличить собственный порыв
|
|
851
|
+
* агента от обычного ответа невозможно — роль там одна и та же.
|
|
852
|
+
*/
|
|
853
|
+
z8.object({
|
|
854
|
+
type: z8.literal("impulse.sent"),
|
|
855
|
+
conversationId: zId,
|
|
856
|
+
messageId: zId,
|
|
857
|
+
/** Чем агент объяснил себе, зачем пишет. Видно человеку в интерфейсе. */
|
|
858
|
+
reason: z8.string()
|
|
859
|
+
}),
|
|
860
|
+
// ── Инструменты ──
|
|
861
|
+
/**
|
|
862
|
+
* Инструмент включили, выключили или переопределили. Без этого события
|
|
863
|
+
* клиент не узнаёт о смене состояния и показывает старое: переключатель
|
|
864
|
+
* выглядит нажатым вхолостую.
|
|
865
|
+
*/
|
|
866
|
+
z8.object({ type: z8.literal("tool.changed"), tool: zToolInfo }),
|
|
867
|
+
// ── Плагины ──
|
|
868
|
+
/**
|
|
869
|
+
* Плагин поставлен, настроен, включён или выключен — то есть изменилось
|
|
870
|
+
* решение пользователя. Смена рабочего состояния (запускается, упал,
|
|
871
|
+
* поднялся) сюда не пишется: это сигнал. Иначе журнал получал бы по паре
|
|
872
|
+
* записей на плагин при каждом старте ядра — навсегда.
|
|
873
|
+
*/
|
|
874
|
+
z8.object({ type: z8.literal("plugin.changed"), plugin: zPluginInfo }),
|
|
875
|
+
z8.object({ type: z8.literal("plugin.removed"), id: z8.string() }),
|
|
876
|
+
// ── Рутины ──
|
|
877
|
+
/**
|
|
878
|
+
* Рутина заведена, изменена или отработала. Исход прогона тоже сюда: это
|
|
879
|
+
* состояние, которое должно пережить перезапуск и доехать до всех устройств,
|
|
880
|
+
* а не мигнуть уведомлением у того, кто в этот момент смотрел в экран.
|
|
881
|
+
*/
|
|
882
|
+
z8.object({ type: z8.literal("routine.changed"), routine: zRoutine }),
|
|
883
|
+
z8.object({ type: z8.literal("routine.removed"), id: zId }),
|
|
884
|
+
/**
|
|
885
|
+
* Рутина просит показать уведомление. В журнале, а не сигналом: у ядра нет
|
|
886
|
+
* экрана, а показать должен тот клиент, который сейчас на связи — и увидеть
|
|
887
|
+
* пропущенное при следующем подключении тоже нужно.
|
|
888
|
+
*/
|
|
889
|
+
z8.object({
|
|
890
|
+
type: z8.literal("routine.notified"),
|
|
891
|
+
routineId: zId,
|
|
892
|
+
title: z8.string(),
|
|
893
|
+
body: z8.string().optional()
|
|
894
|
+
}),
|
|
895
|
+
// ── Устройства ──
|
|
896
|
+
z8.object({ type: z8.literal("device.paired"), device: zDevice }),
|
|
897
|
+
z8.object({ type: z8.literal("device.revoked"), id: zId }),
|
|
898
|
+
// ── Настройки ──
|
|
899
|
+
/**
|
|
900
|
+
* Значения не передаются: среди настроек лежат API-ключи, и им нечего делать
|
|
901
|
+
* в журнале, который синкается на все устройства. Клиент, увидев событие,
|
|
902
|
+
* перезапрашивает то, к чему у него есть доступ.
|
|
903
|
+
*/
|
|
904
|
+
z8.object({ type: z8.literal("settings.changed"), keys: z8.array(z8.string()) })
|
|
905
|
+
]);
|
|
906
|
+
var zJournalEntry = z8.object({
|
|
907
|
+
seq: zSeq,
|
|
908
|
+
at: zTimestamp,
|
|
909
|
+
event: zJournalEvent
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
// ../protocol/dist/signals.js
|
|
913
|
+
import { z as z9 } from "zod";
|
|
914
|
+
var zSignal = z9.discriminatedUnion("type", [
|
|
915
|
+
/** Кусок текста от модели. Клиент дорисовывает его в открытый пузырь. */
|
|
916
|
+
z9.object({
|
|
917
|
+
type: z9.literal("run.delta"),
|
|
918
|
+
runId: zId,
|
|
919
|
+
text: z9.string()
|
|
920
|
+
}),
|
|
921
|
+
/** Что агент делает прямо сейчас — для честного индикатора вместо вечного «печатает…». */
|
|
922
|
+
z9.object({
|
|
923
|
+
type: z9.literal("run.phase"),
|
|
924
|
+
runId: zId,
|
|
925
|
+
/**
|
|
926
|
+
* `retrying` — ждём и пробуем снова после отказа провайдера. Без этой фазы
|
|
927
|
+
* пауза в восемь секунд выглядит как зависание, и человек жмёт «стоп»
|
|
928
|
+
* ровно тогда, когда ядро само бы справилось.
|
|
929
|
+
*/
|
|
930
|
+
phase: z9.enum([
|
|
931
|
+
"thinking",
|
|
932
|
+
"calling_tool",
|
|
933
|
+
"awaiting_permission",
|
|
934
|
+
"summarizing",
|
|
935
|
+
"retrying"
|
|
936
|
+
]),
|
|
937
|
+
/** Имя инструмента для фазы calling_tool. */
|
|
938
|
+
detail: z9.string().optional()
|
|
939
|
+
}),
|
|
940
|
+
/**
|
|
941
|
+
* Расход в реальном времени, пока прогон идёт. Пользователь видит счётчик,
|
|
942
|
+
* а не узнаёт постфактум, что запрос стоил денег.
|
|
943
|
+
*/
|
|
944
|
+
z9.object({
|
|
945
|
+
type: z9.literal("usage.tick"),
|
|
946
|
+
runId: zId,
|
|
947
|
+
usage: zUsage,
|
|
948
|
+
/** Остаток бюджета прогона в токенах. null — потолка нет. */
|
|
949
|
+
budgetRemaining: z9.number().int().nullable()
|
|
950
|
+
}),
|
|
951
|
+
/** Какие ещё устройства сейчас на связи с ядром. */
|
|
952
|
+
z9.object({
|
|
953
|
+
type: z9.literal("presence"),
|
|
954
|
+
deviceIds: z9.array(zId)
|
|
955
|
+
}),
|
|
956
|
+
/**
|
|
957
|
+
* Плагин сменил рабочее состояние: поднимается, поднялся, упал, отдал свои
|
|
958
|
+
* инструменты. Пропажа такого сигнала не ломает состояние — при следующем
|
|
959
|
+
* подключении клиент получит актуальную картину снапшотом.
|
|
960
|
+
*/
|
|
961
|
+
/**
|
|
962
|
+
* Набор инструментов изменился: поднялся плагин, упал, сняли или поставили.
|
|
963
|
+
*
|
|
964
|
+
* Сигналом, а не журнальным событием, потому что состав инструментов —
|
|
965
|
+
* величина производная: он собирается заново при каждом запуске ядра из
|
|
966
|
+
* того, какие плагины поднялись. Журналировать это значило бы писать
|
|
967
|
+
* одинаковую пачку записей на каждый перезапуск.
|
|
968
|
+
*
|
|
969
|
+
* Клиент заменяет свой список целиком: досылать разницу здесь не из чего —
|
|
970
|
+
* инструменты не нумеруются и не досылаются, как сообщения.
|
|
971
|
+
*/
|
|
972
|
+
z9.object({
|
|
973
|
+
type: z9.literal("tools.changed"),
|
|
974
|
+
tools: z9.array(zToolInfo)
|
|
975
|
+
}),
|
|
976
|
+
z9.object({
|
|
977
|
+
type: z9.literal("plugin.status"),
|
|
978
|
+
plugin: zPluginInfo
|
|
979
|
+
})
|
|
980
|
+
]);
|
|
981
|
+
|
|
982
|
+
// ../protocol/dist/settings.js
|
|
983
|
+
import { z as z10 } from "zod";
|
|
984
|
+
var zSecretStatus = z10.object({
|
|
985
|
+
key: z10.string(),
|
|
986
|
+
set: z10.boolean(),
|
|
987
|
+
/** Последние символы значения. Присутствует, только если секрет задан. */
|
|
988
|
+
hint: z10.string().max(8).optional(),
|
|
989
|
+
updatedAt: zTimestamp.optional()
|
|
990
|
+
});
|
|
991
|
+
var zSettingsGetReq = z10.object({
|
|
992
|
+
/** Какие ключи вернуть. Пусто — все, к которым у устройства есть доступ. */
|
|
993
|
+
keys: z10.array(z10.string()).optional()
|
|
994
|
+
});
|
|
995
|
+
var zSettingsGetRes = z10.object({
|
|
996
|
+
values: z10.record(z10.unknown()),
|
|
997
|
+
secrets: z10.array(zSecretStatus)
|
|
998
|
+
});
|
|
999
|
+
var zSettingsSetReq = z10.object({
|
|
1000
|
+
values: z10.record(z10.unknown()).optional(),
|
|
1001
|
+
/**
|
|
1002
|
+
* Секреты пишутся только в одну сторону. `null` — стереть.
|
|
1003
|
+
* Прочитать записанное через протокол нельзя в принципе.
|
|
1004
|
+
*/
|
|
1005
|
+
secrets: z10.record(z10.string().nullable()).optional()
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
// ../protocol/dist/commands.js
|
|
1009
|
+
import { z as z11 } from "zod";
|
|
1010
|
+
var zSyncPullReq = z11.object({
|
|
1011
|
+
/** Последний seq, который клиент уже применил. 0 — холодный старт. */
|
|
1012
|
+
since: zCursor,
|
|
1013
|
+
limit: z11.number().int().positive().max(1e3).default(200)
|
|
1014
|
+
});
|
|
1015
|
+
var zSyncPullRes = z11.object({
|
|
1016
|
+
entries: z11.array(zJournalEntry),
|
|
1017
|
+
/** Курсор после применения выданной пачки. */
|
|
1018
|
+
cursor: zSeq,
|
|
1019
|
+
/** Есть ли ещё — тянуть до false, потом слушать `evt`. */
|
|
1020
|
+
hasMore: z11.boolean()
|
|
1021
|
+
});
|
|
1022
|
+
var zSyncSnapshotReq = z11.object({
|
|
1023
|
+
/** Сколько последних разговоров положить в снапшот. */
|
|
1024
|
+
conversationLimit: z11.number().int().positive().max(200).default(50)
|
|
1025
|
+
});
|
|
1026
|
+
var zSyncSnapshotRes = z11.object({
|
|
1027
|
+
conversations: z11.array(zConversation),
|
|
1028
|
+
facts: z11.array(zFact),
|
|
1029
|
+
observations: z11.array(zObservation),
|
|
1030
|
+
devices: z11.array(zDevice),
|
|
1031
|
+
tools: z11.array(zToolInfo),
|
|
1032
|
+
/** Вместе с рабочим состоянием: журнал его не хранит, а знать его нужно сразу. */
|
|
1033
|
+
plugins: z11.array(zPluginInfo),
|
|
1034
|
+
routines: z11.array(zRoutine),
|
|
1035
|
+
cursor: zSeq
|
|
1036
|
+
});
|
|
1037
|
+
var zConversationCreateReq = z11.object({
|
|
1038
|
+
title: z11.string().max(200).optional()
|
|
1039
|
+
});
|
|
1040
|
+
var zConversationCreateRes = z11.object({ conversation: zConversation });
|
|
1041
|
+
var zConversationRenameReq = z11.object({ id: zId, title: z11.string().max(200) });
|
|
1042
|
+
var zConversationArchiveReq = z11.object({ id: zId, archived: z11.boolean() });
|
|
1043
|
+
var zConversationDeleteReq = z11.object({ id: zId });
|
|
1044
|
+
var zOkRes = z11.object({ ok: z11.literal(true) });
|
|
1045
|
+
var zMessageHistoryReq = z11.object({
|
|
1046
|
+
conversationId: zId,
|
|
1047
|
+
/** Грузить сообщения старше этого id. Пусто — с конца. */
|
|
1048
|
+
before: zId.optional(),
|
|
1049
|
+
limit: z11.number().int().positive().max(200).default(50)
|
|
1050
|
+
});
|
|
1051
|
+
var zMessageHistoryRes = z11.object({
|
|
1052
|
+
messages: z11.array(zMessage),
|
|
1053
|
+
hasMore: z11.boolean()
|
|
1054
|
+
});
|
|
1055
|
+
var zSearchReq = z11.object({
|
|
1056
|
+
query: z11.string().min(1).max(200),
|
|
1057
|
+
limit: z11.number().int().positive().max(100).default(40)
|
|
1058
|
+
});
|
|
1059
|
+
var zSearchRes = z11.object({
|
|
1060
|
+
hits: z11.array(z11.object({
|
|
1061
|
+
conversationId: zId,
|
|
1062
|
+
conversationTitle: z11.string(),
|
|
1063
|
+
messageId: zId,
|
|
1064
|
+
role: z11.string(),
|
|
1065
|
+
createdAt: zTimestamp,
|
|
1066
|
+
/** Найденные слова обёрнуты в «ёлочки» — клиент подсвечивает по ним. */
|
|
1067
|
+
snippet: z11.string()
|
|
1068
|
+
}))
|
|
1069
|
+
});
|
|
1070
|
+
var zMessageSendReq = z11.object({
|
|
1071
|
+
conversationId: zId,
|
|
1072
|
+
parts: z11.array(zContentPart).min(1),
|
|
1073
|
+
/** Потолок расхода на этот прогон. Пусто — берётся из настроек ядра. */
|
|
1074
|
+
budgetTokens: z11.number().int().positive().optional(),
|
|
1075
|
+
/** Разрешённые инструменты сверх дефолтных прав устройства. */
|
|
1076
|
+
allowTools: z11.array(z11.string()).optional()
|
|
1077
|
+
});
|
|
1078
|
+
var zMessageSendRes = z11.object({
|
|
1079
|
+
runId: zId,
|
|
1080
|
+
/** Сообщение пользователя, уже записанное в журнал. */
|
|
1081
|
+
message: zMessage
|
|
1082
|
+
});
|
|
1083
|
+
var zMessageEditReq = z11.object({
|
|
1084
|
+
id: zId,
|
|
1085
|
+
parts: z11.array(zContentPart).min(1)
|
|
1086
|
+
});
|
|
1087
|
+
var zMessageRegenerateReq = z11.object({ conversationId: zId });
|
|
1088
|
+
var zRunCancelReq = z11.object({ runId: zId });
|
|
1089
|
+
var zPermissionResolveReq = z11.object({
|
|
1090
|
+
requestId: zId,
|
|
1091
|
+
decision: zPermissionDecision.exclude(["expired"])
|
|
1092
|
+
});
|
|
1093
|
+
var zToolListRes = z11.object({ tools: z11.array(zToolInfo) });
|
|
1094
|
+
var zToolSetEnabledReq = z11.object({ name: z11.string(), enabled: z11.boolean() });
|
|
1095
|
+
var zRoutineListRes = z11.object({ routines: z11.array(zRoutine) });
|
|
1096
|
+
var zRoutineRes = z11.object({ routine: zRoutine });
|
|
1097
|
+
var zProviderListRes = z11.object({ providers: z11.array(zProviderInfo) });
|
|
1098
|
+
var zPluginListRes = z11.object({ plugins: z11.array(zPluginInfo) });
|
|
1099
|
+
var zPluginCatalogReq = z11.object({
|
|
1100
|
+
/** Человек нажал «обновить» — идти в сеть, не глядя на свежесть кэша. */
|
|
1101
|
+
refresh: z11.boolean().default(false)
|
|
1102
|
+
});
|
|
1103
|
+
var zPluginCatalogRes = z11.object({
|
|
1104
|
+
entries: z11.array(zCatalogEntry),
|
|
1105
|
+
/**
|
|
1106
|
+
* Откуда взялся этот список.
|
|
1107
|
+
*
|
|
1108
|
+
* Показывается человеку: каталог из сборки полугодовой давности и свежий
|
|
1109
|
+
* выглядят одинаково, и без пометки непонятно, почему в нём нет того, что
|
|
1110
|
+
* ему обещали.
|
|
1111
|
+
*/
|
|
1112
|
+
origin: z11.enum(["network", "cache", "bundled"]),
|
|
1113
|
+
fetchedAt: zTimestamp.optional()
|
|
1114
|
+
});
|
|
1115
|
+
var zPluginInstallReq = z11.object({ source: zPluginSource });
|
|
1116
|
+
var zPluginInstallRes = z11.object({ plugin: zPluginInfo });
|
|
1117
|
+
var zPluginIdReq = z11.object({ id: zPluginId });
|
|
1118
|
+
var zPluginSetEnabledReq = z11.object({ id: zPluginId, enabled: z11.boolean() });
|
|
1119
|
+
var zPluginConfigureReq = z11.object({
|
|
1120
|
+
id: zPluginId,
|
|
1121
|
+
values: z11.record(z11.unknown()).default({}),
|
|
1122
|
+
secrets: z11.record(z11.string().nullable()).default({})
|
|
1123
|
+
});
|
|
1124
|
+
var zSkillSetEnabledReq = z11.object({ id: z11.string(), enabled: z11.boolean() });
|
|
1125
|
+
var zPluginLogsReq = z11.object({
|
|
1126
|
+
id: zPluginId,
|
|
1127
|
+
limit: z11.number().int().positive().max(500).default(200)
|
|
1128
|
+
});
|
|
1129
|
+
var zPluginLogsRes = z11.object({
|
|
1130
|
+
lines: z11.array(z11.object({ at: z11.string(), level: z11.string(), text: z11.string() }))
|
|
1131
|
+
});
|
|
1132
|
+
var zDeviceListRes = z11.object({ devices: z11.array(zDevice) });
|
|
1133
|
+
var zDeviceRevokeReq = z11.object({ id: zId });
|
|
1134
|
+
var zPairBeginReq = z11.object({
|
|
1135
|
+
name: z11.string().max(100),
|
|
1136
|
+
platform: zDevicePlatform,
|
|
1137
|
+
scopes: z11.array(zScope),
|
|
1138
|
+
ttlSeconds: z11.number().int().positive().max(3600).default(300)
|
|
1139
|
+
});
|
|
1140
|
+
var zPairBeginRes = z11.object({
|
|
1141
|
+
code: z11.string(),
|
|
1142
|
+
expiresInSeconds: z11.number().int().positive()
|
|
1143
|
+
});
|
|
1144
|
+
var zFactUpsertReq = z11.object({ key: z11.string().max(200), value: z11.string().max(4e3) });
|
|
1145
|
+
var zFactUpsertRes = z11.object({ fact: zFact });
|
|
1146
|
+
var zFactForgetReq = z11.object({ id: zId });
|
|
1147
|
+
var zObservationForgetReq = z11.object({ id: zId });
|
|
1148
|
+
var zPluginActionReq = z11.object({
|
|
1149
|
+
id: zPluginId,
|
|
1150
|
+
action: z11.string().min(1).max(60)
|
|
1151
|
+
});
|
|
1152
|
+
var zPluginActionRes = z11.object({
|
|
1153
|
+
ok: z11.boolean(),
|
|
1154
|
+
message: z11.string().max(2e3)
|
|
1155
|
+
});
|
|
1156
|
+
var zTelegramLoginReq = z11.object({
|
|
1157
|
+
step: z11.enum(["phone", "code", "password", "cancel"]),
|
|
1158
|
+
value: z11.string().max(200).default("")
|
|
1159
|
+
});
|
|
1160
|
+
var zTelegramLoginRes = z11.object({
|
|
1161
|
+
state: z11.enum(["code_sent", "password_needed", "done", "cancelled"]),
|
|
1162
|
+
/** Куда пришёл код — в телеграм или сообщением. Только для подсказки. */
|
|
1163
|
+
hint: z11.string().optional(),
|
|
1164
|
+
/** Как зовут вошедший аккаунт. Появляется на последнем шаге. */
|
|
1165
|
+
name: z11.string().optional()
|
|
1166
|
+
});
|
|
1167
|
+
var zTelegramStatusRes = z11.object({
|
|
1168
|
+
/** Поднят ли бот: у него есть токен и он на связи. */
|
|
1169
|
+
bot: z11.boolean(),
|
|
1170
|
+
/** Работает ли юзербот и под каким именем. */
|
|
1171
|
+
user: z11.boolean(),
|
|
1172
|
+
userName: z11.string().optional()
|
|
1173
|
+
});
|
|
1174
|
+
var zUsageSummaryReq = z11.object({
|
|
1175
|
+
/** Начало окна, ISO-8601. Пусто — с начала суток по времени ядра. */
|
|
1176
|
+
since: z11.string().datetime().optional()
|
|
1177
|
+
});
|
|
1178
|
+
var zUsageSummaryRes = z11.object({
|
|
1179
|
+
inputTokens: z11.number().int().nonnegative(),
|
|
1180
|
+
cachedInputTokens: z11.number().int().nonnegative(),
|
|
1181
|
+
outputTokens: z11.number().int().nonnegative(),
|
|
1182
|
+
costUsd: z11.number().nonnegative(),
|
|
1183
|
+
runs: z11.number().int().nonnegative(),
|
|
1184
|
+
/** Разбивка по моделям — видно, куда именно утекает бюджет. */
|
|
1185
|
+
byModel: z11.array(z11.object({
|
|
1186
|
+
provider: z11.string(),
|
|
1187
|
+
model: z11.string(),
|
|
1188
|
+
inputTokens: z11.number().int().nonnegative(),
|
|
1189
|
+
cachedInputTokens: z11.number().int().nonnegative(),
|
|
1190
|
+
outputTokens: z11.number().int().nonnegative(),
|
|
1191
|
+
costUsd: z11.number().nonnegative()
|
|
1192
|
+
}))
|
|
1193
|
+
});
|
|
1194
|
+
var commands = {
|
|
1195
|
+
"sync.pull": { req: zSyncPullReq, res: zSyncPullRes },
|
|
1196
|
+
"sync.snapshot": { req: zSyncSnapshotReq, res: zSyncSnapshotRes },
|
|
1197
|
+
"conversation.create": { req: zConversationCreateReq, res: zConversationCreateRes },
|
|
1198
|
+
"conversation.rename": { req: zConversationRenameReq, res: zOkRes },
|
|
1199
|
+
"conversation.archive": { req: zConversationArchiveReq, res: zOkRes },
|
|
1200
|
+
"conversation.delete": { req: zConversationDeleteReq, res: zOkRes },
|
|
1201
|
+
"message.history": { req: zMessageHistoryReq, res: zMessageHistoryRes },
|
|
1202
|
+
"message.search": { req: zSearchReq, res: zSearchRes },
|
|
1203
|
+
"context.report": { req: z11.object({ conversationId: zId }), res: zContextReport },
|
|
1204
|
+
"message.send": { req: zMessageSendReq, res: zMessageSendRes },
|
|
1205
|
+
"message.edit": { req: zMessageEditReq, res: zMessageSendRes },
|
|
1206
|
+
"message.regenerate": { req: zMessageRegenerateReq, res: zMessageSendRes },
|
|
1207
|
+
"run.cancel": { req: zRunCancelReq, res: zOkRes },
|
|
1208
|
+
"permission.resolve": { req: zPermissionResolveReq, res: zOkRes },
|
|
1209
|
+
"tool.list": { req: z11.object({}), res: zToolListRes },
|
|
1210
|
+
"tool.setEnabled": { req: zToolSetEnabledReq, res: zOkRes },
|
|
1211
|
+
"provider.list": { req: z11.object({}), res: zProviderListRes },
|
|
1212
|
+
"routine.list": { req: z11.object({}), res: zRoutineListRes },
|
|
1213
|
+
"routine.compile": { req: zRoutineCompileReq, res: zRoutineCompileRes },
|
|
1214
|
+
"routine.create": { req: zRoutineCreateReq, res: zRoutineRes },
|
|
1215
|
+
"routine.update": { req: zRoutineUpdateReq, res: zRoutineRes },
|
|
1216
|
+
"routine.delete": { req: z11.object({ id: zId }), res: zOkRes },
|
|
1217
|
+
"routine.runNow": { req: z11.object({ id: zId }), res: zRoutineRes },
|
|
1218
|
+
"routine.runs": { req: zRoutineRunsReq, res: zRoutineRunsRes },
|
|
1219
|
+
"plugin.list": { req: z11.object({}), res: zPluginListRes },
|
|
1220
|
+
"plugin.catalog": { req: zPluginCatalogReq, res: zPluginCatalogRes },
|
|
1221
|
+
"plugin.install": { req: zPluginInstallReq, res: zPluginInstallRes },
|
|
1222
|
+
"plugin.remove": { req: zPluginIdReq, res: zOkRes },
|
|
1223
|
+
"plugin.setEnabled": { req: zPluginSetEnabledReq, res: zOkRes },
|
|
1224
|
+
"plugin.reload": { req: zPluginIdReq, res: zOkRes },
|
|
1225
|
+
"plugin.update": { req: zPluginIdReq, res: zPluginInstallRes },
|
|
1226
|
+
"plugin.configure": { req: zPluginConfigureReq, res: zOkRes },
|
|
1227
|
+
"plugin.action": { req: zPluginActionReq, res: zPluginActionRes },
|
|
1228
|
+
"plugin.logs": { req: zPluginLogsReq, res: zPluginLogsRes },
|
|
1229
|
+
"skill.setEnabled": { req: zSkillSetEnabledReq, res: zOkRes },
|
|
1230
|
+
"settings.get": { req: zSettingsGetReq, res: zSettingsGetRes },
|
|
1231
|
+
"settings.set": { req: zSettingsSetReq, res: zOkRes },
|
|
1232
|
+
"device.list": { req: z11.object({}), res: zDeviceListRes },
|
|
1233
|
+
"device.revoke": { req: zDeviceRevokeReq, res: zOkRes },
|
|
1234
|
+
"device.pairBegin": { req: zPairBeginReq, res: zPairBeginRes },
|
|
1235
|
+
"fact.upsert": { req: zFactUpsertReq, res: zFactUpsertRes },
|
|
1236
|
+
"fact.forget": { req: zFactForgetReq, res: zOkRes },
|
|
1237
|
+
"observation.forget": { req: zObservationForgetReq, res: zOkRes },
|
|
1238
|
+
"telegram.login": { req: zTelegramLoginReq, res: zTelegramLoginRes },
|
|
1239
|
+
"telegram.logout": { req: z11.object({}), res: zOkRes },
|
|
1240
|
+
"telegram.status": { req: z11.object({}), res: zTelegramStatusRes },
|
|
1241
|
+
"usage.summary": { req: zUsageSummaryReq, res: zUsageSummaryRes }
|
|
1242
|
+
};
|
|
1243
|
+
var commandNames = Object.keys(commands);
|
|
1244
|
+
|
|
1245
|
+
// ../protocol/dist/envelope.js
|
|
1246
|
+
import { z as z12 } from "zod";
|
|
1247
|
+
var zClientFrame = z12.discriminatedUnion("t", [
|
|
1248
|
+
/** Вызов команды. `id` возвращается в ответном кадре. */
|
|
1249
|
+
z12.object({
|
|
1250
|
+
t: z12.literal("req"),
|
|
1251
|
+
id: zId,
|
|
1252
|
+
cmd: z12.string(),
|
|
1253
|
+
payload: z12.unknown()
|
|
1254
|
+
}),
|
|
1255
|
+
/**
|
|
1256
|
+
* Подтверждение позиции в журнале. Ядро запоминает курсор устройства, чтобы
|
|
1257
|
+
* при следующем подключении сразу знать, с чего досылать.
|
|
1258
|
+
*/
|
|
1259
|
+
z12.object({
|
|
1260
|
+
t: z12.literal("ack"),
|
|
1261
|
+
cursor: zCursor
|
|
1262
|
+
}),
|
|
1263
|
+
z12.object({ t: z12.literal("ping") })
|
|
1264
|
+
]);
|
|
1265
|
+
var zCoreInfo = z12.object({
|
|
1266
|
+
/** Постоянный id этой установки ядра. Клиент по нему отличает «переехал на другое ядро». */
|
|
1267
|
+
coreId: zId,
|
|
1268
|
+
version: z12.string(),
|
|
1269
|
+
/** Как ядро запущено: встроено в десктоп или отдельным сервисом. */
|
|
1270
|
+
mode: z12.enum(["embedded", "standalone"]),
|
|
1271
|
+
/** Права выданного устройству токена — клиент прячет то, что всё равно не разрешат. */
|
|
1272
|
+
scopes: z12.array(zScope)
|
|
1273
|
+
});
|
|
1274
|
+
var zServerFrame = z12.discriminatedUnion("t", [
|
|
1275
|
+
/** Первый кадр после подключения. До него клиент не шлёт ничего, кроме ping. */
|
|
1276
|
+
z12.object({
|
|
1277
|
+
t: z12.literal("hello"),
|
|
1278
|
+
protocol: z12.number().int(),
|
|
1279
|
+
/**
|
|
1280
|
+
* Ревизия контракта у ядра. `default` здесь обязателен: ядра, выпущенные
|
|
1281
|
+
* до её появления, этого поля не шлют, и без умолчания разбор кадра упал
|
|
1282
|
+
* бы ровно на тех, ради кого она и заводится.
|
|
1283
|
+
*/
|
|
1284
|
+
revision: z12.number().int().default(0),
|
|
1285
|
+
core: zCoreInfo,
|
|
1286
|
+
/** Текущая вершина журнала — сразу видно, насколько клиент отстал. */
|
|
1287
|
+
head: zSeq
|
|
1288
|
+
}),
|
|
1289
|
+
z12.object({
|
|
1290
|
+
t: z12.literal("res"),
|
|
1291
|
+
id: zId,
|
|
1292
|
+
payload: z12.unknown()
|
|
1293
|
+
}),
|
|
1294
|
+
z12.object({
|
|
1295
|
+
t: z12.literal("err"),
|
|
1296
|
+
id: zId,
|
|
1297
|
+
error: zProtocolError
|
|
1298
|
+
}),
|
|
1299
|
+
/** Новая запись журнала. Клиент применяет её и двигает курсор. */
|
|
1300
|
+
z12.object({
|
|
1301
|
+
t: z12.literal("evt"),
|
|
1302
|
+
seq: zSeq,
|
|
1303
|
+
at: zTimestamp,
|
|
1304
|
+
event: zJournalEvent
|
|
1305
|
+
}),
|
|
1306
|
+
/** Эфемерика: поток токенов, фазы, счётчик расхода. Потеря не страшна. */
|
|
1307
|
+
z12.object({
|
|
1308
|
+
t: z12.literal("sig"),
|
|
1309
|
+
signal: zSignal
|
|
1310
|
+
}),
|
|
1311
|
+
z12.object({ t: z12.literal("pong") })
|
|
1312
|
+
]);
|
|
1313
|
+
|
|
1314
|
+
// ../core/dist/agent/Orchestrator.js
|
|
1315
|
+
var PERMISSION_TTL_MS = 5 * 60 * 1e3;
|
|
1316
|
+
|
|
1317
|
+
// ../core/dist/agent/Impulse.js
|
|
1318
|
+
var TICK_MS = 5 * 6e4;
|
|
1319
|
+
|
|
1320
|
+
// ../core/dist/memory/vectors.js
|
|
1321
|
+
var MISSING = Number.POSITIVE_INFINITY;
|
|
1322
|
+
|
|
1323
|
+
// ../core/dist/plugins/CatalogSource.js
|
|
1324
|
+
var FRESH_FOR_MS = 6 * 60 * 60 * 1e3;
|
|
1325
|
+
var MAX_BYTES = 2 * 1024 * 1024;
|
|
1326
|
+
|
|
1327
|
+
// ../core/dist/plugins/install.js
|
|
1328
|
+
import { execFile } from "node:child_process";
|
|
1329
|
+
|
|
1330
|
+
// ../core/dist/plugins/archive.js
|
|
1331
|
+
var MAX_TOTAL_BYTES = 200 * 1024 * 1024;
|
|
1332
|
+
var MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
1333
|
+
|
|
1334
|
+
// ../core/dist/plugins/install.js
|
|
1335
|
+
import { promisify } from "node:util";
|
|
1336
|
+
|
|
1337
|
+
// ../core/dist/plugins/host.js
|
|
1338
|
+
import fs from "node:fs";
|
|
1339
|
+
import path from "node:path";
|
|
1340
|
+
import { pathToFileURL } from "node:url";
|
|
1341
|
+
|
|
1342
|
+
// ../core/dist/plugins/contract.js
|
|
1343
|
+
var TO_PLUGIN = {
|
|
1344
|
+
activate: "activate",
|
|
1345
|
+
deactivate: "deactivate",
|
|
1346
|
+
toolExecute: "tool.execute",
|
|
1347
|
+
contribute: "context.contribute",
|
|
1348
|
+
providerChat: "provider.chat",
|
|
1349
|
+
jobRun: "job.run",
|
|
1350
|
+
actionRun: "action.run",
|
|
1351
|
+
/** События: ответа нет. */
|
|
1352
|
+
settingsChanged: "settings.changed",
|
|
1353
|
+
journalEvent: "journal.event"
|
|
1354
|
+
};
|
|
1355
|
+
var TO_CORE = {
|
|
1356
|
+
registerTool: "tool.register",
|
|
1357
|
+
unregisterTool: "tool.unregister",
|
|
1358
|
+
registerContributor: "context.register",
|
|
1359
|
+
unregisterContributor: "context.unregister",
|
|
1360
|
+
registerProvider: "provider.register",
|
|
1361
|
+
unregisterProvider: "provider.unregister",
|
|
1362
|
+
addSkill: "skill.add",
|
|
1363
|
+
requestPermission: "permission.request",
|
|
1364
|
+
getSettings: "settings.get",
|
|
1365
|
+
setSettings: "settings.set",
|
|
1366
|
+
listFacts: "fact.list",
|
|
1367
|
+
upsertFact: "fact.upsert",
|
|
1368
|
+
writeBlob: "blob.write",
|
|
1369
|
+
ask: "model.ask",
|
|
1370
|
+
notify: "notify",
|
|
1371
|
+
setStatus: "status.set",
|
|
1372
|
+
searchHistory: "history.search",
|
|
1373
|
+
log: "log"
|
|
1374
|
+
};
|
|
1375
|
+
var HOST_READY = "host.ready";
|
|
1376
|
+
|
|
1377
|
+
// ../core/dist/plugins/rpc.js
|
|
1378
|
+
var RpcError = class extends Error {
|
|
1379
|
+
code;
|
|
1380
|
+
constructor(message, code = "plugin_error") {
|
|
1381
|
+
super(message);
|
|
1382
|
+
this.code = code;
|
|
1383
|
+
this.name = "RpcError";
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
var RpcPeer = class {
|
|
1387
|
+
send;
|
|
1388
|
+
nextId = 1;
|
|
1389
|
+
pending = /* @__PURE__ */ new Map();
|
|
1390
|
+
handlers = /* @__PURE__ */ new Map();
|
|
1391
|
+
events = /* @__PURE__ */ new Map();
|
|
1392
|
+
/** Запросы, которые сейчас выполняем мы — по ним может прийти `cancel`. */
|
|
1393
|
+
inflight = /* @__PURE__ */ new Map();
|
|
1394
|
+
closed = null;
|
|
1395
|
+
constructor(send) {
|
|
1396
|
+
this.send = send;
|
|
1397
|
+
}
|
|
1398
|
+
// ─── Приём ────────────────────────────────────────────────────────────────
|
|
1399
|
+
handle(method, handler) {
|
|
1400
|
+
this.handlers.set(method, handler);
|
|
1401
|
+
}
|
|
1402
|
+
onEvent(method, handler) {
|
|
1403
|
+
this.events.set(method, handler);
|
|
1404
|
+
}
|
|
1405
|
+
/** Скормить входящий кадр. Вызывается транспортом. */
|
|
1406
|
+
receive(frame) {
|
|
1407
|
+
switch (frame.t) {
|
|
1408
|
+
case "req":
|
|
1409
|
+
void this.dispatch(frame);
|
|
1410
|
+
return;
|
|
1411
|
+
case "res": {
|
|
1412
|
+
const pending = this.pending.get(frame.id);
|
|
1413
|
+
if (!pending)
|
|
1414
|
+
return;
|
|
1415
|
+
this.pending.delete(frame.id);
|
|
1416
|
+
pending.detach();
|
|
1417
|
+
if (pending.timer)
|
|
1418
|
+
clearTimeout(pending.timer);
|
|
1419
|
+
if (frame.ok)
|
|
1420
|
+
pending.resolve(frame.result);
|
|
1421
|
+
else
|
|
1422
|
+
pending.reject(new RpcError(frame.error.message, frame.error.code));
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
case "chunk":
|
|
1426
|
+
this.pending.get(frame.id)?.onChunk?.(frame.value);
|
|
1427
|
+
return;
|
|
1428
|
+
case "evt": {
|
|
1429
|
+
const handler = this.events.get(frame.method);
|
|
1430
|
+
if (handler)
|
|
1431
|
+
handler(frame.params);
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
case "cancel":
|
|
1435
|
+
this.inflight.get(frame.id)?.abort();
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
async dispatch(frame) {
|
|
1440
|
+
const handler = this.handlers.get(frame.method);
|
|
1441
|
+
if (!handler) {
|
|
1442
|
+
this.send({
|
|
1443
|
+
t: "res",
|
|
1444
|
+
id: frame.id,
|
|
1445
|
+
ok: false,
|
|
1446
|
+
error: { message: `\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 \u043C\u0435\u0442\u043E\u0434 ${frame.method}`, code: "unknown_method" }
|
|
1447
|
+
});
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
const controller = new AbortController();
|
|
1451
|
+
this.inflight.set(frame.id, controller);
|
|
1452
|
+
try {
|
|
1453
|
+
const result = await handler(frame.params, {
|
|
1454
|
+
signal: controller.signal,
|
|
1455
|
+
push: (value) => this.send({ t: "chunk", id: frame.id, value })
|
|
1456
|
+
});
|
|
1457
|
+
this.send({ t: "res", id: frame.id, ok: true, result: result ?? null });
|
|
1458
|
+
} catch (error) {
|
|
1459
|
+
this.send({
|
|
1460
|
+
t: "res",
|
|
1461
|
+
id: frame.id,
|
|
1462
|
+
ok: false,
|
|
1463
|
+
error: {
|
|
1464
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1465
|
+
...error instanceof RpcError ? { code: error.code } : {}
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
} finally {
|
|
1469
|
+
this.inflight.delete(frame.id);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
// ─── Отправка ─────────────────────────────────────────────────────────────
|
|
1473
|
+
call(method, params = {}, options = {}) {
|
|
1474
|
+
if (this.closed)
|
|
1475
|
+
return Promise.reject(this.closed);
|
|
1476
|
+
const id = this.nextId++;
|
|
1477
|
+
return new Promise((resolve, reject) => {
|
|
1478
|
+
const abort = () => {
|
|
1479
|
+
if (!this.pending.delete(id))
|
|
1480
|
+
return;
|
|
1481
|
+
this.send({ t: "cancel", id });
|
|
1482
|
+
reject(new RpcError("\u0412\u044B\u0437\u043E\u0432 \u043E\u0442\u043C\u0435\u043D\u0451\u043D", "cancelled"));
|
|
1483
|
+
};
|
|
1484
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
1485
|
+
const pending = {
|
|
1486
|
+
resolve,
|
|
1487
|
+
reject,
|
|
1488
|
+
detach: () => options.signal?.removeEventListener("abort", abort),
|
|
1489
|
+
...options.onChunk ? { onChunk: options.onChunk } : {}
|
|
1490
|
+
};
|
|
1491
|
+
if (options.timeoutMs && options.timeoutMs > 0) {
|
|
1492
|
+
pending.timer = setTimeout(() => {
|
|
1493
|
+
if (!this.pending.delete(id))
|
|
1494
|
+
return;
|
|
1495
|
+
pending.detach();
|
|
1496
|
+
reject(new RpcError(`\u041E\u0442\u0432\u0435\u0442 \u043D\u0435 \u043F\u0440\u0438\u0448\u0451\u043B \u0437\u0430 ${options.timeoutMs} \u043C\u0441`, "timeout"));
|
|
1497
|
+
}, options.timeoutMs);
|
|
1498
|
+
pending.timer.unref?.();
|
|
1499
|
+
}
|
|
1500
|
+
this.pending.set(id, pending);
|
|
1501
|
+
try {
|
|
1502
|
+
this.send({ t: "req", id, method, params });
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
this.pending.delete(id);
|
|
1505
|
+
pending.detach();
|
|
1506
|
+
if (pending.timer)
|
|
1507
|
+
clearTimeout(pending.timer);
|
|
1508
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1509
|
+
}
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
emit(method, params = {}) {
|
|
1513
|
+
if (this.closed)
|
|
1514
|
+
return;
|
|
1515
|
+
this.send({ t: "evt", method, params });
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Закрыть пира: все незавершённые вызовы падают, новые сразу отклоняются.
|
|
1519
|
+
*
|
|
1520
|
+
* Без этого падение процесса плагина оставляло бы висеть промисы навсегда, и
|
|
1521
|
+
* прогон агента замирал бы молча — худший из возможных способов сломаться.
|
|
1522
|
+
*/
|
|
1523
|
+
dispose(reason) {
|
|
1524
|
+
this.closed = reason;
|
|
1525
|
+
for (const [id, pending] of this.pending) {
|
|
1526
|
+
this.pending.delete(id);
|
|
1527
|
+
pending.detach();
|
|
1528
|
+
if (pending.timer)
|
|
1529
|
+
clearTimeout(pending.timer);
|
|
1530
|
+
pending.reject(reason);
|
|
1531
|
+
}
|
|
1532
|
+
for (const controller of this.inflight.values())
|
|
1533
|
+
controller.abort();
|
|
1534
|
+
this.inflight.clear();
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1538
|
+
// ../core/dist/plugins/host.js
|
|
1539
|
+
var PLUGIN_API_VERSION = 1;
|
|
1540
|
+
function startPluginHost() {
|
|
1541
|
+
if (typeof process.send !== "function") {
|
|
1542
|
+
throw new Error("\u0425\u043E\u0441\u0442 \u043F\u043B\u0430\u0433\u0438\u043D\u0430 \u0437\u0430\u043F\u0443\u0449\u0435\u043D \u0431\u0435\u0437 \u043A\u0430\u043D\u0430\u043B\u0430 \u043A \u044F\u0434\u0440\u0443 \u2014 \u0442\u0430\u043A \u043E\u043D \u0431\u0435\u0441\u043F\u043E\u043B\u0435\u0437\u0435\u043D");
|
|
1543
|
+
}
|
|
1544
|
+
const send = process.send.bind(process);
|
|
1545
|
+
const peer = new RpcPeer((frame) => send(frame));
|
|
1546
|
+
process.on("message", (frame) => peer.receive(frame));
|
|
1547
|
+
const tools = /* @__PURE__ */ new Map();
|
|
1548
|
+
const contributors = /* @__PURE__ */ new Map();
|
|
1549
|
+
const providers = /* @__PURE__ */ new Map();
|
|
1550
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
1551
|
+
const actions = /* @__PURE__ */ new Map();
|
|
1552
|
+
const journalListeners = [];
|
|
1553
|
+
const settingsListeners = [];
|
|
1554
|
+
let settings = {};
|
|
1555
|
+
let loaded = null;
|
|
1556
|
+
function buildApi(params) {
|
|
1557
|
+
const log = (level) => (message, data) => peer.emit(TO_CORE.log, { level, message, ...data ? { data } : {} });
|
|
1558
|
+
return {
|
|
1559
|
+
id: params.pluginId,
|
|
1560
|
+
dir: params.dir,
|
|
1561
|
+
dataDir: params.dataDir,
|
|
1562
|
+
log: { debug: log("debug"), info: log("info"), warn: log("warn"), error: log("error") },
|
|
1563
|
+
settings: {
|
|
1564
|
+
all: () => ({ ...settings }),
|
|
1565
|
+
get: (key) => settings[key],
|
|
1566
|
+
set: async (values) => {
|
|
1567
|
+
settings = { ...settings, ...values };
|
|
1568
|
+
await peer.call(TO_CORE.setSettings, { values });
|
|
1569
|
+
},
|
|
1570
|
+
onChange: (listener) => settingsListeners.push(listener),
|
|
1571
|
+
/**
|
|
1572
|
+
* Разбор с умолчанием.
|
|
1573
|
+
*
|
|
1574
|
+
* Живёт здесь, а не у автора плагина: `Number(значение ?? 30)` на
|
|
1575
|
+
* пустой строке даёт ноль, а не тридцать, и галочка из формы приходит
|
|
1576
|
+
* строкой `"false"`, которая истинна. Каждый плагин переписывал бы это
|
|
1577
|
+
* заново и ошибался бы одинаково.
|
|
1578
|
+
*/
|
|
1579
|
+
text: (key, fallback) => {
|
|
1580
|
+
const value = settings[key];
|
|
1581
|
+
if (value === void 0 || value === null)
|
|
1582
|
+
return fallback;
|
|
1583
|
+
const text = String(value).trim();
|
|
1584
|
+
return text || fallback;
|
|
1585
|
+
},
|
|
1586
|
+
number: (key, fallback) => {
|
|
1587
|
+
const value = Number(settings[key]);
|
|
1588
|
+
return Number.isFinite(value) ? value : fallback;
|
|
1589
|
+
},
|
|
1590
|
+
flag: (key, fallback) => {
|
|
1591
|
+
const value = settings[key];
|
|
1592
|
+
if (value === void 0 || value === null || value === "")
|
|
1593
|
+
return fallback;
|
|
1594
|
+
if (typeof value === "string")
|
|
1595
|
+
return value !== "false" && value !== "0";
|
|
1596
|
+
return Boolean(value);
|
|
1597
|
+
},
|
|
1598
|
+
lines: (key) => String(settings[key] ?? "").split("\n").map((line) => line.trim()).filter(Boolean)
|
|
1599
|
+
},
|
|
1600
|
+
tools: {
|
|
1601
|
+
register: async (tool) => {
|
|
1602
|
+
tools.set(tool.name, tool);
|
|
1603
|
+
await peer.call(TO_CORE.registerTool, {
|
|
1604
|
+
name: tool.name,
|
|
1605
|
+
title: tool.title,
|
|
1606
|
+
description: tool.description,
|
|
1607
|
+
tier: tool.tier,
|
|
1608
|
+
parameters: tool.parameters,
|
|
1609
|
+
...tool.deferred ? { deferred: true } : {},
|
|
1610
|
+
...tool.previewLimit ? { previewLimit: tool.previewLimit } : {}
|
|
1611
|
+
});
|
|
1612
|
+
},
|
|
1613
|
+
unregister: async (name) => {
|
|
1614
|
+
tools.delete(name);
|
|
1615
|
+
await peer.call(TO_CORE.unregisterTool, { name });
|
|
1616
|
+
}
|
|
1617
|
+
},
|
|
1618
|
+
context: {
|
|
1619
|
+
contribute: async (name, stability, contribute) => {
|
|
1620
|
+
contributors.set(name, contribute);
|
|
1621
|
+
await peer.call(TO_CORE.registerContributor, { name, stability });
|
|
1622
|
+
},
|
|
1623
|
+
remove: async (name) => {
|
|
1624
|
+
contributors.delete(name);
|
|
1625
|
+
await peer.call(TO_CORE.unregisterContributor, { name });
|
|
1626
|
+
}
|
|
1627
|
+
},
|
|
1628
|
+
providers: {
|
|
1629
|
+
register: async (provider) => {
|
|
1630
|
+
providers.set(provider.id, provider);
|
|
1631
|
+
await peer.call(TO_CORE.registerProvider, {
|
|
1632
|
+
id: provider.id,
|
|
1633
|
+
label: provider.label,
|
|
1634
|
+
supportsPromptCache: provider.supportsPromptCache,
|
|
1635
|
+
models: provider.models
|
|
1636
|
+
});
|
|
1637
|
+
},
|
|
1638
|
+
unregister: async (id) => {
|
|
1639
|
+
providers.delete(id);
|
|
1640
|
+
await peer.call(TO_CORE.unregisterProvider, { id });
|
|
1641
|
+
}
|
|
1642
|
+
},
|
|
1643
|
+
skills: {
|
|
1644
|
+
add: async (skill) => {
|
|
1645
|
+
await peer.call(TO_CORE.addSkill, skill);
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
jobs: {
|
|
1649
|
+
on: (name, run2) => jobs.set(name, run2)
|
|
1650
|
+
},
|
|
1651
|
+
actions: {
|
|
1652
|
+
on: (name, run2) => actions.set(name, run2)
|
|
1653
|
+
},
|
|
1654
|
+
journal: {
|
|
1655
|
+
on: (listener) => journalListeners.push(listener)
|
|
1656
|
+
},
|
|
1657
|
+
memory: {
|
|
1658
|
+
facts: () => peer.call(TO_CORE.listFacts),
|
|
1659
|
+
remember: async (key, value) => {
|
|
1660
|
+
await peer.call(TO_CORE.upsertFact, { key, value });
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
1663
|
+
blobs: {
|
|
1664
|
+
write: async ({ data, mime, name }) => {
|
|
1665
|
+
const buffer = typeof data === "string" ? Buffer.from(data, "utf8") : data;
|
|
1666
|
+
return peer.call(TO_CORE.writeBlob, {
|
|
1667
|
+
base64: buffer.toString("base64"),
|
|
1668
|
+
mime,
|
|
1669
|
+
...name ? { name } : {}
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
},
|
|
1673
|
+
model: {
|
|
1674
|
+
ask: (input) => peer.call(TO_CORE.ask, input)
|
|
1675
|
+
},
|
|
1676
|
+
notify: async (title, body) => {
|
|
1677
|
+
await peer.call(TO_CORE.notify, { title, ...body ? { body } : {} });
|
|
1678
|
+
},
|
|
1679
|
+
status: {
|
|
1680
|
+
set: async (note, failed) => {
|
|
1681
|
+
await peer.call(TO_CORE.setStatus, { note, ...failed ? { failed } : {} });
|
|
1682
|
+
},
|
|
1683
|
+
clear: async () => {
|
|
1684
|
+
await peer.call(TO_CORE.setStatus, {});
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
history: {
|
|
1688
|
+
search: (query, limit) => peer.call(TO_CORE.searchHistory, {
|
|
1689
|
+
query,
|
|
1690
|
+
...limit ? { limit } : {}
|
|
1691
|
+
})
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
peer.handle(TO_PLUGIN.activate, async (params) => {
|
|
1696
|
+
settings = params.settings ?? {};
|
|
1697
|
+
const module = await loadModule(params.dir);
|
|
1698
|
+
loaded = module;
|
|
1699
|
+
await module.activate(buildApi(params));
|
|
1700
|
+
return null;
|
|
1701
|
+
});
|
|
1702
|
+
peer.handle(TO_PLUGIN.deactivate, async () => {
|
|
1703
|
+
await loaded?.deactivate?.();
|
|
1704
|
+
return null;
|
|
1705
|
+
});
|
|
1706
|
+
peer.handle(TO_PLUGIN.toolExecute, async (params, ctx) => {
|
|
1707
|
+
const tool = tools.get(params.name);
|
|
1708
|
+
if (!tool)
|
|
1709
|
+
throw new RpcError(`\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442 ${params.name} \u043D\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D`, "not_found");
|
|
1710
|
+
const output = await tool.execute(params.args, {
|
|
1711
|
+
conversationId: params.conversationId,
|
|
1712
|
+
runId: params.runId,
|
|
1713
|
+
signal: ctx.signal,
|
|
1714
|
+
requestPermission: (reason) => peer.call(TO_CORE.requestPermission, { token: params.token, reason })
|
|
1715
|
+
});
|
|
1716
|
+
const result = typeof output === "string" ? { text: output } : { text: output.text, ...output.mime ? { mime: output.mime } : {} };
|
|
1717
|
+
return result;
|
|
1718
|
+
});
|
|
1719
|
+
peer.handle(TO_PLUGIN.contribute, async (params) => {
|
|
1720
|
+
const contribute = contributors.get(params.name);
|
|
1721
|
+
if (!contribute)
|
|
1722
|
+
return null;
|
|
1723
|
+
return await contribute({ conversationId: params.conversationId, userText: params.userText }) ?? null;
|
|
1724
|
+
});
|
|
1725
|
+
peer.handle(TO_PLUGIN.providerChat, async (params, ctx) => {
|
|
1726
|
+
const provider = providers.get(params.providerId);
|
|
1727
|
+
if (!provider)
|
|
1728
|
+
throw new RpcError(`\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 ${params.providerId} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D`, "not_found");
|
|
1729
|
+
for await (const event of provider.chat(params.request, ctx.signal)) {
|
|
1730
|
+
ctx.push(event);
|
|
1731
|
+
}
|
|
1732
|
+
return null;
|
|
1733
|
+
});
|
|
1734
|
+
peer.handle(TO_PLUGIN.actionRun, async (params) => {
|
|
1735
|
+
const handler = actions.get(params.name);
|
|
1736
|
+
if (!handler)
|
|
1737
|
+
throw new Error(`\u041F\u043B\u0430\u0433\u0438\u043D \u043D\u0435 \u0437\u043D\u0430\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \xAB${params.name}\xBB`);
|
|
1738
|
+
try {
|
|
1739
|
+
const message = await handler();
|
|
1740
|
+
return { ok: true, message: message || "\u0413\u043E\u0442\u043E\u0432\u043E" };
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
return { ok: false, message: error.message };
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
peer.handle(TO_PLUGIN.jobRun, async (params) => {
|
|
1746
|
+
const run2 = jobs.get(params.name);
|
|
1747
|
+
if (!run2)
|
|
1748
|
+
throw new RpcError(`\u0417\u0430\u0434\u0430\u0447\u0430 ${params.name} \u043D\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D\u0430`, "not_found");
|
|
1749
|
+
await run2();
|
|
1750
|
+
return null;
|
|
1751
|
+
});
|
|
1752
|
+
peer.onEvent(TO_PLUGIN.settingsChanged, ({ values }) => {
|
|
1753
|
+
settings = { ...settings, ...values };
|
|
1754
|
+
for (const listener of settingsListeners)
|
|
1755
|
+
listener(settings);
|
|
1756
|
+
});
|
|
1757
|
+
peer.onEvent(TO_PLUGIN.journalEvent, ({ entry }) => {
|
|
1758
|
+
for (const listener of journalListeners)
|
|
1759
|
+
listener(entry);
|
|
1760
|
+
});
|
|
1761
|
+
process.on("uncaughtException", (error) => {
|
|
1762
|
+
peer.emit(TO_CORE.log, { level: "error", message: `\u041D\u0435\u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430: ${error.message}` });
|
|
1763
|
+
process.exit(1);
|
|
1764
|
+
});
|
|
1765
|
+
process.on("unhandledRejection", (reason) => {
|
|
1766
|
+
peer.emit(TO_CORE.log, { level: "error", message: `\u041D\u0435\u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u043D\u044B\u0439 \u043E\u0442\u043A\u0430\u0437: ${String(reason)}` });
|
|
1767
|
+
});
|
|
1768
|
+
peer.emit(HOST_READY, { api: PLUGIN_API_VERSION });
|
|
1769
|
+
}
|
|
1770
|
+
async function loadModule(dir) {
|
|
1771
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(dir, "axon.plugin.json"), "utf8"));
|
|
1772
|
+
if (!manifest.main) {
|
|
1773
|
+
throw new RpcError("\u0423 \u043F\u043B\u0430\u0433\u0438\u043D\u0430 \u043D\u0435\u0442 \u0442\u043E\u0447\u043A\u0438 \u0432\u0445\u043E\u0434\u0430 \u2014 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E", "no_main");
|
|
1774
|
+
}
|
|
1775
|
+
const entry = path.resolve(dir, manifest.main);
|
|
1776
|
+
const imported = await import(pathToFileURL(entry).href);
|
|
1777
|
+
const module = "activate" in imported ? imported : imported.default;
|
|
1778
|
+
if (!module || typeof module.activate !== "function") {
|
|
1779
|
+
throw new RpcError(`${manifest.main} \u043D\u0435 \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u0443\u0435\u0442 activate(api)`, "bad_entry");
|
|
1780
|
+
}
|
|
1781
|
+
return module;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// ../core/dist/plugins/install.js
|
|
1785
|
+
var run = promisify(execFile);
|
|
1786
|
+
|
|
1787
|
+
// ../core/dist/plugins/LoadedPlugin.js
|
|
1788
|
+
import { z as z14 } from "zod";
|
|
1789
|
+
|
|
1790
|
+
// ../core/dist/mcp/McpConnection.js
|
|
1791
|
+
import { z as z13 } from "zod";
|
|
1792
|
+
|
|
1793
|
+
// ../core/dist/routines/Compiler.js
|
|
1794
|
+
import { z as z15 } from "zod";
|
|
1795
|
+
var RESULT = z15.object({
|
|
1796
|
+
name: z15.string().min(1).max(120),
|
|
1797
|
+
description: z15.string().max(500).default(""),
|
|
1798
|
+
schedule: zSchedule,
|
|
1799
|
+
steps: z15.array(zRoutineStep).max(60)
|
|
1800
|
+
});
|
|
1801
|
+
|
|
1802
|
+
// ../core/dist/routines/Executor.js
|
|
1803
|
+
var VARIABLE = new RegExp("\\$\\{(\\p{L}[\\p{L}\\p{N}_]*)\\}", "gu");
|
|
1804
|
+
|
|
1805
|
+
// ../core/dist/providers/AnthropicProvider.js
|
|
1806
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
1807
|
+
|
|
1808
|
+
// ../core/dist/providers/pricing.js
|
|
1809
|
+
var ANTHROPIC_DEFAULT = {
|
|
1810
|
+
cacheReadRatio: 0.1,
|
|
1811
|
+
cacheWriteRatio: 1.25
|
|
1812
|
+
};
|
|
1813
|
+
var RATES = {
|
|
1814
|
+
anthropic: {
|
|
1815
|
+
"claude-opus-5": { inputPerMTok: 5, outputPerMTok: 25, ...ANTHROPIC_DEFAULT },
|
|
1816
|
+
"claude-opus-4-8": { inputPerMTok: 5, outputPerMTok: 25, ...ANTHROPIC_DEFAULT },
|
|
1817
|
+
"claude-sonnet-5": { inputPerMTok: 3, outputPerMTok: 15, ...ANTHROPIC_DEFAULT },
|
|
1818
|
+
"claude-haiku-4-5": { inputPerMTok: 1, outputPerMTok: 5, ...ANTHROPIC_DEFAULT }
|
|
1819
|
+
},
|
|
1820
|
+
deepseek: {
|
|
1821
|
+
// У DeepSeek кэш работает иначе: попадание тарифицируется отдельной ставкой,
|
|
1822
|
+
// а не множителем, но в пересчёте это примерно тот же порядок.
|
|
1823
|
+
"deepseek-chat": {
|
|
1824
|
+
inputPerMTok: 0.27,
|
|
1825
|
+
outputPerMTok: 1.1,
|
|
1826
|
+
cacheReadRatio: 0.26,
|
|
1827
|
+
cacheWriteRatio: 1
|
|
1828
|
+
},
|
|
1829
|
+
"deepseek-reasoner": {
|
|
1830
|
+
inputPerMTok: 0.55,
|
|
1831
|
+
outputPerMTok: 2.19,
|
|
1832
|
+
cacheReadRatio: 0.26,
|
|
1833
|
+
cacheWriteRatio: 1
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1837
|
+
|
|
1838
|
+
// ../core/dist/tools/ToolRegistry.js
|
|
1839
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
1840
|
+
|
|
1841
|
+
// ../core/dist/tools/builtin/files.js
|
|
1842
|
+
import { z as z16 } from "zod";
|
|
1843
|
+
var HUGE_FILE_BYTES = 5 * 1024 * 1024;
|
|
1844
|
+
|
|
1845
|
+
// ../core/dist/tools/builtin/http.js
|
|
1846
|
+
import { z as z17 } from "zod";
|
|
1847
|
+
var MAX_RESPONSE_BYTES = 512 * 1024;
|
|
1848
|
+
|
|
1849
|
+
// ../core/dist/tools/builtin/memory.js
|
|
1850
|
+
import { z as z18 } from "zod";
|
|
1851
|
+
|
|
1852
|
+
// ../core/dist/tools/builtin/persona.js
|
|
1853
|
+
import { z as z19 } from "zod";
|
|
1854
|
+
|
|
1855
|
+
// ../core/dist/tools/builtin/recall.js
|
|
1856
|
+
import { z as z20 } from "zod";
|
|
1857
|
+
|
|
1858
|
+
// ../core/dist/tools/builtin/shell.js
|
|
1859
|
+
import iconv from "iconv-lite";
|
|
1860
|
+
import { z as z21 } from "zod";
|
|
1861
|
+
var MAX_OUTPUT_BYTES = 512 * 1024;
|
|
1862
|
+
|
|
1863
|
+
// ../core/dist/tools/builtin/skills.js
|
|
1864
|
+
import { z as z22 } from "zod";
|
|
1865
|
+
|
|
1866
|
+
// ../core/dist/tools/builtin/system.js
|
|
1867
|
+
import { z as z23 } from "zod";
|
|
1868
|
+
|
|
1869
|
+
// src/plugin-host.ts
|
|
1870
|
+
startPluginHost();
|