@hemansubedi/aether-ai 1.0.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/.gitattributes +3 -0
- package/.github/workflows/live-stats.yml +42 -0
- package/.github/workflows/publish.yml +34 -0
- package/.github/workflows/update-preview.yml +41 -0
- package/INSTALL.md +59 -0
- package/LICENSE +21 -0
- package/README.md +397 -0
- package/assets/aether-arena.svg +72 -0
- package/assets/aether-banner.svg +62 -0
- package/assets/aether-router.svg +129 -0
- package/dist/agent.js +125 -0
- package/dist/arena.js +486 -0
- package/dist/checkpoint.js +105 -0
- package/dist/client.js +95 -0
- package/dist/combos.js +176 -0
- package/dist/commands.js +483 -0
- package/dist/config.js +104 -0
- package/dist/cost.js +176 -0
- package/dist/git.js +52 -0
- package/dist/health.js +81 -0
- package/dist/index.js +272 -0
- package/dist/keys.js +128 -0
- package/dist/memory.js +98 -0
- package/dist/modes.js +68 -0
- package/dist/providers/index.js +32 -0
- package/dist/providers/ollama.js +206 -0
- package/dist/providers/openai-compat.js +181 -0
- package/dist/providers/openrouter.js +189 -0
- package/dist/providers/registry.js +211 -0
- package/dist/router-engine.js +200 -0
- package/dist/router.js +171 -0
- package/dist/server.js +210 -0
- package/dist/session.js +97 -0
- package/dist/settings.js +97 -0
- package/dist/skills.js +100 -0
- package/dist/tokensaver.js +50 -0
- package/dist/tools/filesystem.js +243 -0
- package/dist/tools/git.js +53 -0
- package/dist/tools/glob.js +175 -0
- package/dist/tools/grep.js +193 -0
- package/dist/tools/registry.js +39 -0
- package/dist/tools/vision.js +140 -0
- package/dist/tools/websearch.js +118 -0
- package/dist/tui.js +562 -0
- package/dist/types.js +8 -0
- package/docs/preview.txt +51 -0
- package/docs/screenshots.md +110 -0
- package/docs/stats.md +5 -0
- package/install.ps1 +170 -0
- package/install.sh +196 -0
- package/package.json +34 -0
- package/scripts/generate-stats-card.ts +62 -0
- package/scripts/patch_index.ps1 +17 -0
- package/scripts/release.sh +7 -0
- package/src/agent.ts +146 -0
- package/src/arena.ts +584 -0
- package/src/checkpoint.ts +111 -0
- package/src/client.ts +172 -0
- package/src/combos.ts +199 -0
- package/src/commands.ts +973 -0
- package/src/config.ts +122 -0
- package/src/cost.ts +206 -0
- package/src/git.ts +68 -0
- package/src/health.ts +90 -0
- package/src/index.ts +281 -0
- package/src/keys.ts +135 -0
- package/src/memory.ts +101 -0
- package/src/modes.ts +84 -0
- package/src/providers/index.ts +59 -0
- package/src/providers/ollama.ts +222 -0
- package/src/providers/openai-compat.ts +188 -0
- package/src/providers/openrouter.ts +198 -0
- package/src/providers/registry.ts +223 -0
- package/src/router-engine.ts +214 -0
- package/src/router.ts +195 -0
- package/src/server.ts +242 -0
- package/src/session.ts +111 -0
- package/src/settings.ts +125 -0
- package/src/skills.ts +106 -0
- package/src/tokensaver.ts +57 -0
- package/src/tools/filesystem.ts +258 -0
- package/src/tools/git.ts +53 -0
- package/src/tools/glob.ts +180 -0
- package/src/tools/grep.ts +192 -0
- package/src/tools/registry.ts +54 -0
- package/src/tools/vision.ts +152 -0
- package/src/tools/websearch.ts +130 -0
- package/src/tui.ts +664 -0
- package/src/types.ts +77 -0
- package/tsconfig.json +16 -0
package/src/agent.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { Router } from "./router.js";
|
|
2
|
+
import { ToolRegistry } from "./tools/registry.js";
|
|
3
|
+
import type { ChatChunk, Message, ToolCall } from "./types.js";
|
|
4
|
+
import type { Memory } from "./memory.js";
|
|
5
|
+
import type { ModeManager } from "./modes.js";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_MAX_STEPS = 15;
|
|
8
|
+
|
|
9
|
+
// Best-effort repair of tool-call arguments. Some models emit non-JSON strings
|
|
10
|
+
// (e.g. `@{path=foo; content=bar}`); others pass an already-parsed object.
|
|
11
|
+
// Handle both so tools always receive a proper args object.
|
|
12
|
+
function parseToolArgs(raw: any): Record<string, any> {
|
|
13
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) return raw;
|
|
14
|
+
if (typeof raw !== "string") return {};
|
|
15
|
+
const str = raw.trim();
|
|
16
|
+
if (!str) return {};
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(str);
|
|
19
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
20
|
+
} catch {
|
|
21
|
+
// fall through to repair
|
|
22
|
+
}
|
|
23
|
+
const args: Record<string, any> = {};
|
|
24
|
+
const re = /([A-Za-z_][\w.-]*)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|([^;}\s,]+))/g;
|
|
25
|
+
let m: RegExpExecArray | null;
|
|
26
|
+
while ((m = re.exec(str)) !== null) {
|
|
27
|
+
const key = m[1];
|
|
28
|
+
const val = m[2] ?? m[3] ?? m[4];
|
|
29
|
+
if (key !== undefined && val !== undefined) args[key] = val;
|
|
30
|
+
}
|
|
31
|
+
return args;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const SYSTEM_PROMPT = `You are an expert AI software engineer with the ability to read, write, edit files, list directories, run shell commands, search the web, and describe images.
|
|
35
|
+
|
|
36
|
+
WORKFLOW:
|
|
37
|
+
1. Explore first: use ListDir to understand the project structure, then ReadFile to inspect relevant files.
|
|
38
|
+
2. Implement: use WriteFile (for new files) or EditFile (for targeted changes). Write clean, production-quality code.
|
|
39
|
+
3. Verify: use Bash to run tests, typecheck, or lint after making changes.
|
|
40
|
+
4. Never guess file contents - always read before editing.
|
|
41
|
+
|
|
42
|
+
STYLE:
|
|
43
|
+
- Be concise and direct. Do not narrate your actions excessively.
|
|
44
|
+
- Make changes in the smallest, most targeted way possible.
|
|
45
|
+
- Only use tools when needed; do not call tools unnecessarily.
|
|
46
|
+
- When a task is complete, summarize what was done in 1-2 sentences.
|
|
47
|
+
- If a tool fails, read the error and retry with a fix.
|
|
48
|
+
|
|
49
|
+
TRUSTWORTHY TOOL RESULTS:
|
|
50
|
+
- When a tool returns output, treat the tool output as the ground truth. Quote or reference the actual tool output verbatim rather than summarizing it from memory.
|
|
51
|
+
- If a tool returned no matches or an empty result, say exactly that ("no matches found") and show the tool output verbatim - never invent files, line numbers, counts, or facts that the tool did not report.
|
|
52
|
+
- Do not assume a tool succeeded if its output contains an ERROR: prefix; report the error to the user.
|
|
53
|
+
- When counting or summarizing tool output (e.g. "how many files"), base your answer strictly on the returned lines and say how you derived it.`;
|
|
54
|
+
|
|
55
|
+
export class Agent {
|
|
56
|
+
readonly router: Router;
|
|
57
|
+
readonly registry: ToolRegistry;
|
|
58
|
+
readonly maxSteps: number;
|
|
59
|
+
readonly systemPrompt: string;
|
|
60
|
+
readonly memory?: Memory;
|
|
61
|
+
readonly modeManager?: ModeManager;
|
|
62
|
+
lastMessages: Message[] = [];
|
|
63
|
+
|
|
64
|
+
constructor(
|
|
65
|
+
router: Router,
|
|
66
|
+
registry: ToolRegistry,
|
|
67
|
+
opts?: {
|
|
68
|
+
maxSteps?: number;
|
|
69
|
+
systemPrompt?: string;
|
|
70
|
+
memory?: Memory;
|
|
71
|
+
modeManager?: ModeManager;
|
|
72
|
+
}
|
|
73
|
+
) {
|
|
74
|
+
this.router = router;
|
|
75
|
+
this.registry = registry;
|
|
76
|
+
this.maxSteps = opts?.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
77
|
+
this.systemPrompt = opts?.systemPrompt ?? SYSTEM_PROMPT;
|
|
78
|
+
this.memory = opts?.memory;
|
|
79
|
+
this.modeManager = opts?.modeManager;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setMode(name: string): void {
|
|
83
|
+
if (!this.modeManager) throw new Error("Agent has no ModeManager");
|
|
84
|
+
this.modeManager.setMode(name);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getMode(): string {
|
|
88
|
+
return this.modeManager?.getMode() ?? "normal";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getActiveTools(): string[] {
|
|
92
|
+
if (!this.modeManager) return this.registry.list().map((d) => d.name);
|
|
93
|
+
return this.modeManager.allowedTools();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async *run(userMessage: string, history: Message[]): AsyncGenerator<ChatChunk, void> {
|
|
97
|
+
const systemMsg: Message = { role: "system", content: this.systemPrompt };
|
|
98
|
+
this.lastMessages = [systemMsg, ...history, { role: "user", content: userMessage }];
|
|
99
|
+
|
|
100
|
+
for (let step = 0; step < this.maxSteps; step++) {
|
|
101
|
+
const tools = this.registry.list();
|
|
102
|
+
let assistantText = "";
|
|
103
|
+
const toolCalls: ToolCall[] = [];
|
|
104
|
+
|
|
105
|
+
for await (const chunk of this.router.chat(this.lastMessages, tools, {
|
|
106
|
+
temperature: 0.7,
|
|
107
|
+
maxTokens: 4096,
|
|
108
|
+
})) {
|
|
109
|
+
if (chunk.type === "text" && chunk.text) {
|
|
110
|
+
assistantText += chunk.text;
|
|
111
|
+
}
|
|
112
|
+
if (chunk.type === "tool_call" && chunk.tool_call) {
|
|
113
|
+
toolCalls.push(chunk.tool_call);
|
|
114
|
+
}
|
|
115
|
+
yield chunk;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const assistantMsg: Message = {
|
|
119
|
+
role: "assistant",
|
|
120
|
+
content: assistantText,
|
|
121
|
+
tool_calls: toolCalls.length ? toolCalls : undefined,
|
|
122
|
+
};
|
|
123
|
+
this.lastMessages.push(assistantMsg);
|
|
124
|
+
|
|
125
|
+
if (toolCalls.length === 0) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const tc of toolCalls) {
|
|
130
|
+
const parsed = parseToolArgs(tc.function.arguments);
|
|
131
|
+
const result = await this.registry.executeTool(tc.function.name, parsed);
|
|
132
|
+
this.lastMessages.push({
|
|
133
|
+
role: "tool",
|
|
134
|
+
tool_call_id: tc.id,
|
|
135
|
+
name: tc.function.name,
|
|
136
|
+
content: result,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.lastMessages.push({
|
|
142
|
+
role: "assistant",
|
|
143
|
+
content: "\n\n[Reached maximum steps. Stopping.]",
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|