arisa 3.0.6 → 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 CHANGED
@@ -1,11 +1,11 @@
1
1
  # Arisa AGENTS
2
2
 
3
3
  ## Architecture
4
- - `src/transport/telegram/*`: Telegram inbound and outbound transport.
5
- - `src/core/agent/*`: Pi Agent sessions, one per authorized chat.
6
- - `src/core/artifacts/*`: every incoming or generated message/file becomes an artifact.
7
- - `src/core/tools/*`: CLI tool registry, help lookup, config writes, execution.
8
- - `tools/*`: isolated tools. Each tool has `package.json`, `config.js`, `tool.manifest.json`, and `index.js`.
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 into `~/.arisa/tools/<tool>/config.js` with `set_tool_config`
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 under `tools/<tool-name>` following the project conventions.
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, follow the shared path helpers in `src/runtime/paths.js` and `src/core/tools/tool-config.js`:
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 `tools/*` manifests in V1.
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 configs inside `~/.arisa/tools/<tool>/config.js`.
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
- Each tool lives in its own folder under `tools/<tool-name>` and contains:
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
- - each tool has a local `config.js` only for defaults/template values
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
- - bundled tools and generated tools should both use the same source layout under `tools/<tool>/`
78
- - tool runtime temp files and generated outputs live in `~/.arisa/tmp/tools/<tool>/`
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arisa",
3
- "version": "3.0.6",
3
+ "version": "3.0.8",
4
4
  "description": "Telegram + Pi Agent modular assistant",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -4,6 +4,7 @@ import { createAgentSession, SessionManager, defineTool } from "@mariozechner/pi
4
4
  import { Type } from "@sinclair/typebox";
5
5
  import { createPiRuntime, hasProviderAuth } from "./pi-runtime.js";
6
6
  import { loadProjectInstructions } from "./project-instructions.js";
7
+ import { buildAgentRuntimeContext } from "./runtime-context.js";
7
8
  import { getChatDir, piAgentDir as agentDir } from "../../runtime/paths.js";
8
9
 
9
10
  export class AgentManager {
@@ -76,8 +77,10 @@ export class AgentManager {
76
77
  });
77
78
 
78
79
  const instructions = await loadProjectInstructions();
80
+ const runtimeContext = buildAgentRuntimeContext(chatId);
79
81
  this.logger?.log("agent", `injecting project instructions for chat ${chatId}`);
80
- await session.prompt(`${instructions}\n\nAcknowledge with exactly: OK`);
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`);
81
84
 
82
85
  const ctx = { session };
83
86
  this.sessions.set(chatId, ctx);
@@ -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 { getToolConfigPath, getToolTmpDir } from "../../runtime/paths.js";
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 toolsRoot = path.resolve("tools");
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
- let entries = [];
30
- try {
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
- const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
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
- // ignore invalid tool dirs in v1
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
 
@@ -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(stateDir, "chats");
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 path.join(tmpDir, "tools", toolName);
30
+ return getToolDir(toolName);
31
31
  }
32
32
 
33
33
  export function getToolOutDir(toolName) {