arisa 3.0.4 → 3.0.8
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 +10 -27
- package/README.md +7 -4
- package/package.json +1 -1
- package/src/core/agent/agent-manager.js +8 -0
- package/src/core/agent/project-instructions.js +11 -0
- package/src/core/agent/runtime-context.js +17 -0
- package/src/core/tools/tool-registry.js +38 -29
- package/src/runtime/paths.js +2 -2
package/AGENTS.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Arisa AGENTS
|
|
2
2
|
|
|
3
3
|
## Architecture
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
4
|
+
- Telegram transport handles inbound and outbound messaging.
|
|
5
|
+
- Pi Agent keeps one session per authorized chat.
|
|
6
|
+
- Every incoming or generated message or file becomes an artifact.
|
|
7
|
+
- A tool registry handles tool discovery, help lookup, config writes, and execution.
|
|
8
|
+
- Tools are isolated and each one has its own manifest, entrypoint, and config defaults.
|
|
9
9
|
|
|
10
10
|
## Main rule: everything is piped through artifacts
|
|
11
11
|
A pipe transforms one input artifact into one output artifact.
|
|
@@ -70,27 +70,14 @@ Example manual pipe:
|
|
|
70
70
|
## Missing config flow
|
|
71
71
|
If `run_tool` returns `missingConfig`, the agent should:
|
|
72
72
|
1. ask the user naturally in Telegram for the missing value
|
|
73
|
-
2. write the value
|
|
73
|
+
2. write the value with `set_tool_config`
|
|
74
74
|
3. retry the tool
|
|
75
75
|
|
|
76
76
|
Do not assume a rigid question/answer protocol. Continue the conversation naturally and infer the config value from the user reply when possible.
|
|
77
77
|
|
|
78
|
-
## Long-running work
|
|
79
|
-
If a task is likely to take noticeable time — for example creating a new tool, editing multiple files, or doing multi-step work — the agent should first acknowledge the request briefly and naturally, then continue the work.
|
|
80
|
-
|
|
81
|
-
The acknowledgment should:
|
|
82
|
-
- be short and clear
|
|
83
|
-
- tell the user the work is starting
|
|
84
|
-
- mention when the task may take a while
|
|
85
|
-
|
|
86
|
-
Examples:
|
|
87
|
-
- "Understood. I'll build that tool now. This may take a couple of minutes."
|
|
88
|
-
- "Got it. I'll inspect the project and make the change now."
|
|
89
|
-
|
|
90
78
|
## Tool creation
|
|
91
|
-
Do not assume specific future tools such as YouTube support exist.
|
|
92
79
|
If the user asks for a capability that is not currently available, first check whether an existing registered tool can satisfy the task.
|
|
93
|
-
If no existing tool can do it, the default attitude should be to propose creating a new CLI tool
|
|
80
|
+
If no existing tool can do it, the default attitude should be to propose creating a new CLI tool following the project conventions.
|
|
94
81
|
All newly created tools must document their help text, usage instructions, manifests, and user-facing operational strings in English.
|
|
95
82
|
Do not stop at "I cannot do that" when the task is realistically implementable through a new tool.
|
|
96
83
|
Prefer responses like:
|
|
@@ -100,15 +87,11 @@ Prefer responses like:
|
|
|
100
87
|
|
|
101
88
|
For example, if the user asks for live weather and no weather tool exists, the correct attitude is to propose building a weather tool for the bot rather than only saying real-time access is unavailable.
|
|
102
89
|
|
|
103
|
-
When creating or editing tools,
|
|
104
|
-
- config in `~/.arisa/tools/<tool>/config.js`
|
|
105
|
-
- temp/runtime files in `~/.arisa/tmp/tools/<tool>/`
|
|
106
|
-
- durable generated files should become artifacts in `~/.arisa/artifacts/`
|
|
107
|
-
|
|
90
|
+
When creating or editing tools, use the shared path helpers and the runtime paths provided in the prompt instead of assuming fixed locations.
|
|
108
91
|
Consult the local skill for that workflow when building new tools.
|
|
109
92
|
|
|
110
93
|
## Safety
|
|
111
|
-
- Do not install or run arbitrary tools outside registered
|
|
94
|
+
- Do not install or run arbitrary tools outside registered tool manifests in V1.
|
|
112
95
|
- Prefer tool manifests and CLI help over assumptions.
|
|
113
|
-
- Keep tool
|
|
96
|
+
- Keep tool config and runtime data inside the user runtime area.
|
|
114
97
|
- Be proactive about extending capabilities, but do it through the project's tool architecture, not through ad hoc one-off behavior.
|
package/README.md
CHANGED
|
@@ -46,7 +46,9 @@ This distinction is important. Some transformations belong to the transport/inpu
|
|
|
46
46
|
- media is stored as artifacts
|
|
47
47
|
|
|
48
48
|
### Tool model
|
|
49
|
-
|
|
49
|
+
Bundled tools live under `<arisa-install-dir>/tools/<tool-name>` and user-created tools live under `~/.arisa/tools/<tool-name>`.
|
|
50
|
+
|
|
51
|
+
Each tool folder contains:
|
|
50
52
|
|
|
51
53
|
- `package.json`
|
|
52
54
|
- `config.js`
|
|
@@ -57,7 +59,8 @@ Each tool is isolated from the root project and from other tools.
|
|
|
57
59
|
That isolation is part of the architecture:
|
|
58
60
|
|
|
59
61
|
- each tool has its own folder
|
|
60
|
-
-
|
|
62
|
+
- bundled tools have a local `config.js` for defaults/template values
|
|
63
|
+
- user-created tools can live entirely inside `~/.arisa/tools/<tool>/`
|
|
61
64
|
- each tool can have its own dependencies
|
|
62
65
|
- one tool can be changed or replaced without tightly coupling the rest of the system
|
|
63
66
|
|
|
@@ -74,8 +77,8 @@ node index.js run --request-file <json>
|
|
|
74
77
|
- artifact index is stored in `~/.arisa/state/artifacts.json`
|
|
75
78
|
- incoming Telegram attachments are stored directly in `~/.arisa/artifacts/`
|
|
76
79
|
- tool-specific secrets/config live in `~/.arisa/tools/<tool>/config.js`
|
|
77
|
-
-
|
|
78
|
-
- tool runtime temp files and generated outputs live
|
|
80
|
+
- user-created tools also live under `~/.arisa/tools/<tool>/`
|
|
81
|
+
- tool runtime temp files and generated outputs live under `~/.arisa/tools/<tool>/` (for example `tmp/` and `out/`)
|
|
79
82
|
- durable files should end up in `~/.arisa/artifacts/`
|
|
80
83
|
- Pi authentication can use either:
|
|
81
84
|
- an API key entered during bootstrap
|
package/package.json
CHANGED
|
@@ -3,6 +3,8 @@ import { mkdir, unlink } from "node:fs/promises";
|
|
|
3
3
|
import { createAgentSession, SessionManager, defineTool } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
import { Type } from "@sinclair/typebox";
|
|
5
5
|
import { createPiRuntime, hasProviderAuth } from "./pi-runtime.js";
|
|
6
|
+
import { loadProjectInstructions } from "./project-instructions.js";
|
|
7
|
+
import { buildAgentRuntimeContext } from "./runtime-context.js";
|
|
6
8
|
import { getChatDir, piAgentDir as agentDir } from "../../runtime/paths.js";
|
|
7
9
|
|
|
8
10
|
export class AgentManager {
|
|
@@ -74,6 +76,12 @@ export class AgentManager {
|
|
|
74
76
|
sessionManager: SessionManager.continueRecent(cwd)
|
|
75
77
|
});
|
|
76
78
|
|
|
79
|
+
const instructions = await loadProjectInstructions();
|
|
80
|
+
const runtimeContext = buildAgentRuntimeContext(chatId);
|
|
81
|
+
this.logger?.log("agent", `injecting project instructions for chat ${chatId}`);
|
|
82
|
+
this.logger?.log("agent", `runtime context for chat ${chatId}:\n${runtimeContext}`);
|
|
83
|
+
await session.prompt(`${instructions}\n\n${runtimeContext}\n\nAcknowledge with exactly: OK`);
|
|
84
|
+
|
|
77
85
|
const ctx = { session };
|
|
78
86
|
this.sessions.set(chatId, ctx);
|
|
79
87
|
return ctx;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
const instructionsPath = fileURLToPath(new URL("../../../AGENTS.md", import.meta.url));
|
|
5
|
+
let cachedInstructions = null;
|
|
6
|
+
|
|
7
|
+
export async function loadProjectInstructions() {
|
|
8
|
+
if (cachedInstructions !== null) return cachedInstructions;
|
|
9
|
+
cachedInstructions = await readFile(instructionsPath, "utf8");
|
|
10
|
+
return cachedInstructions;
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { arisaHomeDir, artifactsDir, getChatDir, stateDir, toolsDir } from "../../runtime/paths.js";
|
|
3
|
+
|
|
4
|
+
export const arisaInstallDir = fileURLToPath(new URL("../../..", import.meta.url));
|
|
5
|
+
export const bundledToolsDir = fileURLToPath(new URL("../../../tools", import.meta.url));
|
|
6
|
+
|
|
7
|
+
export function buildAgentRuntimeContext(chatId) {
|
|
8
|
+
return [
|
|
9
|
+
`arisaHomeDir: ${arisaHomeDir}`,
|
|
10
|
+
`arisaInstallDir: ${arisaInstallDir}`,
|
|
11
|
+
`bundledToolsDir: ${bundledToolsDir}`,
|
|
12
|
+
`userToolsDir: ${toolsDir}`,
|
|
13
|
+
`artifactsDir: ${artifactsDir}`,
|
|
14
|
+
`stateDir: ${stateDir}`,
|
|
15
|
+
`chatWorkspaceDir: ${getChatDir(chatId)}`
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { mkdir, readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
import {
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { getToolConfigPath, getToolTmpDir, toolsDir as userToolsRoot } from "../../runtime/paths.js";
|
|
5
6
|
import { loadToolConfig, parseConfigModule, writeToolConfig } from "./tool-config.js";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const bundledToolsRoot = fileURLToPath(new URL("../../../tools", import.meta.url));
|
|
9
|
+
const toolRoots = [
|
|
10
|
+
{ root: userToolsRoot, kind: "user" },
|
|
11
|
+
{ root: bundledToolsRoot, kind: "bundled" }
|
|
12
|
+
];
|
|
8
13
|
|
|
9
14
|
function runProcess(command, args, options = {}) {
|
|
10
15
|
return new Promise((resolve) => {
|
|
@@ -26,35 +31,39 @@ export class ToolRegistry {
|
|
|
26
31
|
async load() {
|
|
27
32
|
this.tools.clear();
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
entries = await readdir(toolsRoot, { withFileTypes: true });
|
|
32
|
-
} catch {
|
|
33
|
-
this.logger?.log("tools", `tools directory not found: ${toolsRoot}`);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (const entry of entries) {
|
|
38
|
-
if (!entry.isDirectory()) continue;
|
|
39
|
-
const toolDir = path.join(toolsRoot, entry.name);
|
|
40
|
-
const manifestPath = path.join(toolDir, "tool.manifest.json");
|
|
41
|
-
const configPath = path.join(toolDir, "config.js");
|
|
34
|
+
for (const { root, kind } of toolRoots) {
|
|
35
|
+
let entries = [];
|
|
42
36
|
try {
|
|
43
|
-
|
|
44
|
-
const configSource = await readFile(configPath, "utf8");
|
|
45
|
-
const defaults = parseConfigModule(configSource);
|
|
46
|
-
const config = await loadToolConfig(manifest.name, defaults);
|
|
47
|
-
this.tools.set(manifest.name, {
|
|
48
|
-
...manifest,
|
|
49
|
-
dir: toolDir,
|
|
50
|
-
entry: path.join(toolDir, manifest.entry || "index.js"),
|
|
51
|
-
localConfigPath: configPath,
|
|
52
|
-
configPath: getToolConfigPath(manifest.name),
|
|
53
|
-
defaults,
|
|
54
|
-
config
|
|
55
|
-
});
|
|
37
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
56
38
|
} catch {
|
|
57
|
-
|
|
39
|
+
this.logger?.log("tools", `${kind} tools directory not found: ${root}`);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (!entry.isDirectory()) continue;
|
|
45
|
+
const toolDir = path.join(root, entry.name);
|
|
46
|
+
const manifestPath = path.join(toolDir, "tool.manifest.json");
|
|
47
|
+
const configPath = path.join(toolDir, "config.js");
|
|
48
|
+
try {
|
|
49
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
50
|
+
if (this.tools.has(manifest.name)) continue;
|
|
51
|
+
const configSource = await readFile(configPath, "utf8");
|
|
52
|
+
const defaults = parseConfigModule(configSource);
|
|
53
|
+
const config = await loadToolConfig(manifest.name, defaults);
|
|
54
|
+
this.tools.set(manifest.name, {
|
|
55
|
+
...manifest,
|
|
56
|
+
dir: toolDir,
|
|
57
|
+
entry: path.join(toolDir, manifest.entry || "index.js"),
|
|
58
|
+
localConfigPath: configPath,
|
|
59
|
+
configPath: getToolConfigPath(manifest.name),
|
|
60
|
+
defaults,
|
|
61
|
+
config,
|
|
62
|
+
sourceKind: kind
|
|
63
|
+
});
|
|
64
|
+
} catch {
|
|
65
|
+
// ignore invalid tool dirs in v1
|
|
66
|
+
}
|
|
58
67
|
}
|
|
59
68
|
}
|
|
60
69
|
|
package/src/runtime/paths.js
CHANGED
|
@@ -10,7 +10,7 @@ export const serviceLogFile = path.join(stateDir, "arisa.log");
|
|
|
10
10
|
export const artifactsDir = path.join(arisaHomeDir, "artifacts");
|
|
11
11
|
export const artifactsIndexFile = path.join(stateDir, "artifacts.json");
|
|
12
12
|
export const piAgentDir = path.join(stateDir, "pi-agent");
|
|
13
|
-
export const chatsDir = path.join(
|
|
13
|
+
export const chatsDir = path.join(arisaHomeDir, "chats");
|
|
14
14
|
export const toolsDir = path.join(arisaHomeDir, "tools");
|
|
15
15
|
export const tmpDir = path.join(arisaHomeDir, "tmp");
|
|
16
16
|
|
|
@@ -27,7 +27,7 @@ export function getToolConfigPath(toolName) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export function getToolRuntimeDir(toolName) {
|
|
30
|
-
return
|
|
30
|
+
return getToolDir(toolName);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function getToolOutDir(toolName) {
|