arisa 4.0.24 → 4.1.4
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 +6 -4
- package/package.json +1 -1
- package/src/core/agent/agent-manager.js +69 -12
- package/src/core/agent/core-tools.js +139 -0
- package/src/core/agent/runtime-context.js +14 -3
- package/src/core/agent/system-shell-tool.js +207 -0
- package/src/index.js +44 -10
- package/src/runtime/bootstrap.js +452 -36
- package/src/runtime/create-app.js +52 -9
- package/src/runtime/ipc/ipc-server.js +21 -8
- package/src/runtime/paths.js +10 -1
- package/src/transport/telegram/bot.js +4 -3
- package/test/agent-tool-policy.test.js +91 -0
- package/test/ipc-server.test.js +10 -0
package/src/runtime/paths.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import crypto from "node:crypto";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -9,7 +10,15 @@ export const stateDir = path.join(arisaHomeDir, "state");
|
|
|
9
10
|
export const configFile = path.join(stateDir, "config.json");
|
|
10
11
|
export const servicePidFile = path.join(stateDir, "arisa.pid");
|
|
11
12
|
export const serviceLogFile = path.join(stateDir, "arisa.log");
|
|
12
|
-
export
|
|
13
|
+
export function createIpcSocketPath({ homeDir = arisaHomeDir, platform = process.platform } = {}) {
|
|
14
|
+
if (platform === "win32") {
|
|
15
|
+
const suffix = crypto.createHash("sha256").update(homeDir).digest("hex").slice(0, 16);
|
|
16
|
+
return `\\\\.\\pipe\\arisa-${suffix}`;
|
|
17
|
+
}
|
|
18
|
+
return path.join(homeDir, "state", "arisa.sock");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const arisaIpcSocketFile = createIpcSocketPath();
|
|
13
22
|
export const tasksFile = path.join(stateDir, "tasks.json");
|
|
14
23
|
export const toolsDir = path.join(arisaHomeDir, "tools");
|
|
15
24
|
export const chatsDir = path.join(arisaHomeDir, "chats");
|
|
@@ -74,7 +74,8 @@ function buildPrompt({ ctx, artifact, transcript, toolResult }) {
|
|
|
74
74
|
parts.push(`Important: pre-reasoning media normalization could not be completed, so you do not have a transcript for this audio/video message.`);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
parts.push(`
|
|
77
|
+
parts.push(`Use read/write/edit for file work in the active workspace, bash for bash-compatible commands, and system_shell for native system commands such as PowerShell on Windows.`);
|
|
78
|
+
parts.push(`If you need an Arisa modular CLI tool, use list_tools/tool_help/run_tool.`);
|
|
78
79
|
parts.push(`If a tool config is missing, ask the user naturally and then use set_tool_config.`);
|
|
79
80
|
parts.push(`If the user wants a generated media reply, use send_media_reply.`);
|
|
80
81
|
return parts.join("\n");
|
|
@@ -130,7 +131,7 @@ async function buildAsyncTaskPrompt({ task, artifactStore, toolRegistry, logger
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
parts.push("Treat this as a new request for the chat and fulfill it now.");
|
|
133
|
-
parts.push("If needed, use tools.");
|
|
134
|
+
parts.push("If needed, use read/write/edit, bash, system_shell, or Arisa modular tools via run_tool.");
|
|
134
135
|
return parts.filter(Boolean).join("\n");
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -142,7 +143,7 @@ function buildAsyncEventPrompt(task) {
|
|
|
142
143
|
task.payload.prompt ? `event: ${task.payload.prompt}` : null,
|
|
143
144
|
"A polling checker detected this external event. Evaluate it and decide the next action.",
|
|
144
145
|
"If it warrants no action, you may stay silent.",
|
|
145
|
-
"If needed, use tools."
|
|
146
|
+
"If needed, use read/write/edit, bash, system_shell, or Arisa modular tools via run_tool."
|
|
146
147
|
].filter(Boolean).join("\n");
|
|
147
148
|
}
|
|
148
149
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, realpath } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { buildPiToolPolicy } from "../src/core/agent/core-tools.js";
|
|
7
|
+
import { createSystemShellTool } from "../src/core/agent/system-shell-tool.js";
|
|
8
|
+
import { applyRuntimeOverrides } from "../src/runtime/create-app.js";
|
|
9
|
+
import { arisaHomeDir } from "../src/runtime/paths.js";
|
|
10
|
+
|
|
11
|
+
test("builds default Pi tool policy for the Arisa home workspace", () => {
|
|
12
|
+
const policy = buildPiToolPolicy({ config: { pi: {} } });
|
|
13
|
+
|
|
14
|
+
assert.equal(policy.workspaceDir, path.resolve(arisaHomeDir));
|
|
15
|
+
assert.equal(policy.tools, undefined);
|
|
16
|
+
assert.equal(policy.excludeTools, undefined);
|
|
17
|
+
assert.deepEqual(
|
|
18
|
+
policy.coreTools.filter((tool) => tool.enabled).map((tool) => tool.name),
|
|
19
|
+
["read", "bash", "edit", "write"]
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("keeps Arisa custom tools available when pi.tools is configured", () => {
|
|
24
|
+
const policy = buildPiToolPolicy({
|
|
25
|
+
config: {
|
|
26
|
+
pi: {
|
|
27
|
+
tools: "read,bash",
|
|
28
|
+
excludeTools: "bash",
|
|
29
|
+
workspaceDir: "~/arisa-workspace",
|
|
30
|
+
shellTimeoutMs: "120000"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
customToolNames: ["run_tool", "system_shell"]
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
assert.deepEqual(policy.tools, ["read", "bash", "run_tool", "system_shell"]);
|
|
37
|
+
assert.deepEqual(policy.excludeTools, ["bash"]);
|
|
38
|
+
assert.equal(policy.shell.timeoutMs, 120000);
|
|
39
|
+
assert.equal(policy.workspaceDir, path.join(os.homedir(), "arisa-workspace"));
|
|
40
|
+
assert.deepEqual(
|
|
41
|
+
policy.coreTools.filter((tool) => tool.enabled).map((tool) => tool.name),
|
|
42
|
+
["read"]
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("applies runtime overrides without dropping persisted Pi config", () => {
|
|
47
|
+
const config = {
|
|
48
|
+
telegram: { token: "token" },
|
|
49
|
+
pi: {
|
|
50
|
+
provider: "openai-codex",
|
|
51
|
+
model: "gpt-5.5",
|
|
52
|
+
apiKey: "persisted"
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const next = applyRuntimeOverrides(config, {
|
|
57
|
+
pi: {
|
|
58
|
+
model: "anthropic/claude-opus-4-5",
|
|
59
|
+
workspaceDir: "/tmp/arisa",
|
|
60
|
+
tools: "read,bash",
|
|
61
|
+
excludeTools: "write",
|
|
62
|
+
shellTimeoutMs: "90000"
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
assert.equal(next.pi.provider, "anthropic");
|
|
67
|
+
assert.equal(next.pi.model, "claude-opus-4-5");
|
|
68
|
+
assert.equal(next.pi.apiKey, "persisted");
|
|
69
|
+
assert.equal(next.pi.workspaceDir, "/tmp/arisa");
|
|
70
|
+
assert.deepEqual(next.pi.tools, ["read", "bash"]);
|
|
71
|
+
assert.deepEqual(next.pi.excludeTools, ["write"]);
|
|
72
|
+
assert.equal(next.pi.shellTimeoutMs, 90000);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("system_shell runs commands from the configured workspace", async () => {
|
|
76
|
+
const workspaceDir = await mkdtemp(path.join(os.tmpdir(), "arisa-shell-"));
|
|
77
|
+
const tool = createSystemShellTool({
|
|
78
|
+
workspaceDir,
|
|
79
|
+
shell: { timeoutMs: 10_000 }
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const result = await tool.execute("call-1", {
|
|
83
|
+
command: "node -e \"process.stdout.write(process.cwd())\""
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const realWorkspaceDir = await realpath(workspaceDir);
|
|
87
|
+
assert.equal(result.isError, false);
|
|
88
|
+
assert.equal(result.details.cwd, workspaceDir);
|
|
89
|
+
assert.equal(result.details.exitCode, 0);
|
|
90
|
+
assert.equal(result.details.stdout, realWorkspaceDir);
|
|
91
|
+
});
|
package/test/ipc-server.test.js
CHANGED
|
@@ -7,6 +7,7 @@ import test from "node:test";
|
|
|
7
7
|
import { createArisaClient } from "../src/core/tools/ipc-client.js";
|
|
8
8
|
import { createArisaCapabilities } from "../src/runtime/arisa-capabilities.js";
|
|
9
9
|
import { createIpcServer } from "../src/runtime/ipc/ipc-server.js";
|
|
10
|
+
import { createIpcSocketPath } from "../src/runtime/paths.js";
|
|
10
11
|
|
|
11
12
|
function createFakeArtifactStore() {
|
|
12
13
|
const stores = new Map();
|
|
@@ -67,6 +68,15 @@ function createCapabilities(overrides = {}) {
|
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
test("uses a Windows named pipe for IPC on Windows", () => {
|
|
72
|
+
const socketPath = createIpcSocketPath({
|
|
73
|
+
homeDir: "C:\\Users\\Arisa\\.arisa",
|
|
74
|
+
platform: "win32"
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
assert.match(socketPath, /^\\\\\.\\pipe\\arisa-[a-f0-9]{16}$/);
|
|
78
|
+
});
|
|
79
|
+
|
|
70
80
|
function wait(ms) {
|
|
71
81
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
72
82
|
}
|