arisa 5.1.12 → 5.1.13

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.
@@ -76,7 +76,9 @@ el protocolo, la CLI y la documentación.
76
76
  - Linux con systemd es la primera plataforma operativa soportada. Master y Slave
77
77
  pueden coexistir en un host mediante homes, sockets, PID y logs separados.
78
78
  - El usuario dedicado es la opción recomendada. El usuario actual está
79
- permitido y root requiere siempre una elección y confirmación explícitas.
79
+ permitido y root requiere una elección explícita durante la instalación.
80
+ Una Slave elegida como root queda por defecto en modo host completo con todas
81
+ las capacidades v1; una política guardada posteriormente reemplaza ese default.
80
82
  - Los jobs dirigidos a Slaves offline se rechazan en v1.
81
83
  - La instalación remota acepta sólo tools del catálogo verificado y requiere
82
84
  confirmación. Nunca se instala código silenciosamente como root.
@@ -565,9 +567,10 @@ Ejecutar Arisa Slave como:
565
567
  ```
566
568
 
567
569
  Se admite elegir `root`. El perfil resultante y todos los informes de Master
568
- deben mostrar que Slave tiene autoridad root. El modo root restringido puede
569
- seguir aplicando las raíces y operaciones configuradas en la capa de aplicación.
570
- El modo root con host completo requiere otra confirmación explícita.
570
+ deben mostrar que Slave tiene autoridad root. Elegir root instala por defecto un
571
+ perfil de host completo con raíz `/` y todas las capacidades v1, incluida
572
+ `process.exec`. Una política persistida después puede volver a restringir las
573
+ raíces y operaciones en la capa de aplicación.
571
574
 
572
575
  Las restricciones de aplicación reducen el uso accidental, pero dejan de ser
573
576
  una frontera de seguridad si el propio proceso de Slave resulta comprometido:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arisa",
3
- "version": "5.1.12",
3
+ "version": "5.1.13",
4
4
  "description": "Telegram + Pi Agent modular assistant",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "version": 1,
3
3
  "repository": "https://github.com/clasen/Arisa.git",
4
- "commit": "5959b7378c94b4d9c4864eba9e1ef9aa564f104a",
4
+ "commit": "8438bffbc656de7f85e9a98fe7d8804b3c0224ea",
5
5
  "tools": {
6
6
  "master-slave": {
7
7
  "files": {
8
- "README.md": "39a546ca6046e622f92eff2d46f2c441beef43f150758799c68328bffd54efce",
8
+ "README.md": "8533804fda909c2f7bb0d05d40e2d6be9ef9126215bde70cd0ac4dda4afe9c55",
9
9
  "batch-runner.js": "be3939efe9c5800d3078c15a094f53db1094ef6e809d7826db7c47e3bec0c31b",
10
10
  "chat-state-store.js": "50de39aa33432733bb688eb01968e314a32b169324a66d0f0089b2a19ed9c880",
11
11
  "config.js": "ff1c1cb6701ae1da7a6eb1519ba96a416e48daa73c2155a6f93cf5d64f65cef8",
12
- "index.js": "39dc9bf44f94864bc4fd09ce695cc2e084e59eea48d09764f0fb6f998faf9dd3",
12
+ "index.js": "8586303b3d1bf937932159adbe9b69c1031e6a393300e7d3aca93910ddfab082",
13
13
  "lib/bootstrap-url.js": "f05925cc0f0dff0882f94f9908c6f2c80aa8632cd51f63389381dcbb0f73afb3",
14
14
  "lib/encrypted-frames.js": "47e1b11fafdbbcdfd86a0993a47a774e4f37d6386edeaf8ec1c8511b109728cf",
15
15
  "lib/handshake-crypto.js": "9728b117aad7de53f5f0e255c0fd865ee1a19633368730a7634334d374094578",
@@ -20,7 +20,7 @@
20
20
  "master-domain.js": "10b7dfb43eb9939eb5baec54f742076cc73a2add6589a0ff57c51bc79c236faf",
21
21
  "network-session.js": "db2d81a7ef47af29236d963b98e21414a11fef68e8b20895126eaad3f696ba3d",
22
22
  "package.json": "8e9177238ba878dd4b51e32e0ed58a39c5789186c22b19ad7a43ab6caf9dc469",
23
- "remote-runtime.js": "92c86adf575f2734c6f2f0d9004018f2019a8ac10288a1810425ce9fe0ffa129",
23
+ "remote-runtime.js": "55dd7e577a822be17c2f337798a2d7f44ad0c5b6d68c46490e058282135c4658",
24
24
  "slave-operations.js": "fec2e8addfff98f080284f24f722c1516124e5f6d12d8c03bb3c9cc0c475d1d6",
25
25
  "state-store.js": "c56849a3f1b9c990128276e17963034a7836abacedc44f2e500d5732ebf9e451",
26
26
  "test/batch-runner.test.js": "f14c6fddff5d6240008eef388df6a153e82490e534d06419d7bb267e4f4693a7",
@@ -31,7 +31,7 @@
31
31
  "test/network-session.test.js": "2e459346cc9364be705bc5f54691245044588b2cd03ae77e2e870dee6dfbcca6",
32
32
  "test/pairing-secret-store.test.js": "c51257365fd0b641c24768d710cdff2d1c5f8816bf90f66c2c01a226904cb43f",
33
33
  "test/profile-catalog.test.js": "d8e297bee413d53ec13f2e1779c13f2e019cb8304976d567eab0e9f1ca36637c",
34
- "test/remote-runtime.test.js": "a842c9934a3bf2e37c0a807ad2bc78f2f0d5adb38336c53369ddd92132bfb2b6",
34
+ "test/remote-runtime.test.js": "1161bd653dc47b9b8219bf848d0d395ce390f2bb7b58ea41e3e4270b1d504798",
35
35
  "test/slave-operations.test.js": "a84526ad7554c7d03192a68655b4637bec18750559a5c3436f7471e912ca74cc",
36
36
  "tool.manifest.json": "abf24c67b1d9ef0331ded59e04dcc092772419e74691c0d753ab51b40286d6f6"
37
37
  }
@@ -354,21 +354,23 @@ function buildStartupMessage(chatMeta = {}) {
354
354
  }
355
355
 
356
356
  export async function collectText(session, prompt, { logger, chatId, onSlowPrompt } = {}) {
357
- let text = "";
357
+ const assistantMessages = [];
358
+ let assistantMessage = "";
358
359
  let assistantErrorMessage = "";
359
- let shouldSeparateAssistantMessage = false;
360
360
  let slowPromptTimer = null;
361
+ const finishAssistantMessage = () => {
362
+ if (assistantMessage && !isSilentReply(assistantMessage)) {
363
+ assistantMessages.push(assistantMessage);
364
+ }
365
+ assistantMessage = "";
366
+ };
361
367
  const unsubscribe = session.subscribe((event) => {
362
368
  if (event.arisaPromptScoped === false) return;
363
369
  if (event.type === "message_start" && event.message.role === "assistant") {
364
- shouldSeparateAssistantMessage = text.trim().length > 0;
370
+ finishAssistantMessage();
365
371
  }
366
372
  if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
367
- if (shouldSeparateAssistantMessage && event.assistantMessageEvent.delta) {
368
- text += "\n\n";
369
- shouldSeparateAssistantMessage = false;
370
- }
371
- text += event.assistantMessageEvent.delta;
373
+ assistantMessage += event.assistantMessageEvent.delta;
372
374
  }
373
375
  if (event.type === "message_end" && event.message?.role === "assistant") {
374
376
  if (event.message.stopReason === "error") {
@@ -377,6 +379,7 @@ export async function collectText(session, prompt, { logger, chatId, onSlowPromp
377
379
  // Auto-compaction and retry can emit a transient error before a successful continuation.
378
380
  assistantErrorMessage = "";
379
381
  }
382
+ finishAssistantMessage();
380
383
  }
381
384
  const logMessage = sessionEventLogMessage(event);
382
385
  if (logMessage) logger?.log("agent", `chat ${chatId} ${logMessage}`);
@@ -402,7 +405,8 @@ export async function collectText(session, prompt, { logger, chatId, onSlowPromp
402
405
  throw new Error(assistantErrorMessage);
403
406
  }
404
407
 
405
- return text.trim();
408
+ finishAssistantMessage();
409
+ return assistantMessages.join("\n\n").trim();
406
410
  }
407
411
 
408
412
  export function isSilentReply(text) {
@@ -59,6 +59,44 @@ test("collectText preserves the final assistant error", async () => {
59
59
  await assert.rejects(() => collectText(session, "hello"), /terminal failure/);
60
60
  });
61
61
 
62
+ test("collectText drops a standalone silent event reply before a steered response", async () => {
63
+ const session = createSession([
64
+ { type: "message_start", message: { role: "assistant" } },
65
+ {
66
+ type: "message_update",
67
+ message: { role: "assistant" },
68
+ assistantMessageEvent: { type: "text_delta", delta: "NO_REPLY" }
69
+ },
70
+ { type: "message_end", message: { role: "assistant", stopReason: "stop" } },
71
+ { type: "message_start", message: { role: "assistant" } },
72
+ {
73
+ type: "message_update",
74
+ message: { role: "assistant" },
75
+ assistantMessageEvent: { type: "text_delta", delta: "The useful Telegram response." }
76
+ },
77
+ { type: "message_end", message: { role: "assistant", stopReason: "stop" } }
78
+ ]);
79
+
80
+ assert.equal(await collectText(session, "event followed by a Telegram steer"), "The useful Telegram response.");
81
+ });
82
+
83
+ test("collectText preserves useful text in the same message as a silent marker", async () => {
84
+ const session = createSession([
85
+ { type: "message_start", message: { role: "assistant" } },
86
+ {
87
+ type: "message_update",
88
+ message: { role: "assistant" },
89
+ assistantMessageEvent: { type: "text_delta", delta: "NO_REPLY\n\nThis text belongs to the same response." }
90
+ },
91
+ { type: "message_end", message: { role: "assistant", stopReason: "stop" } }
92
+ ]);
93
+
94
+ assert.equal(
95
+ await collectText(session, "one response that mentions the marker"),
96
+ "NO_REPLY\n\nThis text belongs to the same response."
97
+ );
98
+ });
99
+
62
100
  test("recognizes standalone silent reply markers", () => {
63
101
  assert.equal(isSilentReply("NO_REPLY"), true);
64
102
  assert.equal(isSilentReply("No reply needed."), true);
@@ -3,6 +3,7 @@ import crypto from "node:crypto";
3
3
  import { cp, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
6
7
  import test from "node:test";
7
8
  import {
8
9
  installBundledOfficialTool,
@@ -59,6 +60,16 @@ test("verifies the exact file set and digests", async (t) => {
59
60
  await assert.rejects(() => verifyOfficialToolTree(source, files), /unexpected=extra.js/);
60
61
  });
61
62
 
63
+ test("bundled master-slave lock matches the catalog source", async () => {
64
+ const lock = JSON.parse(await readFile(new URL("../src/official-tools.lock.json", import.meta.url), "utf8"));
65
+ const source = fileURLToPath(new URL("../../tools/master-slave/", import.meta.url));
66
+
67
+ assert.deepEqual(
68
+ await verifyOfficialToolTree(source, lock.tools["master-slave"].files),
69
+ { files: Object.keys(lock.tools["master-slave"].files).length }
70
+ );
71
+ });
72
+
62
73
  test("rejects symbolic links before deployment", async (t) => {
63
74
  const { source, files } = await fixture(t);
64
75
  await symlink(path.join(source, "index.js"), path.join(source, "link.js"));