@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.
Files changed (90) hide show
  1. package/.gitattributes +3 -0
  2. package/.github/workflows/live-stats.yml +42 -0
  3. package/.github/workflows/publish.yml +34 -0
  4. package/.github/workflows/update-preview.yml +41 -0
  5. package/INSTALL.md +59 -0
  6. package/LICENSE +21 -0
  7. package/README.md +397 -0
  8. package/assets/aether-arena.svg +72 -0
  9. package/assets/aether-banner.svg +62 -0
  10. package/assets/aether-router.svg +129 -0
  11. package/dist/agent.js +125 -0
  12. package/dist/arena.js +486 -0
  13. package/dist/checkpoint.js +105 -0
  14. package/dist/client.js +95 -0
  15. package/dist/combos.js +176 -0
  16. package/dist/commands.js +483 -0
  17. package/dist/config.js +104 -0
  18. package/dist/cost.js +176 -0
  19. package/dist/git.js +52 -0
  20. package/dist/health.js +81 -0
  21. package/dist/index.js +272 -0
  22. package/dist/keys.js +128 -0
  23. package/dist/memory.js +98 -0
  24. package/dist/modes.js +68 -0
  25. package/dist/providers/index.js +32 -0
  26. package/dist/providers/ollama.js +206 -0
  27. package/dist/providers/openai-compat.js +181 -0
  28. package/dist/providers/openrouter.js +189 -0
  29. package/dist/providers/registry.js +211 -0
  30. package/dist/router-engine.js +200 -0
  31. package/dist/router.js +171 -0
  32. package/dist/server.js +210 -0
  33. package/dist/session.js +97 -0
  34. package/dist/settings.js +97 -0
  35. package/dist/skills.js +100 -0
  36. package/dist/tokensaver.js +50 -0
  37. package/dist/tools/filesystem.js +243 -0
  38. package/dist/tools/git.js +53 -0
  39. package/dist/tools/glob.js +175 -0
  40. package/dist/tools/grep.js +193 -0
  41. package/dist/tools/registry.js +39 -0
  42. package/dist/tools/vision.js +140 -0
  43. package/dist/tools/websearch.js +118 -0
  44. package/dist/tui.js +562 -0
  45. package/dist/types.js +8 -0
  46. package/docs/preview.txt +51 -0
  47. package/docs/screenshots.md +110 -0
  48. package/docs/stats.md +5 -0
  49. package/install.ps1 +170 -0
  50. package/install.sh +196 -0
  51. package/package.json +34 -0
  52. package/scripts/generate-stats-card.ts +62 -0
  53. package/scripts/patch_index.ps1 +17 -0
  54. package/scripts/release.sh +7 -0
  55. package/src/agent.ts +146 -0
  56. package/src/arena.ts +584 -0
  57. package/src/checkpoint.ts +111 -0
  58. package/src/client.ts +172 -0
  59. package/src/combos.ts +199 -0
  60. package/src/commands.ts +973 -0
  61. package/src/config.ts +122 -0
  62. package/src/cost.ts +206 -0
  63. package/src/git.ts +68 -0
  64. package/src/health.ts +90 -0
  65. package/src/index.ts +281 -0
  66. package/src/keys.ts +135 -0
  67. package/src/memory.ts +101 -0
  68. package/src/modes.ts +84 -0
  69. package/src/providers/index.ts +59 -0
  70. package/src/providers/ollama.ts +222 -0
  71. package/src/providers/openai-compat.ts +188 -0
  72. package/src/providers/openrouter.ts +198 -0
  73. package/src/providers/registry.ts +223 -0
  74. package/src/router-engine.ts +214 -0
  75. package/src/router.ts +195 -0
  76. package/src/server.ts +242 -0
  77. package/src/session.ts +111 -0
  78. package/src/settings.ts +125 -0
  79. package/src/skills.ts +106 -0
  80. package/src/tokensaver.ts +57 -0
  81. package/src/tools/filesystem.ts +258 -0
  82. package/src/tools/git.ts +53 -0
  83. package/src/tools/glob.ts +180 -0
  84. package/src/tools/grep.ts +192 -0
  85. package/src/tools/registry.ts +54 -0
  86. package/src/tools/vision.ts +152 -0
  87. package/src/tools/websearch.ts +130 -0
  88. package/src/tui.ts +664 -0
  89. package/src/types.ts +77 -0
  90. package/tsconfig.json +16 -0
@@ -0,0 +1,62 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 300" width="1200" height="300">
3
+ <defs>
4
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
5
+ <stop offset="0%" stop-color="#0a0e1a"/>
6
+ <stop offset="50%" stop-color="#0f1525"/>
7
+ <stop offset="100%" stop-color="#0a0e1a"/>
8
+ </linearGradient>
9
+ <linearGradient id="titleGrad" x1="0" y1="0" x2="1" y2="0">
10
+ <stop offset="0%" stop-color="#7dd3fc"/>
11
+ <stop offset="50%" stop-color="#a78bfa"/>
12
+ <stop offset="100%" stop-color="#f0abfc"/>
13
+ </linearGradient>
14
+ <linearGradient id="lineGrad" x1="0" y1="0" x2="1" y2="0">
15
+ <stop offset="0%" stop-color="#22d3ee" stop-opacity="0"/>
16
+ <stop offset="50%" stop-color="#22d3ee" stop-opacity="1"/>
17
+ <stop offset="100%" stop-color="#22d3ee" stop-opacity="0"/>
18
+ </linearGradient>
19
+ <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
20
+ <feGaussianBlur stdDeviation="4" result="b"/>
21
+ <feMerge>
22
+ <feMergeNode in="b"/>
23
+ <feMergeNode in="SourceGraphic"/>
24
+ </feMerge>
25
+ </filter>
26
+ <filter id="bigGlow" x="-100%" y="-100%" width="300%" height="300%">
27
+ <feGaussianBlur stdDeviation="8" result="b"/>
28
+ <feMerge>
29
+ <feMergeNode in="b"/>
30
+ <feMergeNode in="SourceGraphic"/>
31
+ </feMerge>
32
+ </filter>
33
+ </defs>
34
+ <rect width="1200" height="300" fill="url(#bg)"/>
35
+ <g fill="#1e293b">
36
+ <circle cx="100" cy="80" r="1.5"/><circle cx="200" cy="80" r="1.5"/><circle cx="300" cy="80" r="1.5"/>
37
+ <circle cx="100" cy="150" r="1.5"/><circle cx="200" cy="150" r="1.5"/><circle cx="300" cy="150" r="1.5"/>
38
+ <circle cx="100" cy="220" r="1.5"/><circle cx="200" cy="220" r="1.5"/><circle cx="300" cy="220" r="1.5"/>
39
+ <circle cx="900" cy="80" r="1.5"/><circle cx="1000" cy="80" r="1.5"/><circle cx="1100" cy="80" r="1.5"/>
40
+ <circle cx="900" cy="150" r="1.5"/><circle cx="1000" cy="150" r="1.5"/><circle cx="1100" cy="150" r="1.5"/>
41
+ <circle cx="900" cy="220" r="1.5"/><circle cx="1000" cy="220" r="1.5"/><circle cx="1100" cy="220" r="1.5"/>
42
+ </g>
43
+ <g stroke="url(#lineGrad)" stroke-width="2" fill="none">
44
+ <line x1="0" y1="60" x2="420" y2="60"><animate attributeName="stroke-dasharray" from="0 200" to="200 0" dur="2.4s" repeatCount="indefinite"/><animate attributeName="opacity" values="0;1;0" dur="2.4s" repeatCount="indefinite"/></line>
45
+ <line x1="0" y1="150" x2="380" y2="150"><animate attributeName="stroke-dasharray" from="0 200" to="200 0" dur="3s" begin="0.4s" repeatCount="indefinite"/><animate attributeName="opacity" values="0;1;0" dur="3s" begin="0.4s" repeatCount="indefinite"/></line>
46
+ <line x1="0" y1="240" x2="440" y2="240"><animate attributeName="stroke-dasharray" from="0 200" to="200 0" dur="2.6s" begin="0.8s" repeatCount="indefinite"/><animate attributeName="opacity" values="0;1;0" dur="2.6s" begin="0.8s" repeatCount="indefinite"/></line>
47
+ <line x1="780" y1="60" x2="1200" y2="60"><animate attributeName="stroke-dasharray" from="200 0" to="0 200" dur="2.8s" begin="0.2s" repeatCount="indefinite"/><animate attributeName="opacity" values="0;1;0" dur="2.8s" begin="0.2s" repeatCount="indefinite"/></line>
48
+ <line x1="820" y1="150" x2="1200" y2="150"><animate attributeName="stroke-dasharray" from="200 0" to="0 200" dur="3.2s" begin="0.6s" repeatCount="indefinite"/><animate attributeName="opacity" values="0;1;0" dur="3.2s" begin="0.6s" repeatCount="indefinite"/></line>
49
+ <line x1="760" y1="240" x2="1200" y2="240"><animate attributeName="stroke-dasharray" from="200 0" to="0 200" dur="2.5s" begin="1s" repeatCount="indefinite"/><animate attributeName="opacity" values="0;1;0" dur="2.5s" begin="1s" repeatCount="indefinite"/></line>
50
+ </g>
51
+ <text x="600" y="135" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="128" font-weight="800" letter-spacing="6" fill="url(#titleGrad)" filter="url(#bigGlow)">AETHER</text>
52
+ <g filter="url(#glow)">
53
+ <circle cx="580" cy="205" r="6" fill="#22d3ee"><animate attributeName="r" values="4;10;4" dur="2s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;0.3;1" dur="2s" repeatCount="indefinite"/></circle>
54
+ </g>
55
+ <g filter="url(#glow)">
56
+ <circle cx="620" cy="205" r="6" fill="#a78bfa"><animate attributeName="r" values="4;10;4" dur="2s" begin="0.6s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;0.3;1" dur="2s" begin="0.6s" repeatCount="indefinite"/></circle>
57
+ </g>
58
+ <g filter="url(#glow)">
59
+ <circle cx="660" cy="205" r="6" fill="#f0abfc"><animate attributeName="r" values="4;10;4" dur="2s" begin="1.2s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;0.3;1" dur="2s" begin="1.2s" repeatCount="indefinite"/></circle>
60
+ </g>
61
+ <text x="600" y="270" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="18" font-weight="400" letter-spacing="3" fill="#94a3b8">free · unlimited · multi-provider LLM CLI</text>
62
+ </svg>
@@ -0,0 +1,129 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 280" width="1200" height="280">
3
+ <defs>
4
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
5
+ <stop offset="0%" stop-color="#0a0e1a"/>
6
+ <stop offset="50%" stop-color="#0f1525"/>
7
+ <stop offset="100%" stop-color="#0a0e1a"/>
8
+ </linearGradient>
9
+ <linearGradient id="titleGrad" x1="0" y1="0" x2="1" y2="0">
10
+ <stop offset="0%" stop-color="#7dd3fc"/>
11
+ <stop offset="50%" stop-color="#a78bfa"/>
12
+ <stop offset="100%" stop-color="#f0abfc"/>
13
+ </linearGradient>
14
+ <linearGradient id="greenArrow" x1="0" y1="0" x2="1" y2="0">
15
+ <stop offset="0%" stop-color="#22d3ee" stop-opacity="0"/>
16
+ <stop offset="50%" stop-color="#4ade80" stop-opacity="1"/>
17
+ <stop offset="100%" stop-color="#22d3ee" stop-opacity="0"/>
18
+ </linearGradient>
19
+ <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
20
+ <feGaussianBlur stdDeviation="3" result="b"/>
21
+ <feMerge>
22
+ <feMergeNode in="b"/>
23
+ <feMergeNode in="SourceGraphic"/>
24
+ </feMerge>
25
+ </filter>
26
+ <filter id="routerGlow" x="-100%" y="-100%" width="300%" height="300%">
27
+ <feGaussianBlur stdDeviation="6" result="b"/>
28
+ <feMerge>
29
+ <feMergeNode in="b"/>
30
+ <feMergeNode in="SourceGraphic"/>
31
+ </feMerge>
32
+ </filter>
33
+ </defs>
34
+ <rect width="1200" height="280" fill="url(#bg)"/>
35
+ <g fill="#1e293b">
36
+ <circle cx="60" cy="60" r="1.5"/><circle cx="140" cy="60" r="1.5"/><circle cx="220" cy="60" r="1.5"/>
37
+ <circle cx="60" cy="140" r="1.5"/><circle cx="140" cy="140" r="1.5"/><circle cx="220" cy="140" r="1.5"/>
38
+ <circle cx="60" cy="220" r="1.5"/><circle cx="140" cy="220" r="1.5"/><circle cx="220" cy="220" r="1.5"/>
39
+ <circle cx="980" cy="60" r="1.5"/><circle cx="1060" cy="60" r="1.5"/><circle cx="1140" cy="60" r="1.5"/>
40
+ <circle cx="980" cy="140" r="1.5"/><circle cx="1060" cy="140" r="1.5"/><circle cx="1140" cy="140" r="1.5"/>
41
+ <circle cx="980" cy="220" r="1.5"/><circle cx="1060" cy="220" r="1.5"/><circle cx="1140" cy="220" r="1.5"/>
42
+ </g>
43
+
44
+ <!-- title -->
45
+ <text x="600" y="32" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="17" font-weight="700" letter-spacing="2" fill="url(#titleGrad)">Multi-Provider Routing with Automatic Failover</text>
46
+
47
+ <!-- provider nodes -->
48
+ <g>
49
+ <rect x="40" y="60" width="90" height="34" rx="8" fill="#0f172a" stroke="#334155" stroke-width="1.5"/>
50
+ <text x="85" y="81" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="11" font-weight="600" fill="#94a3b8">Ollama</text>
51
+ </g>
52
+ <g>
53
+ <rect x="40" y="123" width="90" height="34" rx="8" fill="#0f172a" stroke="#334155" stroke-width="1.5"/>
54
+ <text x="85" y="144" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="11" font-weight="600" fill="#94a3b8">OpenRouter</text>
55
+ </g>
56
+ <g>
57
+ <rect x="40" y="186" width="90" height="34" rx="8" fill="#0f172a" stroke="#334155" stroke-width="1.5"/>
58
+ <text x="85" y="207" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="11" font-weight="600" fill="#94a3b8">Groq</text>
59
+ </g>
60
+ <g>
61
+ <rect x="1070" y="60" width="90" height="34" rx="8" fill="#0f172a" stroke="#334155" stroke-width="1.5"/>
62
+ <text x="1115" y="81" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="11" font-weight="600" fill="#94a3b8">Mistral</text>
63
+ </g>
64
+ <g>
65
+ <rect x="1070" y="123" width="90" height="34" rx="8" fill="#0f172a" stroke="#334155" stroke-width="1.5"/>
66
+ <text x="1115" y="144" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="11" font-weight="600" fill="#94a3b8">Together</text>
67
+ </g>
68
+
69
+ <!-- arrows from providers INTO router -->
70
+ <g stroke="url(#greenArrow)" stroke-width="2.5" fill="none">
71
+ <line x1="130" y1="77" x2="530" y2="140">
72
+ <animate attributeName="stroke-dasharray" from="0 500" to="500 0" dur="2.2s" repeatCount="indefinite"/>
73
+ <animate attributeName="opacity" values="0;1;0" dur="2.2s" repeatCount="indefinite"/>
74
+ </line>
75
+ <line x1="130" y1="140" x2="530" y2="140">
76
+ <animate attributeName="stroke-dasharray" from="0 500" to="500 0" dur="2.6s" begin="0.3s" repeatCount="indefinite"/>
77
+ <animate attributeName="opacity" values="0;1;0" dur="2.6s" begin="0.3s" repeatCount="indefinite"/>
78
+ </line>
79
+ <line x1="130" y1="203" x2="530" y2="140">
80
+ <animate attributeName="stroke-dasharray" from="0 500" to="500 0" dur="2.4s" begin="0.6s" repeatCount="indefinite"/>
81
+ <animate attributeName="opacity" values="0;1;0" dur="2.4s" begin="0.6s" repeatCount="indefinite"/>
82
+ </line>
83
+ <line x1="1070" y1="77" x2="670" y2="140">
84
+ <animate attributeName="stroke-dasharray" from="500 0" to="0 500" dur="2.3s" begin="0.2s" repeatCount="indefinite"/>
85
+ <animate attributeName="opacity" values="0;1;0" dur="2.3s" begin="0.2s" repeatCount="indefinite"/>
86
+ </line>
87
+ <line x1="1070" y1="140" x2="670" y2="140">
88
+ <animate attributeName="stroke-dasharray" from="500 0" to="0 500" dur="2.7s" begin="0.5s" repeatCount="indefinite"/>
89
+ <animate attributeName="opacity" values="0;1;0" dur="2.7s" begin="0.5s" repeatCount="indefinite"/>
90
+ </line>
91
+ </g>
92
+
93
+ <!-- arrowhead markers -->
94
+ <defs>
95
+ <marker id="arrowIn" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
96
+ <path d="M0,0 L7,4 L0,8 Z" fill="#4ade80"/>
97
+ </marker>
98
+ <marker id="arrowOut" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
99
+ <path d="M0,0 L7,4 L0,8 Z" fill="#fbbf24"/>
100
+ </marker>
101
+ </defs>
102
+
103
+ <!-- router -->
104
+ <g filter="url(#routerGlow)">
105
+ <rect x="530" y="108" width="140" height="64" rx="14" fill="#0f172a" stroke="#4ade80" stroke-width="2"/>
106
+ <text x="600" y="134" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="15" font-weight="700" fill="#4ade80">ROUTER</text>
107
+ <text x="600" y="152" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="9" font-weight="500" fill="#64748b">auto-failover</text>
108
+ </g>
109
+
110
+ <!-- circuit breaker pulse -->
111
+ <g filter="url(#glow)">
112
+ <circle cx="600" cy="175" r="4" fill="#fbbf24">
113
+ <animate attributeName="r" values="3;8;3" dur="1.6s" repeatCount="indefinite"/>
114
+ <animate attributeName="opacity" values="1;0.2;1" dur="1.6s" repeatCount="indefinite"/>
115
+ </circle>
116
+ </g>
117
+
118
+ <!-- router OUT to CLI -->
119
+ <g>
120
+ <line x1="670" y1="140" x2="1050" y2="203" stroke="#fbbf24" stroke-width="2.5" fill="none" marker-end="url(#arrowOut)">
121
+ <animate attributeName="stroke-dasharray" from="0 500" to="500 0" dur="2s" begin="0.8s" repeatCount="indefinite"/>
122
+ <animate attributeName="opacity" values="0;1;0" dur="2s" begin="0.8s" repeatCount="indefinite"/>
123
+ </line>
124
+ </g>
125
+ <g>
126
+ <rect x="1050" y="186" width="110" height="34" rx="8" fill="#0f172a" stroke="#fbbf24" stroke-width="1.5"/>
127
+ <text x="1105" y="207" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="11" font-weight="700" fill="#fbbf24">CLI</text>
128
+ </g>
129
+ </svg>
package/dist/agent.js ADDED
@@ -0,0 +1,125 @@
1
+ const DEFAULT_MAX_STEPS = 15;
2
+ // Best-effort repair of tool-call arguments. Some models emit non-JSON strings
3
+ // (e.g. `@{path=foo; content=bar}`); others pass an already-parsed object.
4
+ // Handle both so tools always receive a proper args object.
5
+ function parseToolArgs(raw) {
6
+ if (raw && typeof raw === "object" && !Array.isArray(raw))
7
+ return raw;
8
+ if (typeof raw !== "string")
9
+ return {};
10
+ const str = raw.trim();
11
+ if (!str)
12
+ return {};
13
+ try {
14
+ const parsed = JSON.parse(str);
15
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
16
+ return parsed;
17
+ }
18
+ catch {
19
+ // fall through to repair
20
+ }
21
+ const args = {};
22
+ const re = /([A-Za-z_][\w.-]*)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|([^;}\s,]+))/g;
23
+ let m;
24
+ while ((m = re.exec(str)) !== null) {
25
+ const key = m[1];
26
+ const val = m[2] ?? m[3] ?? m[4];
27
+ if (key !== undefined && val !== undefined)
28
+ args[key] = val;
29
+ }
30
+ return args;
31
+ }
32
+ 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.
33
+
34
+ WORKFLOW:
35
+ 1. Explore first: use ListDir to understand the project structure, then ReadFile to inspect relevant files.
36
+ 2. Implement: use WriteFile (for new files) or EditFile (for targeted changes). Write clean, production-quality code.
37
+ 3. Verify: use Bash to run tests, typecheck, or lint after making changes.
38
+ 4. Never guess file contents - always read before editing.
39
+
40
+ STYLE:
41
+ - Be concise and direct. Do not narrate your actions excessively.
42
+ - Make changes in the smallest, most targeted way possible.
43
+ - Only use tools when needed; do not call tools unnecessarily.
44
+ - When a task is complete, summarize what was done in 1-2 sentences.
45
+ - If a tool fails, read the error and retry with a fix.
46
+
47
+ TRUSTWORTHY TOOL RESULTS:
48
+ - 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.
49
+ - 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.
50
+ - Do not assume a tool succeeded if its output contains an ERROR: prefix; report the error to the user.
51
+ - 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.`;
52
+ export class Agent {
53
+ router;
54
+ registry;
55
+ maxSteps;
56
+ systemPrompt;
57
+ memory;
58
+ modeManager;
59
+ lastMessages = [];
60
+ constructor(router, registry, opts) {
61
+ this.router = router;
62
+ this.registry = registry;
63
+ this.maxSteps = opts?.maxSteps ?? DEFAULT_MAX_STEPS;
64
+ this.systemPrompt = opts?.systemPrompt ?? SYSTEM_PROMPT;
65
+ this.memory = opts?.memory;
66
+ this.modeManager = opts?.modeManager;
67
+ }
68
+ setMode(name) {
69
+ if (!this.modeManager)
70
+ throw new Error("Agent has no ModeManager");
71
+ this.modeManager.setMode(name);
72
+ }
73
+ getMode() {
74
+ return this.modeManager?.getMode() ?? "normal";
75
+ }
76
+ getActiveTools() {
77
+ if (!this.modeManager)
78
+ return this.registry.list().map((d) => d.name);
79
+ return this.modeManager.allowedTools();
80
+ }
81
+ async *run(userMessage, history) {
82
+ const systemMsg = { role: "system", content: this.systemPrompt };
83
+ this.lastMessages = [systemMsg, ...history, { role: "user", content: userMessage }];
84
+ for (let step = 0; step < this.maxSteps; step++) {
85
+ const tools = this.registry.list();
86
+ let assistantText = "";
87
+ const toolCalls = [];
88
+ for await (const chunk of this.router.chat(this.lastMessages, tools, {
89
+ temperature: 0.7,
90
+ maxTokens: 4096,
91
+ })) {
92
+ if (chunk.type === "text" && chunk.text) {
93
+ assistantText += chunk.text;
94
+ }
95
+ if (chunk.type === "tool_call" && chunk.tool_call) {
96
+ toolCalls.push(chunk.tool_call);
97
+ }
98
+ yield chunk;
99
+ }
100
+ const assistantMsg = {
101
+ role: "assistant",
102
+ content: assistantText,
103
+ tool_calls: toolCalls.length ? toolCalls : undefined,
104
+ };
105
+ this.lastMessages.push(assistantMsg);
106
+ if (toolCalls.length === 0) {
107
+ return;
108
+ }
109
+ for (const tc of toolCalls) {
110
+ const parsed = parseToolArgs(tc.function.arguments);
111
+ const result = await this.registry.executeTool(tc.function.name, parsed);
112
+ this.lastMessages.push({
113
+ role: "tool",
114
+ tool_call_id: tc.id,
115
+ name: tc.function.name,
116
+ content: result,
117
+ });
118
+ }
119
+ }
120
+ this.lastMessages.push({
121
+ role: "assistant",
122
+ content: "\n\n[Reached maximum steps. Stopping.]",
123
+ });
124
+ }
125
+ }