@aiaiaichain/agent 0.1.5 → 0.1.7

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 (121) hide show
  1. package/dist/api/ExtensionAPI.d.ts +0 -1
  2. package/dist/api/ExtensionAPI.js +3 -7
  3. package/dist/api/Registry.d.ts +0 -1
  4. package/dist/api/Registry.js +54 -57
  5. package/dist/cli.d.ts +0 -1
  6. package/dist/cli.js +684 -685
  7. package/dist/core/AgentDir.d.ts +1 -1
  8. package/dist/core/AgentDir.js +45 -39
  9. package/dist/core/ChainConfig.d.ts +0 -1
  10. package/dist/core/ChainConfig.js +51 -55
  11. package/dist/core/EnvLoader.d.ts +4 -1
  12. package/dist/core/EnvLoader.js +97 -84
  13. package/dist/core/SystemMonitor.d.ts +0 -1
  14. package/dist/core/SystemMonitor.js +72 -85
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.js +19 -26
  17. package/dist/loader.d.ts +0 -1
  18. package/dist/loader.js +64 -67
  19. package/dist/mcp/entry.d.ts +0 -1
  20. package/dist/mcp/entry.js +3 -6
  21. package/dist/mcp/server.d.ts +0 -1
  22. package/dist/mcp/server.js +152 -156
  23. package/dist/models/CostTracker.d.ts +0 -1
  24. package/dist/models/CostTracker.js +58 -61
  25. package/dist/models/ModelRegistry.d.ts +0 -1
  26. package/dist/models/ModelRegistry.js +195 -155
  27. package/dist/providers/ProviderRegistry.d.ts +0 -1
  28. package/dist/providers/ProviderRegistry.js +33 -36
  29. package/dist/runner/AgentRunner.d.ts +0 -1
  30. package/dist/runner/AgentRunner.js +180 -184
  31. package/dist/runner/ModelClient.d.ts +0 -1
  32. package/dist/runner/ModelClient.js +133 -134
  33. package/dist/runner/SwarmRouter.d.ts +0 -1
  34. package/dist/runner/SwarmRouter.js +18 -22
  35. package/dist/runner/ToolDispatcher.d.ts +0 -1
  36. package/dist/runner/ToolDispatcher.js +30 -33
  37. package/dist/scheduler/AgentScheduler.d.ts +0 -1
  38. package/dist/scheduler/AgentScheduler.js +99 -103
  39. package/dist/session/ContextStore.d.ts +1 -1
  40. package/dist/session/ContextStore.js +76 -78
  41. package/dist/session/GoalManager.d.ts +0 -1
  42. package/dist/session/GoalManager.js +96 -100
  43. package/dist/session/MemoryStore.d.ts +2 -1
  44. package/dist/session/MemoryStore.js +108 -87
  45. package/dist/session/SessionManager.d.ts +5 -4
  46. package/dist/session/SessionManager.js +83 -62
  47. package/dist/session/SessionStore.d.ts +0 -1
  48. package/dist/session/SessionStore.js +112 -116
  49. package/dist/setup/SetupWizard.d.ts +0 -1
  50. package/dist/setup/SetupWizard.js +61 -64
  51. package/dist/tools/CrossTools.d.ts +0 -1
  52. package/dist/tools/CrossTools.js +140 -144
  53. package/dist/tools/GmgnIntegration.d.ts +0 -1
  54. package/dist/tools/GmgnIntegration.js +220 -230
  55. package/dist/tools/MarketSentiment.d.ts +0 -1
  56. package/dist/tools/MarketSentiment.js +213 -195
  57. package/dist/tools/NewsSentiment.d.ts +0 -1
  58. package/dist/tools/NewsSentiment.js +126 -130
  59. package/dist/tools/PriceFeed.d.ts +6 -1
  60. package/dist/tools/PriceFeed.js +201 -133
  61. package/dist/tools/TechnicalAnalysis.d.ts +1 -2
  62. package/dist/tools/TechnicalAnalysis.js +248 -216
  63. package/dist/tools/TechnicalAnalysis.worker.d.ts +25 -0
  64. package/dist/tools/TechnicalAnalysis.worker.js +92 -0
  65. package/dist/tools/TokenCalendar.d.ts +0 -1
  66. package/dist/tools/TokenCalendar.js +63 -68
  67. package/dist/tools/TokenSecurityScanner.d.ts +0 -1
  68. package/dist/tools/TokenSecurityScanner.js +93 -96
  69. package/dist/tools/TransactionSim.d.ts +0 -1
  70. package/dist/tools/TransactionSim.js +65 -71
  71. package/dist/tui/App.d.ts +0 -1
  72. package/dist/tui/App.js +895 -824
  73. package/dist/tui/ModelSelector.d.ts +0 -1
  74. package/dist/tui/ModelSelector.js +46 -49
  75. package/dist/tui/REPL.d.ts +0 -1
  76. package/dist/tui/REPL.js +222 -210
  77. package/dist/tui/Sparkline.d.ts +0 -1
  78. package/dist/tui/Sparkline.js +36 -37
  79. package/dist/tui/StatusBar.d.ts +0 -1
  80. package/dist/tui/StatusBar.js +9 -10
  81. package/dist/tui/ThemePresets.d.ts +0 -1
  82. package/dist/tui/ThemePresets.js +99 -103
  83. package/dist/tui/theme.d.ts +0 -1
  84. package/dist/tui/theme.js +50 -31
  85. package/dist/util/clipboard.d.ts +0 -1
  86. package/dist/util/clipboard.js +16 -20
  87. package/dist/util/commandSuggest.d.ts +0 -1
  88. package/dist/util/commandSuggest.js +34 -38
  89. package/dist/util/confirmation.d.ts +0 -1
  90. package/dist/util/confirmation.js +8 -11
  91. package/dist/util/errorHandler.d.ts +0 -1
  92. package/dist/util/errorHandler.js +20 -23
  93. package/dist/util/errors.d.ts +59 -0
  94. package/dist/util/errors.js +93 -0
  95. package/dist/util/logger.d.ts +0 -1
  96. package/dist/util/logger.js +30 -33
  97. package/dist/util/processManager.d.ts +0 -1
  98. package/dist/util/processManager.js +33 -36
  99. package/dist/util/resilientFetch.d.ts +6 -1
  100. package/dist/util/resilientFetch.js +134 -80
  101. package/dist/util/responseCache.d.ts +0 -1
  102. package/dist/util/responseCache.js +36 -45
  103. package/dist/util/rpc.d.ts +16 -0
  104. package/dist/util/rpc.js +69 -0
  105. package/dist/util/safeLog.d.ts +0 -1
  106. package/dist/util/safeLog.js +52 -53
  107. package/dist/util/scheduler.d.ts +0 -1
  108. package/dist/util/scheduler.js +53 -58
  109. package/dist/util/webhooks.d.ts +0 -1
  110. package/dist/util/webhooks.js +54 -58
  111. package/dist/wallet/ActionFeed.d.ts +3 -3
  112. package/dist/wallet/ActionFeed.js +189 -187
  113. package/dist/wallet/AgentWallet.d.ts +9 -7
  114. package/dist/wallet/AgentWallet.js +121 -141
  115. package/dist/wallet/ProfitTracker.d.ts +0 -1
  116. package/dist/wallet/ProfitTracker.js +71 -74
  117. package/package.json +12 -7
  118. package/scripts/build-esbuild.mjs +40 -0
  119. package/scripts/bundle-dts.mjs +58 -0
  120. package/scripts/minify.mjs +44 -0
  121. package/scripts/postinstall.js +27 -0
@@ -1,188 +1,184 @@
1
- /**
2
- * AgentRunner — main agent loop. Handles tool calling, streaming, and message processing.
3
- * The AI responds with text or tool calls; we parse, dispatch, and feed results back.
4
- */
1
+
5
2
  import { logger } from "../util/logger.js";
6
3
  import { ToolDispatcher } from "./ToolDispatcher.js";
7
4
  import { ModelClient, resolveModelConfig } from "./ModelClient.js";
8
5
  export class AgentRunner {
9
- registry;
10
- session;
11
- dispatcher;
12
- onEvent;
13
- sessionCtx;
14
- effectLevel;
15
- modelReg;
16
- costTracker;
17
- goalManager;
18
- contextStore;
19
- abortController = null;
20
- _pendingApproval;
21
- _toolRetries = new Map();
22
- _maxToolRetries = 2;
23
- constructor(registry, session, onEvent, sessionCtx, effectLevel, modelReg, costTracker, goalManager, contextStore) {
24
- this.registry = registry;
25
- this.session = session;
26
- this.dispatcher = new ToolDispatcher(registry);
27
- this.onEvent = onEvent;
28
- this.sessionCtx = sessionCtx;
29
- this.effectLevel = effectLevel;
30
- this.modelReg = modelReg;
31
- this.costTracker = costTracker;
32
- this.goalManager = goalManager;
33
- this.contextStore = contextStore;
34
- }
35
- setEffectLevel(level) {
36
- this.effectLevel = level;
37
- }
38
- async run(input) {
39
- this.session.addMessage("user", input);
40
- const config = resolveModelConfig(this.modelReg);
41
- const messages = this.session.getMessages();
42
- const systemPrompt = this.session.getSystemPrompt();
43
- let fullResponse = "";
44
- let toolCallBuffer = "";
45
- this.abortController = new AbortController();
46
- const handleEvent = (event) => {
47
- if (event.type === "text_delta") {
48
- fullResponse += event.text;
49
- // Check for tool call patterns
50
- toolCallBuffer += event.text;
51
- // Simple JSON tool call detection
52
- const toolCallMatch = toolCallBuffer.match(/\{("tool"|"name"):\s*"([^"]+)"\s*,\s*"args"|"params"|"parameters":\s*(\{[^}]+\})\s*\}/);
53
- if (toolCallMatch) {
54
- const toolName = toolCallMatch[2];
55
- let args = {};
56
- try {
57
- args = JSON.parse(toolCallMatch[3]);
58
- }
59
- catch {
60
- logger.warn('AgentRunner', 'Failed to parse tool call args from stream', { toolCall: toolCallMatch[0].slice(0, 100) });
61
- }
62
- fullResponse = fullResponse.replace(toolCallMatch[0], "").trim();
63
- toolCallBuffer = "";
64
- if (toolName !== "noop") {
65
- this.onEvent({ type: "tool_start", name: toolName });
66
- // Check if tool requires approval
67
- const tool = this.dispatcher.getTool(toolName);
68
- if (tool && this.requiresApproval(toolName)) {
69
- this.onEvent({
70
- type: "approval_request",
71
- toolName,
72
- args: JSON.stringify(args),
73
- approve: (ok) => {
74
- if (ok) {
75
- this.executeTool(toolName, args, fullResponse, systemPrompt, messages, config);
76
- }
77
- else {
78
- this.onEvent({
79
- type: "tool_done",
80
- name: toolName,
81
- result: JSON.stringify({ error: "User denied approval" }),
82
- isError: true,
83
- });
84
- this.session.addMessage("assistant", fullResponse || `(tool ${toolName} was denied)`);
85
- this.onEvent({ type: "turn_done" });
86
- }
87
- },
88
- });
89
- return;
90
- }
91
- this.executeTool(toolName, args, fullResponse, systemPrompt, messages, config);
92
- }
93
- }
94
- this.onEvent({ type: "text_delta", text: event.text });
95
- }
96
- else if (event.type === "turn_done") {
97
- if (fullResponse.trim()) {
98
- this.session.addMessage("assistant", fullResponse.trim());
99
- if (this.costTracker) {
100
- this.costTracker.track(config.model, messages.reduce((s, m) => s + m.content.length, 0), fullResponse.length);
101
- }
102
- }
103
- this.onEvent({ type: "turn_done" });
104
- }
105
- else if (event.type === "error") {
106
- // Check if it's a streaming error that might be tool-call related
107
- if (toolCallBuffer.includes("tool") || toolCallBuffer.includes("function")) {
108
- // Try to extract tool name from error
109
- const nameMatch = toolCallBuffer.match(/"name"\s*:\s*"([^"]+)"/);
110
- const name = nameMatch ? nameMatch[1] : "unknown";
111
- this.onEvent({
112
- type: "tool_done",
113
- name,
114
- result: JSON.stringify({ error: event.message }),
115
- isError: true,
116
- });
117
- }
118
- this.onEvent({ type: "error", message: event.message ?? "unknown error" });
119
- }
120
- };
121
- await ModelClient.runStreaming({
122
- config,
123
- systemPrompt,
124
- messages,
125
- onEvent: handleEvent,
126
- signal: this.abortController.signal,
127
- });
128
- }
129
- async executeTool(toolName, args, responseSoFar, systemPrompt, messages, config) {
130
- const result = await this.dispatcher.dispatch(toolName, args);
131
- const resultText = result.content.map(c => c.text).join("\n");
132
- this.onEvent({
133
- type: "tool_done",
134
- name: toolName,
135
- result: resultText,
136
- isError: result.isError ?? false,
137
- });
138
- // Break retry loop: don't feed back if tool has failed too many times
139
- if (result.isError) {
140
- const retries = (this._toolRetries.get(toolName) ?? 0) + 1;
141
- this._toolRetries.set(toolName, retries);
142
- if (retries > this._maxToolRetries) {
143
- this._toolRetries.delete(toolName);
144
- return; // Stop feeding errors — prevent loop
145
- }
146
- }
147
- else {
148
- this._toolRetries.delete(toolName);
149
- }
150
- // Feed the result back to the model for a follow-up
151
- const toolMessages = [
152
- ...messages.slice(-5),
153
- { role: "assistant", content: responseSoFar || `(using ${toolName})` },
154
- { role: "user", content: `Tool "${toolName}" returned:\n${resultText}\n\nSummarize the result or continue.` },
155
- ];
156
- let secondResponse = "";
157
- await ModelClient.runStreaming({
158
- config,
159
- systemPrompt,
160
- messages: toolMessages,
161
- onEvent: (event) => {
162
- if (event.type === "text_delta") {
163
- secondResponse += event.text;
164
- this.onEvent({ type: "text_delta", text: event.text });
165
- }
166
- else if (event.type === "turn_done") {
167
- if (secondResponse.trim()) {
168
- this.session.addMessage("assistant", secondResponse.trim());
169
- }
170
- this.onEvent({ type: "turn_done" });
171
- }
172
- else if (event.type === "error") {
173
- this.onEvent({ type: "error", message: event.message ?? "unknown error" });
174
- }
175
- },
176
- signal: this.abortController?.signal,
177
- });
178
- }
179
- requiresApproval(toolName) {
180
- const approvalTools = ["execute_trade", "send_transaction", "sign_message", "deploy_contract"];
181
- return approvalTools.includes(toolName);
182
- }
183
- abort() {
184
- this.abortController?.abort();
185
- this.abortController = null;
186
- }
187
- }
188
- //# sourceMappingURL=AgentRunner.js.map
6
+ registry;
7
+ session;
8
+ dispatcher;
9
+ onEvent;
10
+ sessionCtx;
11
+ effectLevel;
12
+ modelReg;
13
+ costTracker;
14
+ goalManager;
15
+ contextStore;
16
+ abortController = null;
17
+ _pendingApproval;
18
+ _toolRetries = new Map();
19
+ _maxToolRetries = 2;
20
+ constructor(registry, session, onEvent, sessionCtx, effectLevel, modelReg, costTracker, goalManager, contextStore) {
21
+ this.registry = registry;
22
+ this.session = session;
23
+ this.dispatcher = new ToolDispatcher(registry);
24
+ this.onEvent = onEvent;
25
+ this.sessionCtx = sessionCtx;
26
+ this.effectLevel = effectLevel;
27
+ this.modelReg = modelReg;
28
+ this.costTracker = costTracker;
29
+ this.goalManager = goalManager;
30
+ this.contextStore = contextStore;
31
+ }
32
+ setEffectLevel(level) {
33
+ this.effectLevel = level;
34
+ }
35
+ async run(input) {
36
+ this.session.addMessage("user", input);
37
+ const config = resolveModelConfig(this.modelReg);
38
+ const messages = this.session.getMessages();
39
+ const systemPrompt = this.session.getSystemPrompt();
40
+ let fullResponse = "";
41
+ let toolCallBuffer = "";
42
+ this.abortController = new AbortController();
43
+ const handleEvent = (event) => {
44
+ if (event.type === "text_delta") {
45
+ fullResponse += event.text;
46
+
47
+ toolCallBuffer += event.text;
48
+
49
+ const toolCallMatch = toolCallBuffer.match(/\{("tool"|"name"):\s*"([^"]+)"\s*,\s*"args"|"params"|"parameters":\s*(\{[^}]+\})\s*\}/);
50
+ if (toolCallMatch) {
51
+ const toolName = toolCallMatch[2];
52
+ let args = {};
53
+ try {
54
+ args = JSON.parse(toolCallMatch[3]);
55
+ }
56
+ catch {
57
+ logger.warn('AgentRunner', 'Failed to parse tool call args from stream', { toolCall: toolCallMatch[0].slice(0, 100) });
58
+ }
59
+ fullResponse = fullResponse.replace(toolCallMatch[0], "").trim();
60
+ toolCallBuffer = "";
61
+ if (toolName !== "noop") {
62
+ this.onEvent({ type: "tool_start", name: toolName });
63
+
64
+ const tool = this.dispatcher.getTool(toolName);
65
+ if (tool && this.requiresApproval(toolName)) {
66
+ this.onEvent({
67
+ type: "approval_request",
68
+ toolName,
69
+ args: JSON.stringify(args),
70
+ approve: (ok) => {
71
+ if (ok) {
72
+ this.executeTool(toolName, args, fullResponse, systemPrompt, messages, config);
73
+ }
74
+ else {
75
+ this.onEvent({
76
+ type: "tool_done",
77
+ name: toolName,
78
+ result: JSON.stringify({ error: "User denied approval" }),
79
+ isError: true,
80
+ });
81
+ this.session.addMessage("assistant", fullResponse || `(tool ${toolName} was denied)`);
82
+ this.onEvent({ type: "turn_done" });
83
+ }
84
+ },
85
+ });
86
+ return;
87
+ }
88
+ this.executeTool(toolName, args, fullResponse, systemPrompt, messages, config);
89
+ }
90
+ }
91
+ this.onEvent({ type: "text_delta", text: event.text });
92
+ }
93
+ else if (event.type === "turn_done") {
94
+ if (fullResponse.trim()) {
95
+ this.session.addMessage("assistant", fullResponse.trim());
96
+ if (this.costTracker) {
97
+ this.costTracker.track(config.model, messages.reduce((s, m) => s + m.content.length, 0), fullResponse.length);
98
+ }
99
+ }
100
+ this.onEvent({ type: "turn_done" });
101
+ }
102
+ else if (event.type === "error") {
103
+
104
+ if (toolCallBuffer.includes("tool") || toolCallBuffer.includes("function")) {
105
+
106
+ const nameMatch = toolCallBuffer.match(/"name"\s*:\s*"([^"]+)"/);
107
+ const name = nameMatch ? nameMatch[1] : "unknown";
108
+ this.onEvent({
109
+ type: "tool_done",
110
+ name,
111
+ result: JSON.stringify({ error: event.message }),
112
+ isError: true,
113
+ });
114
+ }
115
+ this.onEvent({ type: "error", message: event.message ?? "unknown error" });
116
+ }
117
+ };
118
+ await ModelClient.runStreaming({
119
+ config,
120
+ systemPrompt,
121
+ messages,
122
+ onEvent: handleEvent,
123
+ signal: this.abortController.signal,
124
+ });
125
+ }
126
+ async executeTool(toolName, args, responseSoFar, systemPrompt, messages, config) {
127
+ const result = await this.dispatcher.dispatch(toolName, args);
128
+ const resultText = result.content.map(c => c.text).join("\n");
129
+ this.onEvent({
130
+ type: "tool_done",
131
+ name: toolName,
132
+ result: resultText,
133
+ isError: result.isError ?? false,
134
+ });
135
+
136
+ if (result.isError) {
137
+ const retries = (this._toolRetries.get(toolName) ?? 0) + 1;
138
+ this._toolRetries.set(toolName, retries);
139
+ if (retries > this._maxToolRetries) {
140
+ this._toolRetries.delete(toolName);
141
+ return;
142
+ }
143
+ }
144
+ else {
145
+ this._toolRetries.delete(toolName);
146
+ }
147
+
148
+ const toolMessages = [
149
+ ...messages.slice(-5),
150
+ { role: "assistant", content: responseSoFar || `(using ${toolName})` },
151
+ { role: "user", content: `Tool "${toolName}" returned:\n${resultText}\n\nSummarize the result or continue.` },
152
+ ];
153
+ let secondResponse = "";
154
+ await ModelClient.runStreaming({
155
+ config,
156
+ systemPrompt,
157
+ messages: toolMessages,
158
+ onEvent: (event) => {
159
+ if (event.type === "text_delta") {
160
+ secondResponse += event.text;
161
+ this.onEvent({ type: "text_delta", text: event.text });
162
+ }
163
+ else if (event.type === "turn_done") {
164
+ if (secondResponse.trim()) {
165
+ this.session.addMessage("assistant", secondResponse.trim());
166
+ }
167
+ this.onEvent({ type: "turn_done" });
168
+ }
169
+ else if (event.type === "error") {
170
+ this.onEvent({ type: "error", message: event.message ?? "unknown error" });
171
+ }
172
+ },
173
+ signal: this.abortController?.signal,
174
+ });
175
+ }
176
+ requiresApproval(toolName) {
177
+ const approvalTools = ["execute_trade", "send_transaction", "sign_message", "deploy_contract"];
178
+ return approvalTools.includes(toolName);
179
+ }
180
+ abort() {
181
+ this.abortController?.abort();
182
+ this.abortController = null;
183
+ }
184
+ }
@@ -27,4 +27,3 @@ export interface ModelClientOptions {
27
27
  export declare class ModelClient {
28
28
  static runStreaming(options: ModelClientOptions): Promise<void>;
29
29
  }
30
- //# sourceMappingURL=ModelClient.d.ts.map
@@ -1,7 +1,4 @@
1
- /**
2
- * ModelClient — LLM API client for OpenRouter.
3
- * Handles streaming and non-streaming calls.
4
- */
1
+
5
2
  import { env } from "../core/EnvLoader.js";
6
3
  import { logger } from "../util/logger.js";
7
4
  import { resilientFetch } from "../util/resilientFetch.js";
@@ -9,137 +6,139 @@ const MAX_RETRIES = 2;
9
6
  const RETRY_BACKOFF_MS = 1000;
10
7
  const REQUEST_TIMEOUT = 60_000;
11
8
  function isRetryable(status) {
12
- return status === 429 || status >= 500;
9
+ return status === 429 || status >= 500;
13
10
  }
14
11
  export function resolveModelConfig(registry) {
15
- const apiKey = env.get("OPENROUTER_API_KEY");
16
- const defaultModel = env.get("DEFAULT_MODEL");
17
- return {
18
- model: defaultModel || registry.resolvePrimary(),
19
- apiKey: apiKey || "",
20
- baseUrl: "https://openrouter.ai/api/v1",
21
- };
12
+ const apiKey = env.get("OPENROUTER_API_KEY");
13
+ const defaultModel = env.get("DEFAULT_MODEL");
14
+ return {
15
+ model: defaultModel || registry.resolvePrimary(),
16
+ apiKey: apiKey || "",
17
+ baseUrl: "https://openrouter.ai/api/v1",
18
+ };
22
19
  }
23
20
  export class ModelClient {
24
- static async runStreaming(options) {
25
- const { config, systemPrompt, messages, onEvent, signal } = options;
26
- const body = {
27
- model: config.model,
28
- messages: [
29
- { role: "system", content: systemPrompt },
30
- ...messages,
31
- ],
32
- stream: true,
33
- max_tokens: 4096,
34
- };
35
- let lastError = "";
36
- let lastStatus = 0;
37
- let timer;
38
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
39
- try {
40
- if (attempt > 0) {
41
- const delay = RETRY_BACKOFF_MS * Math.pow(2, attempt - 1);
42
- logger.info('ModelClient', `Retry attempt ${attempt}/${MAX_RETRIES} after ${delay}ms`);
43
- await new Promise(r => setTimeout(r, delay));
44
- }
45
- const controller = new AbortController();
46
- timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
47
- if (signal?.aborted) {
48
- onEvent({ type: "error", message: "Request aborted" });
49
- return;
50
- }
51
- const combinedSignal = signal ?? controller.signal;
52
- const response = await resilientFetch(`${config.baseUrl}/chat/completions`, {
53
- timeout: REQUEST_TIMEOUT,
54
- retries: 0,
55
- method: "POST",
56
- headers: {
57
- "Content-Type": "application/json",
58
- Authorization: `Bearer ${config.apiKey}`,
59
- "HTTP-Referer": "https://aiaiaichain.dev",
60
- "X-Title": "AIAIAI Chain Agent",
61
- },
62
- body: JSON.stringify(body),
63
- signal: combinedSignal,
64
- });
65
- clearTimeout(timer);
66
- if (!response.ok) {
67
- const err = await response.text().catch(() => "unknown error");
68
- lastError = err;
69
- lastStatus = response.status;
70
- if (isRetryable(response.status) && attempt < MAX_RETRIES) {
71
- logger.warn('ModelClient', `Retryable error ${response.status}`, { attempt });
72
- continue;
73
- }
74
- if (response.status === 401) {
75
- onEvent({ type: "error", message: `API auth failed: OpenRouter key is missing or invalid.\nRun: aiaiai setup\nGet a key: https://openrouter.ai/keys` });
76
- }
77
- else {
78
- onEvent({ type: "error", message: `API error ${response.status}: ${err}` });
79
- }
80
- return;
81
- }
82
- const reader = response.body?.getReader();
83
- if (!reader) {
84
- onEvent({ type: "error", message: "No response body" });
85
- return;
86
- }
87
- const decoder = new TextDecoder();
88
- let buffer = "";
89
- while (true) {
90
- const { done, value } = await reader.read();
91
- if (done)
92
- break;
93
- buffer += decoder.decode(value, { stream: true });
94
- const lines = buffer.split("\n");
95
- buffer = lines.pop() || "";
96
- for (const line of lines) {
97
- const trimmed = line.trim();
98
- if (!trimmed || trimmed === "data: [DONE]")
99
- continue;
100
- if (!trimmed.startsWith("data: "))
101
- continue;
102
- try {
103
- const json = JSON.parse(trimmed.slice(6));
104
- const delta = json.choices?.[0]?.delta?.content;
105
- if (delta)
106
- onEvent({ type: "text_delta", text: delta });
107
- }
108
- catch {
109
- logger.debug('ModelClient', 'Skipped malformed SSE line', { line: trimmed.slice(0, 100) });
110
- }
111
- }
112
- }
113
- onEvent({ type: "turn_done" });
114
- return; // Success — exit retry loop
115
- }
116
- catch (e) {
117
- clearTimeout(timer);
118
- if (e instanceof Error && e.name === "AbortError") {
119
- if (signal?.aborted) {
120
- onEvent({ type: "turn_done" });
121
- return;
122
- }
123
- // Timeout
124
- lastError = "Request timed out";
125
- if (attempt < MAX_RETRIES) {
126
- logger.warn('ModelClient', `Timeout, retrying`, { attempt });
127
- continue;
128
- }
129
- onEvent({ type: "error", message: "Request timed out" });
130
- return;
131
- }
132
- lastError = e instanceof Error ? e.message : String(e);
133
- if (attempt < MAX_RETRIES) {
134
- logger.warn('ModelClient', `Error, retrying`, { attempt, error: lastError });
135
- continue;
136
- }
137
- onEvent({ type: "error", message: `ModelClient error (${lastStatus || 'offline'}): ${lastError}` });
138
- return;
139
- }
140
- }
141
- // Exhausted retries
142
- onEvent({ type: "error", message: `ModelClient failed after ${MAX_RETRIES + 1} attempts: ${lastError}` });
143
- }
144
- }
145
- //# sourceMappingURL=ModelClient.js.map
21
+ static async runStreaming(options) {
22
+ const { config, systemPrompt, messages, onEvent, signal } = options;
23
+ const body = {
24
+ model: config.model,
25
+ messages: [
26
+ { role: "system", content: systemPrompt },
27
+ ...messages,
28
+ ],
29
+ stream: true,
30
+ max_tokens: 4096,
31
+ };
32
+ let lastError = "";
33
+ let lastStatus = 0;
34
+ let timer;
35
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
36
+ try {
37
+ if (attempt > 0) {
38
+ const delay = RETRY_BACKOFF_MS * Math.pow(2, attempt - 1);
39
+ logger.info('ModelClient', `Retry attempt ${attempt}/${MAX_RETRIES} after ${delay}ms`);
40
+ await new Promise(r => setTimeout(r, delay));
41
+ }
42
+ const controller = new AbortController();
43
+ timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
44
+ if (signal?.aborted) {
45
+ onEvent({ type: "error", message: "Request aborted" });
46
+ return;
47
+ }
48
+ const combinedSignal = signal ?? controller.signal;
49
+ const response = await resilientFetch(`${config.baseUrl}/chat/completions`, {
50
+ timeout: REQUEST_TIMEOUT,
51
+ retries: 0,
52
+ method: "POST",
53
+ headers: {
54
+ "Content-Type": "application/json",
55
+ Authorization: `Bearer ${config.apiKey}`,
56
+ "HTTP-Referer": "https://aiaiaichain.dev",
57
+ "X-Title": "AIAIAI Chain Agent",
58
+ },
59
+ body: JSON.stringify(body),
60
+ signal: combinedSignal,
61
+ });
62
+ clearTimeout(timer);
63
+ if (!response.ok) {
64
+ const err = await response.text().catch(() => "unknown error");
65
+ lastError = err;
66
+ lastStatus = response.status;
67
+ if (isRetryable(response.status) && attempt < MAX_RETRIES) {
68
+ logger.warn('ModelClient', `Retryable error ${response.status}`, { attempt });
69
+ continue;
70
+ }
71
+ if (response.status === 401) {
72
+ onEvent({ type: "error", message: `API auth failed: OpenRouter key is missing or invalid.\nRun: aiaiai setup\nGet a key: https://openrouter.ai/keys` });
73
+ }
74
+ else {
75
+ onEvent({ type: "error", message: `API error ${response.status}: ${err}` });
76
+ }
77
+ return;
78
+ }
79
+
80
+ const reader = response.body?.getReader();
81
+ if (!reader) {
82
+ onEvent({ type: "error", message: "No response body" });
83
+ return;
84
+ }
85
+
86
+ const textStream = response.body.pipeThrough(new TextDecoderStream());
87
+ const lineReader = textStream.getReader();
88
+ let buffer = "";
89
+ while (true) {
90
+ const { done, value } = await lineReader.read();
91
+ if (done)
92
+ break;
93
+ buffer += value;
94
+ const lines = buffer.split("\n");
95
+ buffer = lines.pop() || "";
96
+ for (const line of lines) {
97
+ const trimmed = line.trim();
98
+ if (!trimmed || trimmed === "data: [DONE]")
99
+ continue;
100
+ if (!trimmed.startsWith("data: "))
101
+ continue;
102
+ try {
103
+ const json = JSON.parse(trimmed.slice(6));
104
+ const delta = json.choices?.[0]?.delta?.content;
105
+ if (delta)
106
+ onEvent({ type: "text_delta", text: delta });
107
+ }
108
+ catch {
109
+ logger.debug('ModelClient', 'Skipped malformed SSE line', { line: trimmed.slice(0, 100) });
110
+ }
111
+ }
112
+ }
113
+ onEvent({ type: "turn_done" });
114
+ return;
115
+ }
116
+ catch (e) {
117
+ clearTimeout(timer);
118
+ if (e instanceof Error && e.name === "AbortError") {
119
+ if (signal?.aborted) {
120
+ onEvent({ type: "turn_done" });
121
+ return;
122
+ }
123
+
124
+ lastError = "Request timed out";
125
+ if (attempt < MAX_RETRIES) {
126
+ logger.warn('ModelClient', `Timeout, retrying`, { attempt });
127
+ continue;
128
+ }
129
+ onEvent({ type: "error", message: "Request timed out" });
130
+ return;
131
+ }
132
+ lastError = e instanceof Error ? e.message : String(e);
133
+ if (attempt < MAX_RETRIES) {
134
+ logger.warn('ModelClient', `Error, retrying`, { attempt, error: lastError });
135
+ continue;
136
+ }
137
+ onEvent({ type: "error", message: `ModelClient error (${lastStatus || 'offline'}): ${lastError}` });
138
+ return;
139
+ }
140
+ }
141
+
142
+ onEvent({ type: "error", message: `ModelClient failed after ${MAX_RETRIES + 1} attempts: ${lastError}` });
143
+ }
144
+ }