arisa 5.1.49 → 5.1.60
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 +0 -2
- package/package.json +1 -1
- package/src/core/agent/agent-manager.js +39 -489
- package/src/core/agent/agent-session-lifecycle.js +181 -0
- package/src/core/agent/pi-capability-tools.js +183 -0
- package/src/core/artifacts/artifact-store.js +73 -17
- package/src/core/capabilities/capability-service.js +340 -0
- package/src/core/tasks/task-routing.js +7 -0
- package/src/core/tasks/task-runner.js +53 -0
- package/src/core/tasks/task-store.js +316 -92
- package/src/core/tools/tool-output-materializer.js +5 -5
- package/src/official-tools.lock.json +7 -4
- package/src/runtime/arisa-capabilities.js +51 -242
- package/src/runtime/create-app.js +10 -1
- package/src/runtime/create-headless-app.js +5 -2
- package/src/transport/telegram/bot.js +112 -368
- package/src/transport/telegram/chat-queue.js +72 -6
- package/src/transport/telegram/prompt-builders.js +9 -0
- package/src/transport/telegram/task-dispatcher.js +73 -36
- package/src/transport/telegram/telegram-auth-controller.js +180 -0
- package/src/transport/telegram/telegram-session-bridge.js +170 -0
- package/src/transport/telegram/telegram-tools-command.js +28 -0
- package/src/transport/telegram/telegram-workspace-controller.js +66 -0
- package/test/agent-session-lifecycle.test.js +58 -0
- package/test/artifact-store.test.js +38 -2
- package/test/capabilities-security.test.js +58 -0
- package/test/context-and-task-bounds.test.js +76 -1
- package/test/device-code-message.test.js +9 -0
- package/test/media-caption.test.js +1 -1
- package/test/pi-capability-tools.test.js +65 -0
- package/test/session-start-operational-notes.test.js +1 -1
- package/test/task-idempotency.test.js +40 -0
- package/test/task-routing.test.js +62 -0
- package/test/task-store.test.js +178 -6
- package/test/telegram-task-dispatcher.test.js +99 -23
- package/test/telegram-text-artifact.test.js +13 -2
- package/test/telegram-tools-command.test.js +47 -0
|
@@ -1,11 +1,39 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
4
|
import { tasksFile } from "../../runtime/paths.js";
|
|
5
5
|
|
|
6
|
+
const DEFAULT_RETRY = Object.freeze({
|
|
7
|
+
maxAttempts: 3,
|
|
8
|
+
baseDelaySeconds: 30,
|
|
9
|
+
maxDelaySeconds: 900,
|
|
10
|
+
multiplier: 2
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const TERMINAL_STATUSES = new Set(["done", "failed", "outcome_uncertain"]);
|
|
14
|
+
const TERMINAL_PAYLOAD_KEYS = ["chatId", "toolName", "resourceId", "artifactId"];
|
|
15
|
+
|
|
16
|
+
const taskFileOperations = new Map();
|
|
17
|
+
|
|
18
|
+
async function serializeTaskFileOperation(operation) {
|
|
19
|
+
const previous = taskFileOperations.get(tasksFile) || Promise.resolve();
|
|
20
|
+
const current = previous.catch(() => {}).then(operation);
|
|
21
|
+
taskFileOperations.set(tasksFile, current);
|
|
22
|
+
try {
|
|
23
|
+
return await current;
|
|
24
|
+
} finally {
|
|
25
|
+
if (taskFileOperations.get(tasksFile) === current) taskFileOperations.delete(tasksFile);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function waitForTaskFileOperations() {
|
|
30
|
+
await (taskFileOperations.get(tasksFile) || Promise.resolve()).catch(() => {});
|
|
31
|
+
}
|
|
32
|
+
|
|
6
33
|
async function loadTasksFile() {
|
|
7
34
|
try {
|
|
8
|
-
|
|
35
|
+
const parsed = JSON.parse(await readFile(tasksFile, "utf8"));
|
|
36
|
+
return Array.isArray(parsed) ? parsed.map(migrateTask) : [];
|
|
9
37
|
} catch {
|
|
10
38
|
return [];
|
|
11
39
|
}
|
|
@@ -13,173 +41,369 @@ async function loadTasksFile() {
|
|
|
13
41
|
|
|
14
42
|
async function saveTasksFile(tasks) {
|
|
15
43
|
await mkdir(path.dirname(tasksFile), { recursive: true });
|
|
16
|
-
|
|
44
|
+
const temporaryFile = `${tasksFile}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
45
|
+
try {
|
|
46
|
+
await writeFile(temporaryFile, `${JSON.stringify(tasks, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
47
|
+
await rename(temporaryFile, tasksFile);
|
|
48
|
+
} finally {
|
|
49
|
+
await rm(temporaryFile, { force: true }).catch(() => {});
|
|
50
|
+
}
|
|
17
51
|
}
|
|
18
52
|
|
|
19
53
|
function taskId() {
|
|
20
54
|
return crypto.randomUUID();
|
|
21
55
|
}
|
|
22
56
|
|
|
23
|
-
function
|
|
57
|
+
function boundedPositiveInteger(value, fallback, maximum) {
|
|
58
|
+
const parsed = Number(value);
|
|
59
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? Math.min(parsed, maximum) : fallback;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function boundedPositiveNumber(value, fallback, maximum) {
|
|
63
|
+
const parsed = Number(value);
|
|
64
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.min(parsed, maximum) : fallback;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeRetry(retry = {}) {
|
|
68
|
+
const value = retry && typeof retry === "object" && !Array.isArray(retry) ? retry : {};
|
|
69
|
+
return {
|
|
70
|
+
maxAttempts: boundedPositiveInteger(value.maxAttempts, DEFAULT_RETRY.maxAttempts, 10),
|
|
71
|
+
baseDelaySeconds: boundedPositiveNumber(value.baseDelaySeconds, DEFAULT_RETRY.baseDelaySeconds, 3_600),
|
|
72
|
+
maxDelaySeconds: boundedPositiveNumber(value.maxDelaySeconds, DEFAULT_RETRY.maxDelaySeconds, 86_400),
|
|
73
|
+
multiplier: boundedPositiveNumber(value.multiplier, DEFAULT_RETRY.multiplier, 10)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function legacyTelegramRoute(telegramContext) {
|
|
78
|
+
if (!telegramContext?.transportChatId) return null;
|
|
24
79
|
return {
|
|
80
|
+
transport: "telegram",
|
|
81
|
+
destination: {
|
|
82
|
+
chatId: telegramContext.transportChatId,
|
|
83
|
+
...(telegramContext.messageThreadId ? { threadId: telegramContext.messageThreadId } : {})
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function migrateTask(task = {}) {
|
|
89
|
+
const payload = { ...(task.payload || {}) };
|
|
90
|
+
const route = task.route || legacyTelegramRoute(payload.telegramContext) || null;
|
|
91
|
+
delete payload.telegramContext;
|
|
92
|
+
const migrated = {
|
|
93
|
+
...task,
|
|
94
|
+
payload,
|
|
95
|
+
route,
|
|
96
|
+
attempts: Number.isSafeInteger(task.attempts) && task.attempts >= 0 ? task.attempts : 0
|
|
97
|
+
};
|
|
98
|
+
if (!TERMINAL_STATUSES.has(migrated.status)) migrated.retry = normalizeRetry(task.retry);
|
|
99
|
+
return migrated;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function compactTerminalPayload(payload = {}) {
|
|
103
|
+
return Object.fromEntries(TERMINAL_PAYLOAD_KEYS.flatMap((key) => {
|
|
104
|
+
const value = payload[key];
|
|
105
|
+
return ["string", "number", "boolean"].includes(typeof value) ? [[key, value]] : [];
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function compactTerminalTask(task) {
|
|
110
|
+
if (!TERMINAL_STATUSES.has(task.status)) return false;
|
|
111
|
+
const payload = compactTerminalPayload(task.payload);
|
|
112
|
+
const changed = !task.payloadCompacted
|
|
113
|
+
|| JSON.stringify(task.payload || {}) !== JSON.stringify(payload)
|
|
114
|
+
|| Object.hasOwn(task, "retry")
|
|
115
|
+
|| Object.hasOwn(task, "recurrence");
|
|
116
|
+
task.payload = payload;
|
|
117
|
+
task.payloadCompacted = true;
|
|
118
|
+
delete task.retry;
|
|
119
|
+
delete task.recurrence;
|
|
120
|
+
return changed;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function normalizeTask(task, defaults = {}) {
|
|
124
|
+
const mergedPayload = {
|
|
125
|
+
...(defaults.payload || {}),
|
|
126
|
+
...(task.payload || {})
|
|
127
|
+
};
|
|
128
|
+
const route = task.route
|
|
129
|
+
|| defaults.route
|
|
130
|
+
|| legacyTelegramRoute(mergedPayload.telegramContext)
|
|
131
|
+
|| null;
|
|
132
|
+
delete mergedPayload.telegramContext;
|
|
133
|
+
const normalized = migrateTask({
|
|
25
134
|
id: task.id || taskId(),
|
|
26
135
|
status: task.status || "pending",
|
|
27
136
|
createdAt: task.createdAt || new Date().toISOString(),
|
|
28
137
|
updatedAt: new Date().toISOString(),
|
|
29
138
|
kind: task.kind,
|
|
30
139
|
runAt: task.runAt || new Date().toISOString(),
|
|
31
|
-
payload:
|
|
32
|
-
|
|
33
|
-
...(task.payload || {})
|
|
34
|
-
},
|
|
140
|
+
payload: mergedPayload,
|
|
141
|
+
route,
|
|
35
142
|
recurrence: task.recurrence || defaults.recurrence || null,
|
|
36
143
|
source: {
|
|
37
144
|
...(defaults.source || {}),
|
|
38
145
|
...(task.source || {})
|
|
39
|
-
}
|
|
40
|
-
|
|
146
|
+
},
|
|
147
|
+
attempts: task.attempts || 0,
|
|
148
|
+
retry: task.retry || defaults.retry
|
|
149
|
+
});
|
|
150
|
+
compactTerminalTask(normalized);
|
|
151
|
+
return normalized;
|
|
41
152
|
}
|
|
42
153
|
|
|
43
|
-
function computeNextRunAt(task) {
|
|
154
|
+
function computeNextRunAt(task, now = Date.now()) {
|
|
44
155
|
if (task.recurrence?.type === "interval" && Number(task.recurrence.everySeconds) > 0) {
|
|
45
|
-
return new Date(
|
|
156
|
+
return new Date(now + (Number(task.recurrence.everySeconds) * 1000)).toISOString();
|
|
46
157
|
}
|
|
47
158
|
return "";
|
|
48
159
|
}
|
|
49
160
|
|
|
161
|
+
function retryDelayMs(task) {
|
|
162
|
+
const exponent = Math.max(0, Number(task.attempts || 1) - 1);
|
|
163
|
+
const seconds = Math.min(
|
|
164
|
+
task.retry.maxDelaySeconds,
|
|
165
|
+
task.retry.baseDelaySeconds * (task.retry.multiplier ** exponent)
|
|
166
|
+
);
|
|
167
|
+
return Math.max(1, Math.round(seconds * 1000));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function failTask(task, error) {
|
|
171
|
+
const failedAt = new Date().toISOString();
|
|
172
|
+
task.status = "failed";
|
|
173
|
+
task.error = error instanceof Error ? error.message : String(error);
|
|
174
|
+
task.lastError = task.error;
|
|
175
|
+
task.failedAt = failedAt;
|
|
176
|
+
task.updatedAt = failedAt;
|
|
177
|
+
compactTerminalTask(task);
|
|
178
|
+
return structuredClone(task);
|
|
179
|
+
}
|
|
180
|
+
|
|
50
181
|
export class TaskStore {
|
|
51
182
|
constructor() {
|
|
52
183
|
this.tasks = null;
|
|
53
184
|
}
|
|
54
185
|
|
|
55
186
|
async init() {
|
|
56
|
-
if (!this.tasks) this.
|
|
187
|
+
if (!this.tasks) await this.reload();
|
|
57
188
|
}
|
|
58
189
|
|
|
59
190
|
async reload() {
|
|
191
|
+
await waitForTaskFileOperations();
|
|
60
192
|
this.tasks = await loadTasksFile();
|
|
61
193
|
}
|
|
62
194
|
|
|
195
|
+
async mutate(operation) {
|
|
196
|
+
return serializeTaskFileOperation(async () => {
|
|
197
|
+
this.tasks = await loadTasksFile();
|
|
198
|
+
const { result, changed = true } = await operation(this.tasks);
|
|
199
|
+
if (changed) await saveTasksFile(this.tasks);
|
|
200
|
+
return result;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
63
204
|
async save() {
|
|
64
|
-
|
|
205
|
+
const tasks = structuredClone(this.tasks || []);
|
|
206
|
+
return serializeTaskFileOperation(async () => {
|
|
207
|
+
this.tasks = tasks;
|
|
208
|
+
await saveTasksFile(tasks);
|
|
209
|
+
});
|
|
65
210
|
}
|
|
66
211
|
|
|
67
212
|
async add(task, defaults = {}) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
213
|
+
return this.mutate(async (tasks) => {
|
|
214
|
+
const normalized = normalizeTask(task, defaults);
|
|
215
|
+
tasks.push(normalized);
|
|
216
|
+
return { result: structuredClone(normalized) };
|
|
217
|
+
});
|
|
73
218
|
}
|
|
74
219
|
|
|
75
|
-
async addMany(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
220
|
+
async addMany(tasksToAdd = [], defaults = {}) {
|
|
221
|
+
return this.mutate(async (tasks) => {
|
|
222
|
+
const created = tasksToAdd.map((task) => normalizeTask(task, defaults));
|
|
223
|
+
tasks.push(...created);
|
|
224
|
+
return { result: structuredClone(created), changed: created.length > 0 };
|
|
225
|
+
});
|
|
81
226
|
}
|
|
82
227
|
|
|
83
228
|
async claimDue(limit = 10) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
for (const task of this.tasks) {
|
|
89
|
-
if (due.length >= limit) break;
|
|
90
|
-
if (task.status !== "pending") continue;
|
|
91
|
-
if (!task.runAt || Number.isNaN(Date.parse(task.runAt))) continue;
|
|
92
|
-
if (Date.parse(task.runAt) > now) continue;
|
|
93
|
-
task.status = "running";
|
|
94
|
-
task.updatedAt = new Date().toISOString();
|
|
95
|
-
due.push({ ...task });
|
|
96
|
-
}
|
|
229
|
+
return this.mutate(async (tasks) => {
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
const due = [];
|
|
97
232
|
|
|
98
|
-
|
|
99
|
-
|
|
233
|
+
for (const task of tasks) {
|
|
234
|
+
if (due.length >= limit) break;
|
|
235
|
+
if (task.status !== "pending") continue;
|
|
236
|
+
if (!task.runAt || Number.isNaN(Date.parse(task.runAt))) continue;
|
|
237
|
+
if (Date.parse(task.runAt) > now) continue;
|
|
238
|
+
task.status = "running";
|
|
239
|
+
task.attempts += 1;
|
|
240
|
+
task.startedAt = new Date(now).toISOString();
|
|
241
|
+
task.updatedAt = task.startedAt;
|
|
242
|
+
due.push(structuredClone(task));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return { result: due, changed: due.length > 0 };
|
|
246
|
+
});
|
|
100
247
|
}
|
|
101
248
|
|
|
102
249
|
async recoverInterrupted() {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
250
|
+
return this.mutate(async (tasks) => {
|
|
251
|
+
const recovered = [];
|
|
252
|
+
let compacted = false;
|
|
253
|
+
const now = Date.now();
|
|
106
254
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
255
|
+
for (const task of tasks) {
|
|
256
|
+
if (task.status === "running") {
|
|
257
|
+
const interruptedAt = new Date(now).toISOString();
|
|
258
|
+
task.lastError = "execution interrupted before confirmation";
|
|
259
|
+
task.lastFailedAt = interruptedAt;
|
|
260
|
+
task.updatedAt = interruptedAt;
|
|
261
|
+
if (task.kind === "poll_tool") {
|
|
262
|
+
task.status = "pending";
|
|
263
|
+
task.runAt = new Date(now + retryDelayMs(task)).toISOString();
|
|
264
|
+
} else {
|
|
265
|
+
const nextRunAt = computeNextRunAt(task, now);
|
|
266
|
+
if (nextRunAt) {
|
|
267
|
+
task.status = "pending";
|
|
268
|
+
task.runAt = nextRunAt;
|
|
269
|
+
task.attempts = 0;
|
|
270
|
+
task.lastOutcome = "outcome_uncertain";
|
|
271
|
+
} else {
|
|
272
|
+
task.status = "outcome_uncertain";
|
|
273
|
+
task.error = task.lastError;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
compactTerminalTask(task);
|
|
277
|
+
recovered.push(structuredClone(task));
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (compactTerminalTask(task)) compacted = true;
|
|
281
|
+
}
|
|
113
282
|
|
|
114
|
-
|
|
115
|
-
|
|
283
|
+
return { result: recovered, changed: recovered.length > 0 || compacted };
|
|
284
|
+
});
|
|
116
285
|
}
|
|
117
286
|
|
|
118
287
|
async complete(taskId) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
288
|
+
return this.mutate(async (tasks) => {
|
|
289
|
+
const task = tasks.find((item) => item.id === taskId);
|
|
290
|
+
if (!task) return { result: null, changed: false };
|
|
122
291
|
|
|
123
|
-
|
|
124
|
-
|
|
292
|
+
const now = Date.now();
|
|
293
|
+
const completedAt = new Date(now).toISOString();
|
|
294
|
+
const nextRunAt = computeNextRunAt(task, now);
|
|
295
|
+
task.lastCompletedAt = completedAt;
|
|
296
|
+
task.lastRunAt = completedAt;
|
|
297
|
+
delete task.lastError;
|
|
298
|
+
delete task.error;
|
|
299
|
+
delete task.lastOutcome;
|
|
300
|
+
delete task.consecutiveFailures;
|
|
301
|
+
if (nextRunAt) {
|
|
302
|
+
task.status = "pending";
|
|
303
|
+
task.runAt = nextRunAt;
|
|
304
|
+
task.attempts = 0;
|
|
305
|
+
delete task.startedAt;
|
|
306
|
+
} else {
|
|
307
|
+
task.status = "done";
|
|
308
|
+
task.completedAt = completedAt;
|
|
309
|
+
}
|
|
310
|
+
task.updatedAt = completedAt;
|
|
311
|
+
compactTerminalTask(task);
|
|
312
|
+
return { result: structuredClone(task) };
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async retryOrFail(taskId, error, { retryable = true, outcomeUncertain = false } = {}) {
|
|
317
|
+
return this.mutate(async (tasks) => {
|
|
318
|
+
const task = tasks.find((item) => item.id === taskId);
|
|
319
|
+
if (!task) return { result: null, changed: false };
|
|
320
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
321
|
+
if (outcomeUncertain) {
|
|
322
|
+
const uncertainAt = new Date().toISOString();
|
|
323
|
+
task.status = "outcome_uncertain";
|
|
324
|
+
task.error = message;
|
|
325
|
+
task.lastError = message;
|
|
326
|
+
task.uncertainAt = uncertainAt;
|
|
327
|
+
task.updatedAt = uncertainAt;
|
|
328
|
+
compactTerminalTask(task);
|
|
329
|
+
return { result: structuredClone(task) };
|
|
330
|
+
}
|
|
331
|
+
if (!retryable) return { result: failTask(task, message) };
|
|
332
|
+
if (task.attempts >= task.retry.maxAttempts) {
|
|
333
|
+
const now = Date.now();
|
|
334
|
+
const nextRunAt = computeNextRunAt(task, now);
|
|
335
|
+
if (!nextRunAt) return { result: failTask(task, message) };
|
|
336
|
+
|
|
337
|
+
const failedAt = new Date(now).toISOString();
|
|
338
|
+
task.status = "pending";
|
|
339
|
+
task.runAt = nextRunAt;
|
|
340
|
+
task.attempts = 0;
|
|
341
|
+
task.lastOutcome = "failed";
|
|
342
|
+
task.lastError = message;
|
|
343
|
+
task.lastFailedAt = failedAt;
|
|
344
|
+
task.consecutiveFailures = Number(task.consecutiveFailures || 0) + 1;
|
|
345
|
+
task.updatedAt = failedAt;
|
|
346
|
+
delete task.startedAt;
|
|
347
|
+
delete task.error;
|
|
348
|
+
return { result: { ...structuredClone(task), terminalFailure: true } };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const now = Date.now();
|
|
125
352
|
task.status = "pending";
|
|
126
|
-
task.runAt =
|
|
127
|
-
task.
|
|
128
|
-
|
|
129
|
-
task.
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
task.updatedAt = new Date().toISOString();
|
|
133
|
-
await this.save();
|
|
134
|
-
return task;
|
|
353
|
+
task.runAt = new Date(now + retryDelayMs(task)).toISOString();
|
|
354
|
+
task.lastError = message;
|
|
355
|
+
task.lastFailedAt = new Date(now).toISOString();
|
|
356
|
+
task.updatedAt = task.lastFailedAt;
|
|
357
|
+
return { result: structuredClone(task) };
|
|
358
|
+
});
|
|
135
359
|
}
|
|
136
360
|
|
|
137
361
|
async fail(taskId, error) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
await this.save();
|
|
145
|
-
return task;
|
|
362
|
+
return this.mutate(async (tasks) => {
|
|
363
|
+
const task = tasks.find((item) => item.id === taskId);
|
|
364
|
+
return task
|
|
365
|
+
? { result: failTask(task, error) }
|
|
366
|
+
: { result: null, changed: false };
|
|
367
|
+
});
|
|
146
368
|
}
|
|
147
369
|
|
|
148
370
|
async list(filter = {}) {
|
|
149
371
|
await this.reload();
|
|
150
372
|
return this.tasks.filter((task) => {
|
|
151
|
-
if (filter.chatId && task.payload?.chatId !== filter.chatId) return false;
|
|
373
|
+
if (filter.chatId && String(task.payload?.chatId) !== String(filter.chatId)) return false;
|
|
152
374
|
if (filter.status && task.status !== filter.status) return false;
|
|
153
375
|
if (filter.kind && task.kind !== filter.kind) return false;
|
|
154
376
|
return true;
|
|
155
|
-
});
|
|
377
|
+
}).map((task) => structuredClone(task));
|
|
156
378
|
}
|
|
157
379
|
|
|
158
380
|
async get(taskId) {
|
|
159
381
|
await this.reload();
|
|
160
|
-
|
|
382
|
+
const task = this.tasks.find((item) => item.id === taskId);
|
|
383
|
+
return task ? structuredClone(task) : null;
|
|
161
384
|
}
|
|
162
385
|
|
|
163
386
|
async cancel(taskId) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
387
|
+
return this.mutate(async (tasks) => {
|
|
388
|
+
const index = tasks.findIndex((item) => item.id === taskId);
|
|
389
|
+
if (index === -1) return { result: null, changed: false };
|
|
390
|
+
const [task] = tasks.splice(index, 1);
|
|
391
|
+
return { result: structuredClone(task) };
|
|
392
|
+
});
|
|
170
393
|
}
|
|
171
394
|
|
|
172
395
|
async cancelAll(filter = {}) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
396
|
+
return this.mutate(async (tasks) => {
|
|
397
|
+
const removed = [];
|
|
398
|
+
const remaining = tasks.filter((task) => {
|
|
399
|
+
if (filter.chatId && String(task.payload?.chatId) !== String(filter.chatId)) return true;
|
|
400
|
+
if (filter.status && task.status !== filter.status) return true;
|
|
401
|
+
if (["done", "failed", "outcome_uncertain"].includes(task.status)) return true;
|
|
402
|
+
removed.push(structuredClone(task));
|
|
403
|
+
return false;
|
|
404
|
+
});
|
|
405
|
+
tasks.splice(0, tasks.length, ...remaining);
|
|
406
|
+
return { result: removed, changed: removed.length > 0 };
|
|
181
407
|
});
|
|
182
|
-
if (removed.length) await this.save();
|
|
183
|
-
return removed;
|
|
184
408
|
}
|
|
185
409
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { taskWithoutCallerRouting } from "../tasks/task-routing.js";
|
|
3
4
|
|
|
4
5
|
export async function materializeToolOutput({ result, name, chatId, artifactStore, taskStore, taskContext = null }) {
|
|
5
6
|
const chatArtifactStore = artifactStore.forChat(chatId);
|
|
@@ -27,11 +28,10 @@ export async function materializeToolOutput({ result, name, chatId, artifactStor
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
if (result.asyncTask || result.asyncTasks?.length) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
31
|
+
const requestedTasks = (result.asyncTasks || [result.asyncTask]).map(taskWithoutCallerRouting);
|
|
32
|
+
result.asyncTasks = await taskStore.addMany(requestedTasks, {
|
|
33
|
+
payload: { chatId },
|
|
34
|
+
...(taskContext ? { route: taskContext } : {}),
|
|
35
35
|
source: { type: "tool", toolName: name, chatId }
|
|
36
36
|
});
|
|
37
37
|
delete result.asyncTask;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
3
|
"repository": "https://github.com/clasen/Arisa.git",
|
|
4
|
-
"commit": "
|
|
4
|
+
"commit": "43b032cbaa5e4ec42345a822119f06a13f49ff8f",
|
|
5
5
|
"tools": {
|
|
6
6
|
"browser-session-bridge": {
|
|
7
7
|
"version": "0.1.0",
|
|
@@ -96,12 +96,14 @@
|
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"master-slave": {
|
|
99
|
+
"version": "0.1.2",
|
|
99
100
|
"files": {
|
|
100
|
-
"README.md": "
|
|
101
|
+
"README.md": "9755dd59d01680236d12b57f87e4bc52568649a0e63b1a96065baf800d8dc1c8",
|
|
101
102
|
"batch-runner.js": "97756a03b36278cd0315cc16da0c34a7821c322149c51ba6666f1c5081dbecbc",
|
|
102
103
|
"chat-state-store.js": "50de39aa33432733bb688eb01968e314a32b169324a66d0f0089b2a19ed9c880",
|
|
104
|
+
"command-arguments.js": "ef00f814dee6105e1cd3b8929e974bc071117f162b17e3dcc3dc0ea68e8b1e92",
|
|
103
105
|
"config.js": "ff1c1cb6701ae1da7a6eb1519ba96a416e48daa73c2155a6f93cf5d64f65cef8",
|
|
104
|
-
"index.js": "
|
|
106
|
+
"index.js": "404db150dee0ecf79cd64bca245c618fa1e2bb324d6bc15f5ac0022b72497c63",
|
|
105
107
|
"lib/bootstrap-url.js": "f05925cc0f0dff0882f94f9908c6f2c80aa8632cd51f63389381dcbb0f73afb3",
|
|
106
108
|
"lib/encrypted-frames.js": "47e1b11fafdbbcdfd86a0993a47a774e4f37d6386edeaf8ec1c8511b109728cf",
|
|
107
109
|
"lib/handshake-crypto.js": "9728b117aad7de53f5f0e255c0fd865ee1a19633368730a7634334d374094578",
|
|
@@ -111,12 +113,13 @@
|
|
|
111
113
|
"lib/secure-store.js": "b8f365197f8e60d7f81fde6ed1cfc8851b603f6b0783e4cdc9036bb5c99726e1",
|
|
112
114
|
"master-domain.js": "10b7dfb43eb9939eb5baec54f742076cc73a2add6589a0ff57c51bc79c236faf",
|
|
113
115
|
"network-session.js": "db2d81a7ef47af29236d963b98e21414a11fef68e8b20895126eaad3f696ba3d",
|
|
114
|
-
"package.json": "
|
|
116
|
+
"package.json": "1f37665b860b7f2f5b37cc931dcc9c4ac4fb82d1260fc14c0646fa4d92ade0b3",
|
|
115
117
|
"remote-runtime.js": "55dd7e577a822be17c2f337798a2d7f44ad0c5b6d68c46490e058282135c4658",
|
|
116
118
|
"slave-operations.js": "fec2e8addfff98f080284f24f722c1516124e5f6d12d8c03bb3c9cc0c475d1d6",
|
|
117
119
|
"state-store.js": "c56849a3f1b9c990128276e17963034a7836abacedc44f2e500d5732ebf9e451",
|
|
118
120
|
"test/batch-runner.test.js": "69f69150eadd18c1ad1bc54f429e6cb5bc0c4492e3c6a6e76c2ce6827780ae41",
|
|
119
121
|
"test/bootstrap-url.test.js": "62c112b1a38c59ea2c77f797e32873667b52db32386da661a6ae8410490126fe",
|
|
122
|
+
"test/command-arguments.test.js": "360c087edd0a589b28111bb82d06e3e73e93a6b22da0a57cb96f0a7e92a9bd5f",
|
|
120
123
|
"test/encrypted-frames.test.js": "cfb3195d3a58df7dc570bc7840a21c1841a53e0abc8b92840e0c24ed3435bcc1",
|
|
121
124
|
"test/handshake-crypto.test.js": "f11d6e95658f2055d8256bd7dfb66b39bedf95bf62a3cec5525b109bb6f9bc95",
|
|
122
125
|
"test/master-domain.test.js": "7298766db3e6b186cca0109f352766e1bf417a8af12a58dd1c2b78fd1a61ff24",
|