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.
Files changed (37) hide show
  1. package/AGENTS.md +0 -2
  2. package/package.json +1 -1
  3. package/src/core/agent/agent-manager.js +39 -489
  4. package/src/core/agent/agent-session-lifecycle.js +181 -0
  5. package/src/core/agent/pi-capability-tools.js +183 -0
  6. package/src/core/artifacts/artifact-store.js +73 -17
  7. package/src/core/capabilities/capability-service.js +340 -0
  8. package/src/core/tasks/task-routing.js +7 -0
  9. package/src/core/tasks/task-runner.js +53 -0
  10. package/src/core/tasks/task-store.js +316 -92
  11. package/src/core/tools/tool-output-materializer.js +5 -5
  12. package/src/official-tools.lock.json +7 -4
  13. package/src/runtime/arisa-capabilities.js +51 -242
  14. package/src/runtime/create-app.js +10 -1
  15. package/src/runtime/create-headless-app.js +5 -2
  16. package/src/transport/telegram/bot.js +112 -368
  17. package/src/transport/telegram/chat-queue.js +72 -6
  18. package/src/transport/telegram/prompt-builders.js +9 -0
  19. package/src/transport/telegram/task-dispatcher.js +73 -36
  20. package/src/transport/telegram/telegram-auth-controller.js +180 -0
  21. package/src/transport/telegram/telegram-session-bridge.js +170 -0
  22. package/src/transport/telegram/telegram-tools-command.js +28 -0
  23. package/src/transport/telegram/telegram-workspace-controller.js +66 -0
  24. package/test/agent-session-lifecycle.test.js +58 -0
  25. package/test/artifact-store.test.js +38 -2
  26. package/test/capabilities-security.test.js +58 -0
  27. package/test/context-and-task-bounds.test.js +76 -1
  28. package/test/device-code-message.test.js +9 -0
  29. package/test/media-caption.test.js +1 -1
  30. package/test/pi-capability-tools.test.js +65 -0
  31. package/test/session-start-operational-notes.test.js +1 -1
  32. package/test/task-idempotency.test.js +40 -0
  33. package/test/task-routing.test.js +62 -0
  34. package/test/task-store.test.js +178 -6
  35. package/test/telegram-task-dispatcher.test.js +99 -23
  36. package/test/telegram-text-artifact.test.js +13 -2
  37. 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
- return JSON.parse(await readFile(tasksFile, "utf8"));
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
- await writeFile(tasksFile, `${JSON.stringify(tasks, null, 2)}\n`, "utf8");
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 normalizeTask(task, defaults = {}) {
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
- ...(defaults.payload || {}),
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(Date.now() + (Number(task.recurrence.everySeconds) * 1000)).toISOString();
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.tasks = await loadTasksFile();
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
- await saveTasksFile(this.tasks || []);
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
- await this.init();
69
- const normalized = normalizeTask(task, defaults);
70
- this.tasks.push(normalized);
71
- await this.save();
72
- return normalized;
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(tasks = [], defaults = {}) {
76
- const created = [];
77
- for (const task of tasks) {
78
- created.push(await this.add(task, defaults));
79
- }
80
- return created;
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
- await this.reload();
85
- const now = Date.now();
86
- const due = [];
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
- if (due.length) await this.save();
99
- return due;
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
- await this.reload();
104
- const recovered = [];
105
- const updatedAt = new Date().toISOString();
250
+ return this.mutate(async (tasks) => {
251
+ const recovered = [];
252
+ let compacted = false;
253
+ const now = Date.now();
106
254
 
107
- for (const task of this.tasks) {
108
- if (task.status !== "running") continue;
109
- task.status = "pending";
110
- task.updatedAt = updatedAt;
111
- recovered.push({ ...task });
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
- if (recovered.length) await this.save();
115
- return recovered;
283
+ return { result: recovered, changed: recovered.length > 0 || compacted };
284
+ });
116
285
  }
117
286
 
118
287
  async complete(taskId) {
119
- await this.init();
120
- const task = this.tasks.find((item) => item.id === taskId);
121
- if (!task) return null;
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
- const nextRunAt = computeNextRunAt(task);
124
- if (nextRunAt) {
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 = nextRunAt;
127
- task.lastRunAt = new Date().toISOString();
128
- } else {
129
- task.status = "done";
130
- task.completedAt = new Date().toISOString();
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
- await this.init();
139
- const task = this.tasks.find((item) => item.id === taskId);
140
- if (!task) return null;
141
- task.status = "failed";
142
- task.error = error;
143
- task.updatedAt = new Date().toISOString();
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
- return this.tasks.find((item) => item.id === taskId) || null;
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
- await this.reload();
165
- const index = this.tasks.findIndex((item) => item.id === taskId);
166
- if (index === -1) return null;
167
- const [task] = this.tasks.splice(index, 1);
168
- await this.save();
169
- return task;
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
- await this.reload();
174
- const removed = [];
175
- this.tasks = this.tasks.filter((task) => {
176
- if (filter.chatId && task.payload?.chatId !== filter.chatId) return true;
177
- if (filter.status && task.status !== filter.status) return true;
178
- if (task.status === "done" || task.status === "failed") return true;
179
- removed.push({ ...task });
180
- return false;
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
- result.asyncTasks = await taskStore.addMany(result.asyncTasks || [result.asyncTask], {
31
- payload: {
32
- chatId,
33
- ...(taskContext ? { telegramContext: taskContext } : {})
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": "aaec12e4f63f4959462b26380e7d4c1a60562586",
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": "84d2841d8df31c07065d4fee6e434dc43cad241f3abf63e95efafad51ed69fcb",
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": "8586303b3d1bf937932159adbe9b69c1031e6a393300e7d3aca93910ddfab082",
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": "ef4824ccb46ddb3f901dd6e7a94abbd458152a2e73292ec409470643f45ff396",
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",