arisa 3.0.8 → 3.0.9
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 +22 -11
- package/README.md +8 -6
- package/package.json +1 -1
- package/src/core/agent/agent-manager.js +7 -11
- package/src/core/agent/runtime-context.js +3 -4
- package/src/index.js +23 -0
- package/src/runtime/paths.js +0 -8
- package/src/runtime/pi-package-manager.js +49 -0
package/AGENTS.md
CHANGED
|
@@ -76,19 +76,30 @@ If `run_tool` returns `missingConfig`, the agent should:
|
|
|
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
78
|
## Tool creation
|
|
79
|
-
|
|
80
|
-
If no existing tool can do it, the default attitude should be to propose creating a new CLI tool following the project conventions.
|
|
81
|
-
All newly created tools must document their help text, usage instructions, manifests, and user-facing operational strings in English.
|
|
82
|
-
Do not stop at "I cannot do that" when the task is realistically implementable through a new tool.
|
|
83
|
-
Prefer responses like:
|
|
84
|
-
- identify that no current tool satisfies the request
|
|
85
|
-
- state that the missing capability can be added
|
|
86
|
-
- propose or start creating the tool needed to fulfill the request
|
|
79
|
+
Reason in terms of capabilities, not tool names.
|
|
87
80
|
|
|
88
|
-
|
|
81
|
+
When the user asks for something new:
|
|
82
|
+
1. check whether an existing registered tool can already satisfy the task
|
|
83
|
+
2. also check whether the task can be satisfied indirectly through an existing capability
|
|
84
|
+
3. only propose creating a new tool when the needed capability is truly missing
|
|
89
85
|
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
Do not stop at "I cannot do that" when the task is realistically implementable through the tool architecture.
|
|
87
|
+
The default attitude is:
|
|
88
|
+
- identify that no current tool satisfies the request
|
|
89
|
+
- state that the missing capability can be added
|
|
90
|
+
- propose or start creating the needed tool
|
|
91
|
+
|
|
92
|
+
When creating or editing tools:
|
|
93
|
+
- use the shared path helpers and the runtime paths provided in the prompt instead of assuming fixed locations
|
|
94
|
+
- consult the local skill for that workflow when building new tools
|
|
95
|
+
- keep all help text, usage instructions, manifests, and user-facing operational strings in English
|
|
96
|
+
- follow the One Thing Rule: each function or method should do one thing well; if it mixes low-level operations with high-level policy, split it into smaller focused units
|
|
97
|
+
|
|
98
|
+
## Dependency installation
|
|
99
|
+
Arisa installs tool dependencies itself.
|
|
100
|
+
- Prefer `pnpm install`.
|
|
101
|
+
- Fall back to `npm install`.
|
|
102
|
+
- Do not ask the user to do it manually.
|
|
92
103
|
|
|
93
104
|
## Safety
|
|
94
105
|
- Do not install or run arbitrary tools outside registered tool manifests in V1.
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Arisa is a personal Telegram assistant powered by Pi Agent.
|
|
|
4
4
|
|
|
5
5
|
## Origin
|
|
6
6
|
|
|
7
|
-
The initial inspiration was [OpenClaw](https://github.com/openclaw/openclaw). OpenClaw has interesting ideas but carries
|
|
7
|
+
The initial inspiration was [OpenClaw](https://github.com/openclaw/openclaw). OpenClaw has interesting ideas but carries a lot of weight (about **185 MB**) compared to Arisa (**76.7 kB**): when it generates tools they end up disorganized, and the overall framework feels overloaded.
|
|
8
8
|
|
|
9
9
|
The real heart of OpenClaw is Pi Agent: a [minimal terminal coding harness](https://www.youtube.com/watch?v=Dli5slNaJu0) that lets an AI agent reason and act with very little infrastructure. That part is genuinely good.
|
|
10
10
|
|
|
@@ -99,11 +99,13 @@ arisa
|
|
|
99
99
|
Command modes:
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
|
-
arisa
|
|
103
|
-
arisa start
|
|
104
|
-
arisa stop
|
|
105
|
-
arisa status
|
|
106
|
-
arisa flush
|
|
102
|
+
arisa # foreground, blocking
|
|
103
|
+
arisa start # start in background
|
|
104
|
+
arisa stop # stop background service
|
|
105
|
+
arisa status # show background service status
|
|
106
|
+
arisa flush # remove ~/.arisa
|
|
107
|
+
arisa install <source> # install a Pi package into Arisa's runtime
|
|
108
|
+
arisa remove <source> # remove a Pi package from Arisa's runtime
|
|
107
109
|
```
|
|
108
110
|
|
|
109
111
|
## Bootstrap flow
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { 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
6
|
import { loadProjectInstructions } from "./project-instructions.js";
|
|
7
|
-
import { buildAgentRuntimeContext } from "./runtime-context.js";
|
|
8
|
-
import {
|
|
7
|
+
import { arisaInstallDir, buildAgentRuntimeContext } from "./runtime-context.js";
|
|
8
|
+
import { arisaHomeDir } from "../../runtime/paths.js";
|
|
9
9
|
|
|
10
10
|
export class AgentManager {
|
|
11
11
|
constructor({ config, artifactStore, toolRegistry, logger }) {
|
|
@@ -50,7 +50,6 @@ export class AgentManager {
|
|
|
50
50
|
return this.sessions.get(chatId);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
await mkdir(agentDir, { recursive: true });
|
|
54
53
|
const { authStorage, modelRegistry } = createPiRuntime({
|
|
55
54
|
provider: this.config.pi.provider,
|
|
56
55
|
apiKey: this.config.pi.apiKey
|
|
@@ -61,23 +60,20 @@ export class AgentManager {
|
|
|
61
60
|
throw new Error(`No auth found for ${this.config.pi.provider}. Re-run bootstrap and complete login for this provider before Telegram starts.`);
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
const cwd = getChatDir(chatId);
|
|
65
|
-
await mkdir(cwd, { recursive: true });
|
|
66
|
-
|
|
67
63
|
this.logger?.log("agent", `creating session for chat ${chatId}`);
|
|
68
64
|
const customTools = this.createTools(telegram);
|
|
69
65
|
const { session } = await createAgentSession({
|
|
70
|
-
cwd,
|
|
71
|
-
agentDir,
|
|
66
|
+
cwd: arisaInstallDir,
|
|
67
|
+
agentDir: arisaHomeDir,
|
|
72
68
|
authStorage,
|
|
73
69
|
modelRegistry,
|
|
74
70
|
model,
|
|
75
71
|
customTools,
|
|
76
|
-
sessionManager: SessionManager.
|
|
72
|
+
sessionManager: SessionManager.inMemory()
|
|
77
73
|
});
|
|
78
74
|
|
|
79
75
|
const instructions = await loadProjectInstructions();
|
|
80
|
-
const runtimeContext = buildAgentRuntimeContext(
|
|
76
|
+
const runtimeContext = buildAgentRuntimeContext();
|
|
81
77
|
this.logger?.log("agent", `injecting project instructions for chat ${chatId}`);
|
|
82
78
|
this.logger?.log("agent", `runtime context for chat ${chatId}:\n${runtimeContext}`);
|
|
83
79
|
await session.prompt(`${instructions}\n\n${runtimeContext}\n\nAcknowledge with exactly: OK`);
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
|
-
import { arisaHomeDir, artifactsDir,
|
|
2
|
+
import { arisaHomeDir, artifactsDir, stateDir, toolsDir } from "../../runtime/paths.js";
|
|
3
3
|
|
|
4
4
|
export const arisaInstallDir = fileURLToPath(new URL("../../..", import.meta.url));
|
|
5
5
|
export const bundledToolsDir = fileURLToPath(new URL("../../../tools", import.meta.url));
|
|
6
6
|
|
|
7
|
-
export function buildAgentRuntimeContext(
|
|
7
|
+
export function buildAgentRuntimeContext() {
|
|
8
8
|
return [
|
|
9
9
|
`arisaHomeDir: ${arisaHomeDir}`,
|
|
10
10
|
`arisaInstallDir: ${arisaInstallDir}`,
|
|
11
11
|
`bundledToolsDir: ${bundledToolsDir}`,
|
|
12
12
|
`userToolsDir: ${toolsDir}`,
|
|
13
13
|
`artifactsDir: ${artifactsDir}`,
|
|
14
|
-
`stateDir: ${stateDir}
|
|
15
|
-
`chatWorkspaceDir: ${getChatDir(chatId)}`
|
|
14
|
+
`stateDir: ${stateDir}`
|
|
16
15
|
].join("\n");
|
|
17
16
|
}
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createApp } from "./runtime/create-app.js";
|
|
|
5
5
|
import { createLogger } from "./runtime/logger.js";
|
|
6
6
|
import { getServiceStatus, registerServiceProcess, startService, stopService } from "./runtime/service-manager.js";
|
|
7
7
|
import { flushArisaHome } from "./runtime/flush.js";
|
|
8
|
+
import { installPiPackage, removePiPackage } from "./runtime/pi-package-manager.js";
|
|
8
9
|
|
|
9
10
|
const args = process.argv.slice(2);
|
|
10
11
|
const command = args.find((arg) => !arg.startsWith("--")) || "run";
|
|
@@ -83,6 +84,28 @@ async function main() {
|
|
|
83
84
|
return;
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
if (command === "install") {
|
|
88
|
+
const source = args.filter((arg) => !arg.startsWith("--")).slice(1)[0];
|
|
89
|
+
if (!source) {
|
|
90
|
+
console.log("Usage: arisa install <pi-package-source>");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const result = await installPiPackage(source);
|
|
94
|
+
process.exitCode = result.ok ? 0 : result.code;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (command === "remove") {
|
|
99
|
+
const source = args.filter((arg) => !arg.startsWith("--")).slice(1)[0];
|
|
100
|
+
if (!source) {
|
|
101
|
+
console.log("Usage: arisa remove <pi-package-source>");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const result = await removePiPackage(source);
|
|
105
|
+
process.exitCode = result.ok ? 0 : result.code;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
86
109
|
await runForeground();
|
|
87
110
|
}
|
|
88
111
|
|
package/src/runtime/paths.js
CHANGED
|
@@ -9,15 +9,9 @@ export const servicePidFile = path.join(stateDir, "arisa.pid");
|
|
|
9
9
|
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
|
-
export const piAgentDir = path.join(stateDir, "pi-agent");
|
|
13
|
-
export const chatsDir = path.join(arisaHomeDir, "chats");
|
|
14
12
|
export const toolsDir = path.join(arisaHomeDir, "tools");
|
|
15
13
|
export const tmpDir = path.join(arisaHomeDir, "tmp");
|
|
16
14
|
|
|
17
|
-
export function getChatDir(chatId) {
|
|
18
|
-
return path.join(chatsDir, String(chatId));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
export function getToolDir(toolName) {
|
|
22
16
|
return path.join(toolsDir, toolName);
|
|
23
17
|
}
|
|
@@ -41,8 +35,6 @@ export function getToolTmpDir(toolName) {
|
|
|
41
35
|
export async function ensureArisaHome() {
|
|
42
36
|
await mkdir(stateDir, { recursive: true });
|
|
43
37
|
await mkdir(artifactsDir, { recursive: true });
|
|
44
|
-
await mkdir(piAgentDir, { recursive: true });
|
|
45
|
-
await mkdir(chatsDir, { recursive: true });
|
|
46
38
|
await mkdir(toolsDir, { recursive: true });
|
|
47
39
|
await mkdir(tmpDir, { recursive: true });
|
|
48
40
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { DefaultPackageManager, SettingsManager } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { arisaHomeDir } from "./paths.js";
|
|
3
|
+
|
|
4
|
+
function createPackageManager() {
|
|
5
|
+
const settingsManager = SettingsManager.create(arisaHomeDir, arisaHomeDir);
|
|
6
|
+
const packageManager = new DefaultPackageManager({
|
|
7
|
+
cwd: arisaHomeDir,
|
|
8
|
+
agentDir: arisaHomeDir,
|
|
9
|
+
settingsManager
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
packageManager.setProgressCallback((event) => {
|
|
13
|
+
if (event.type === "start") {
|
|
14
|
+
process.stdout.write(`${event.message}\n`);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return packageManager;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function installPiPackage(source) {
|
|
22
|
+
const packageManager = createPackageManager();
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
await packageManager.installAndPersist(source, { local: false });
|
|
26
|
+
console.log(`Installed ${source}`);
|
|
27
|
+
return { ok: true, code: 0 };
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
30
|
+
return { ok: false, code: 1 };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function removePiPackage(source) {
|
|
35
|
+
const packageManager = createPackageManager();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const removed = await packageManager.removeAndPersist(source, { local: false });
|
|
39
|
+
if (!removed) {
|
|
40
|
+
console.error(`No matching package found for ${source}`);
|
|
41
|
+
return { ok: false, code: 1 };
|
|
42
|
+
}
|
|
43
|
+
console.log(`Removed ${source}`);
|
|
44
|
+
return { ok: true, code: 0 };
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
47
|
+
return { ok: false, code: 1 };
|
|
48
|
+
}
|
|
49
|
+
}
|