arisa 4.1.14 → 4.2.0
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
CHANGED
|
@@ -23,6 +23,15 @@ Do not build runtime paths by hand. Use `src/runtime/paths.js`:
|
|
|
23
23
|
|
|
24
24
|
Tools receive `chatId` from the registry. Any persisted or indexed user content must be scoped by chat. Avoid ad hoc roots like `~/.arisa/state/<toolName>`, `~/.arisa/state/chats`, or runtime data inside `~/.arisa/tools/<toolName>`.
|
|
25
25
|
|
|
26
|
+
## Tool config rules
|
|
27
|
+
Every tool has a local `config.js` that exports generic, non-user-specific defaults. Keep user credentials, deployment-specific URLs, account ids, phone numbers, and other per-user values empty or neutral there; document required values in `tool.manifest.json` `configSchema`.
|
|
28
|
+
|
|
29
|
+
Tools must consume config through `src/core/tools/tool-config.js`:
|
|
30
|
+
- `loadToolConfig(toolName, defaults)` for global tools and daemons that run as one shared process.
|
|
31
|
+
- `loadToolConfig(toolName, defaults, request.chatId)` inside `run()` for request-scoped tools that should honor per-chat overrides.
|
|
32
|
+
|
|
33
|
+
Config precedence is a shallow merge: tool defaults -> global tool config (`~/.arisa/tools/<toolName>/config.js`) -> chat tool config (`~/.arisa/chats/<chatId>/config/tools/<toolName>/config.js`). Per-request `args` may override loaded config for that invocation only; do not persist request args as config unless the user explicitly asked to set a default.
|
|
34
|
+
|
|
26
35
|
## Main rule: everything is piped through artifacts
|
|
27
36
|
A pipe transforms one input artifact into one output artifact.
|
|
28
37
|
Examples:
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { stat, unlink } from "node:fs/promises";
|
|
3
|
-
import { createAgentSession, SessionManager, defineTool } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { readFile, stat, unlink } from "node:fs/promises";
|
|
3
|
+
import { createAgentSession, DefaultResourceLoader, SessionManager, defineTool } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Type } from "@sinclair/typebox";
|
|
5
5
|
import { createPiRuntime, hasProviderAuth } from "./pi-runtime.js";
|
|
6
|
-
import { arisaInstallDir, buildAgentRuntimeContext } from "./runtime-context.js";
|
|
6
|
+
import { appendArisaAgentsFile, arisaAgentsFile, arisaInstallDir, buildAgentRuntimeContext } from "./runtime-context.js";
|
|
7
7
|
import { withTimeout } from "./prompt-timeout.js";
|
|
8
8
|
import { buildPiToolPolicy, getCoreCodingTools } from "./core-tools.js";
|
|
9
9
|
import { createSystemShellTool } from "./system-shell-tool.js";
|
|
@@ -88,6 +88,17 @@ async function assertDirectory(dir, label) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
async function createArisaResourceLoader({ cwd, agentDir }) {
|
|
92
|
+
const arisaAgentsContent = await readFile(arisaAgentsFile, "utf8");
|
|
93
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
94
|
+
cwd,
|
|
95
|
+
agentDir,
|
|
96
|
+
agentsFilesOverride: (current) => appendArisaAgentsFile(current, arisaAgentsContent)
|
|
97
|
+
});
|
|
98
|
+
await resourceLoader.reload();
|
|
99
|
+
return resourceLoader;
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
export class AgentManager {
|
|
92
103
|
constructor({ config, artifactStore, toolRegistry, taskStore, logger }) {
|
|
93
104
|
this.config = config;
|
|
@@ -188,9 +199,14 @@ export class AgentManager {
|
|
|
188
199
|
...this.createTools(telegram, chatId, policy),
|
|
189
200
|
createSystemShellTool({ workspaceDir: policy.workspaceDir, shell: policy.shell })
|
|
190
201
|
];
|
|
202
|
+
const resourceLoader = await createArisaResourceLoader({
|
|
203
|
+
cwd: policy.workspaceDir,
|
|
204
|
+
agentDir: arisaHomeDir
|
|
205
|
+
});
|
|
191
206
|
const { session } = await createAgentSession({
|
|
192
207
|
cwd: policy.workspaceDir,
|
|
193
208
|
agentDir: arisaHomeDir,
|
|
209
|
+
resourceLoader,
|
|
194
210
|
authStorage,
|
|
195
211
|
modelRegistry,
|
|
196
212
|
model,
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { fileURLToPath } from "node:url";
|
|
2
3
|
import { arisaHomeDir, arisaPackageDir, chatsDir, stateDir, toolStateDir, toolsDir } from "../../runtime/paths.js";
|
|
3
4
|
|
|
4
5
|
export const arisaInstallDir = fileURLToPath(new URL("../../..", import.meta.url));
|
|
6
|
+
export const arisaAgentsFile = path.join(arisaPackageDir, "AGENTS.md");
|
|
7
|
+
|
|
8
|
+
export function appendArisaAgentsFile(current, content, agentsFilePath = arisaAgentsFile) {
|
|
9
|
+
return {
|
|
10
|
+
...current,
|
|
11
|
+
agentsFiles: [
|
|
12
|
+
...(current.agentsFiles || []).filter((file) => path.resolve(file.path) !== path.resolve(agentsFilePath)),
|
|
13
|
+
{ path: agentsFilePath, content }
|
|
14
|
+
]
|
|
15
|
+
};
|
|
16
|
+
}
|
|
5
17
|
|
|
6
18
|
function formatCoreTools(coreTools = []) {
|
|
7
19
|
const enabled = coreTools
|
|
@@ -20,6 +32,7 @@ export function buildAgentRuntimeContext({ workspaceDir = arisaHomeDir, coreTool
|
|
|
20
32
|
`toolStateDir: ${toolStateDir}`,
|
|
21
33
|
`chatsDir: ${chatsDir}`,
|
|
22
34
|
`stateDir: ${stateDir}`,
|
|
35
|
+
`arisaAgentsFile: ${arisaAgentsFile}`,
|
|
23
36
|
`enabledCoreTools: ${formatCoreTools(coreTools)}`,
|
|
24
37
|
"Guidance: create or edit user tools under userToolsDir. Treat arisaPackageDir/arisaInstallDir as Arisa core and modify it only when the user explicitly asks for core changes."
|
|
25
38
|
].join("\n");
|
|
@@ -7,6 +7,11 @@ function id() {
|
|
|
7
7
|
return crypto.randomUUID();
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function writeArtifactFile(filePath, content) {
|
|
11
|
+
if (typeof content === "string") return writeFile(filePath, content, "utf8");
|
|
12
|
+
return writeFile(filePath, content);
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
class ChatArtifactStore {
|
|
11
16
|
constructor(chatId) {
|
|
12
17
|
this.chatId = String(chatId);
|
|
@@ -81,7 +86,7 @@ class ChatArtifactStore {
|
|
|
81
86
|
const dir = path.join(this.rootDir, artifactId);
|
|
82
87
|
await mkdir(dir, { recursive: true });
|
|
83
88
|
const destPath = path.join(dir, fileName);
|
|
84
|
-
await
|
|
89
|
+
await writeArtifactFile(destPath, content);
|
|
85
90
|
const artifact = {
|
|
86
91
|
id: artifactId,
|
|
87
92
|
chatId: this.chatId,
|
|
@@ -4,6 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
import { buildPiToolPolicy } from "../src/core/agent/core-tools.js";
|
|
7
|
+
import { appendArisaAgentsFile, arisaAgentsFile } from "../src/core/agent/runtime-context.js";
|
|
7
8
|
import { createSystemShellTool } from "../src/core/agent/system-shell-tool.js";
|
|
8
9
|
import { applyRuntimeOverrides } from "../src/runtime/create-app.js";
|
|
9
10
|
import { arisaHomeDir } from "../src/runtime/paths.js";
|
|
@@ -89,3 +90,22 @@ test("system_shell runs commands from the configured workspace", async () => {
|
|
|
89
90
|
assert.equal(result.details.exitCode, 0);
|
|
90
91
|
assert.equal(result.details.stdout, realWorkspaceDir);
|
|
91
92
|
});
|
|
93
|
+
|
|
94
|
+
test("adds Arisa AGENTS.md to Pi context files without duplicating it", () => {
|
|
95
|
+
const current = {
|
|
96
|
+
agentsFiles: [
|
|
97
|
+
{ path: "/workspace/AGENTS.md", content: "# Workspace" },
|
|
98
|
+
{ path: arisaAgentsFile, content: "old" }
|
|
99
|
+
],
|
|
100
|
+
diagnostics: []
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const next = appendArisaAgentsFile(current, "# Arisa AGENTS");
|
|
104
|
+
|
|
105
|
+
assert.deepEqual(next.diagnostics, []);
|
|
106
|
+
assert.deepEqual(
|
|
107
|
+
next.agentsFiles.map((file) => file.path),
|
|
108
|
+
["/workspace/AGENTS.md", arisaAgentsFile]
|
|
109
|
+
);
|
|
110
|
+
assert.equal(next.agentsFiles.at(-1).content, "# Arisa AGENTS");
|
|
111
|
+
});
|
|
@@ -83,6 +83,20 @@ test("creates generated file artifacts", async () => {
|
|
|
83
83
|
assert.equal(artifact.mimeType, "text/markdown");
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
+
test("writes generated text file artifacts as UTF-8", async () => {
|
|
87
|
+
await resetHome();
|
|
88
|
+
const content = "# Español\nÑandú\n";
|
|
89
|
+
const artifact = await new ArtifactStore().forChat("chat-1").createGeneratedFile({
|
|
90
|
+
fileName: "reply.txt",
|
|
91
|
+
content,
|
|
92
|
+
kind: "document",
|
|
93
|
+
mimeType: "text/plain",
|
|
94
|
+
source: { type: "assistant" }
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
assert.deepEqual(await readFile(artifact.path), Buffer.from(content, "utf8"));
|
|
98
|
+
});
|
|
99
|
+
|
|
86
100
|
test("keeps artifact indexes isolated by chat", async () => {
|
|
87
101
|
await resetHome();
|
|
88
102
|
const store = new ArtifactStore();
|