neoagent 3.2.1-beta.1 → 3.2.1-beta.10

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 (172) hide show
  1. package/extensions/chrome-browser/background.mjs +318 -88
  2. package/extensions/chrome-browser/http.mjs +136 -0
  3. package/extensions/chrome-browser/protocol.mjs +654 -90
  4. package/flutter_app/lib/main_chat.dart +118 -739
  5. package/flutter_app/lib/main_controller.dart +111 -20
  6. package/flutter_app/lib/main_integrations.dart +607 -8
  7. package/flutter_app/lib/main_models.dart +3 -0
  8. package/flutter_app/lib/main_operations.dart +334 -321
  9. package/flutter_app/lib/main_security.dart +266 -112
  10. package/flutter_app/lib/main_settings.dart +4 -3
  11. package/flutter_app/lib/main_shared.dart +14 -11
  12. package/flutter_app/lib/src/backend_client.dart +78 -0
  13. package/flutter_app/lib/src/desktop_companion_actions.dart +185 -31
  14. package/flutter_app/lib/src/desktop_companion_io.dart +319 -86
  15. package/flutter_app/windows/runner/flutter_window.cpp +143 -32
  16. package/landing/index.html +3 -1
  17. package/lib/manager.js +106 -89
  18. package/lib/schema_migrations.js +115 -13
  19. package/package.json +30 -15
  20. package/runtime/paths.js +49 -5
  21. package/server/db/database.js +2 -2
  22. package/server/guest-agent.cli.package.json +13 -0
  23. package/server/guest_agent.js +85 -40
  24. package/server/http/middleware.js +24 -0
  25. package/server/http/routes.js +11 -6
  26. package/server/public/.last_build_id +1 -1
  27. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  28. package/server/public/flutter_bootstrap.js +2 -2
  29. package/server/public/main.dart.js +73083 -72209
  30. package/server/routes/admin.js +1 -1
  31. package/server/routes/android.js +30 -34
  32. package/server/routes/browser.js +23 -15
  33. package/server/routes/desktop.js +18 -1
  34. package/server/routes/integrations.js +107 -1
  35. package/server/routes/memory.js +1 -0
  36. package/server/routes/settings.js +16 -5
  37. package/server/routes/social_reach.js +12 -3
  38. package/server/routes/social_video.js +4 -0
  39. package/server/services/agents/manager.js +1 -1
  40. package/server/services/ai/capabilityHealth.js +62 -96
  41. package/server/services/ai/compaction.js +7 -2
  42. package/server/services/ai/history.js +45 -6
  43. package/server/services/ai/integrated_tools/http_request.js +8 -0
  44. package/server/services/ai/loop/agent_engine_core.js +390 -162
  45. package/server/services/ai/loop/blank_recovery.js +5 -4
  46. package/server/services/ai/loop/callbacks.js +1 -0
  47. package/server/services/ai/loop/completion_judge.js +121 -5
  48. package/server/services/ai/loop/conversation_loop.js +510 -332
  49. package/server/services/ai/loop/messaging_delivery.js +129 -57
  50. package/server/services/ai/loop/model_call_guard.js +91 -0
  51. package/server/services/ai/loop/model_io.js +20 -45
  52. package/server/services/ai/loop/progress_classification.js +2 -0
  53. package/server/services/ai/loop/tool_dispatch.js +19 -8
  54. package/server/services/ai/loopPolicy.js +48 -21
  55. package/server/services/ai/messagingFallback.js +17 -17
  56. package/server/services/ai/model_discovery.js +227 -0
  57. package/server/services/ai/model_failure_cache.js +108 -0
  58. package/server/services/ai/model_identity.js +71 -0
  59. package/server/services/ai/models.js +68 -163
  60. package/server/services/ai/providerRetry.js +17 -59
  61. package/server/services/ai/provider_selector.js +166 -0
  62. package/server/services/ai/providers/anthropic.js +2 -2
  63. package/server/services/ai/providers/claudeCode.js +21 -33
  64. package/server/services/ai/providers/githubCopilot.js +41 -20
  65. package/server/services/ai/providers/google.js +135 -97
  66. package/server/services/ai/providers/grok.js +4 -3
  67. package/server/services/ai/providers/grokOauth.js +19 -27
  68. package/server/services/ai/providers/nvidia.js +10 -5
  69. package/server/services/ai/providers/ollama.js +111 -84
  70. package/server/services/ai/providers/ollama_stream.js +142 -0
  71. package/server/services/ai/providers/openai.js +39 -5
  72. package/server/services/ai/providers/openaiCodex.js +11 -4
  73. package/server/services/ai/providers/openrouter.js +29 -7
  74. package/server/services/ai/providers/provider_error.js +36 -0
  75. package/server/services/ai/settings.js +26 -2
  76. package/server/services/ai/systemPrompt.js +19 -12
  77. package/server/services/ai/taskAnalysis.js +58 -10
  78. package/server/services/ai/terminal_reply.js +18 -0
  79. package/server/services/ai/toolEvidence.js +350 -29
  80. package/server/services/ai/tools.js +190 -111
  81. package/server/services/android/controller.js +770 -237
  82. package/server/services/android/process.js +140 -0
  83. package/server/services/android/sdk_download.js +143 -0
  84. package/server/services/android/uia.js +6 -5
  85. package/server/services/artifacts/store.js +24 -0
  86. package/server/services/browser/controller.js +843 -385
  87. package/server/services/browser/extension/gateway.js +40 -16
  88. package/server/services/browser/extension/protocol.js +15 -1
  89. package/server/services/browser/extension/provider.js +71 -47
  90. package/server/services/browser/extension/registry.js +155 -34
  91. package/server/services/cli/executor.js +62 -9
  92. package/server/services/credentials/bitwarden_cli.js +322 -0
  93. package/server/services/credentials/broker.js +594 -0
  94. package/server/services/desktop/gateway.js +41 -4
  95. package/server/services/desktop/protocol.js +3 -0
  96. package/server/services/desktop/provider.js +39 -42
  97. package/server/services/desktop/registry.js +137 -52
  98. package/server/services/integrations/bitwarden/constants.js +14 -0
  99. package/server/services/integrations/bitwarden/provider.js +197 -0
  100. package/server/services/integrations/bitwarden/snapshot.js +65 -0
  101. package/server/services/integrations/figma/provider.js +78 -12
  102. package/server/services/integrations/github/common.js +11 -6
  103. package/server/services/integrations/github/provider.js +52 -53
  104. package/server/services/integrations/google/provider.js +55 -19
  105. package/server/services/integrations/home_assistant/network.js +17 -20
  106. package/server/services/integrations/home_assistant/provider.js +7 -5
  107. package/server/services/integrations/home_assistant/tools.js +17 -5
  108. package/server/services/integrations/http.js +51 -0
  109. package/server/services/integrations/manager.js +159 -53
  110. package/server/services/integrations/microsoft/provider.js +80 -13
  111. package/server/services/integrations/neoarchive/provider.js +55 -29
  112. package/server/services/integrations/neorecall/client.js +17 -10
  113. package/server/services/integrations/neorecall/provider.js +20 -11
  114. package/server/services/integrations/notion/provider.js +16 -13
  115. package/server/services/integrations/oauth_provider.js +115 -51
  116. package/server/services/integrations/registry.js +2 -0
  117. package/server/services/integrations/slack/provider.js +98 -9
  118. package/server/services/integrations/spotify/provider.js +67 -71
  119. package/server/services/integrations/trello/provider.js +21 -7
  120. package/server/services/integrations/weather/provider.js +18 -12
  121. package/server/services/integrations/whatsapp/provider.js +76 -16
  122. package/server/services/manager.js +110 -1
  123. package/server/services/memory/embedding_index.js +20 -8
  124. package/server/services/memory/embeddings.js +151 -90
  125. package/server/services/memory/ingestion.js +50 -9
  126. package/server/services/memory/ingestion_documents.js +13 -3
  127. package/server/services/memory/manager.js +52 -19
  128. package/server/services/messaging/automation.js +85 -10
  129. package/server/services/messaging/formatting_guides.js +7 -4
  130. package/server/services/messaging/http_platforms.js +33 -13
  131. package/server/services/messaging/inbound_queue.js +78 -24
  132. package/server/services/messaging/inbound_store.js +224 -0
  133. package/server/services/messaging/manager.js +326 -51
  134. package/server/services/messaging/typing_keepalive.js +5 -2
  135. package/server/services/messaging/whatsapp.js +22 -14
  136. package/server/services/network/http.js +210 -0
  137. package/server/services/network/safe_request.js +307 -0
  138. package/server/services/runtime/backends/local-vm.js +227 -67
  139. package/server/services/runtime/docker-vm-manager.js +9 -0
  140. package/server/services/runtime/guest_bootstrap.js +30 -4
  141. package/server/services/runtime/guest_image.js +43 -12
  142. package/server/services/runtime/manager.js +77 -23
  143. package/server/services/runtime/validation.js +7 -6
  144. package/server/services/security/tool_categories.js +6 -0
  145. package/server/services/social_reach/channels/github.js +10 -4
  146. package/server/services/social_reach/channels/reddit.js +4 -4
  147. package/server/services/social_reach/channels/rss.js +2 -2
  148. package/server/services/social_reach/channels/social_video.js +12 -7
  149. package/server/services/social_reach/channels/v2ex.js +21 -8
  150. package/server/services/social_reach/channels/x.js +2 -2
  151. package/server/services/social_reach/channels/xueqiu.js +5 -5
  152. package/server/services/social_reach/service.js +9 -6
  153. package/server/services/social_reach/utils.js +65 -14
  154. package/server/services/social_video/service.js +160 -50
  155. package/server/services/tasks/integration_runtime.js +18 -8
  156. package/server/services/tasks/runtime.js +39 -4
  157. package/server/services/voice/agentBridge.js +17 -4
  158. package/server/services/voice/bufferedLiveRelayAdapter.js +5 -0
  159. package/server/services/voice/liveSession.js +31 -0
  160. package/server/services/voice/message.js +1 -1
  161. package/server/services/voice/openaiSpeech.js +33 -8
  162. package/server/services/voice/providers.js +233 -151
  163. package/server/services/voice/runtime.js +2 -2
  164. package/server/services/voice/runtimeManager.js +118 -20
  165. package/server/services/voice/turnRunner.js +6 -0
  166. package/server/services/wearable/firmware_manifest.js +51 -13
  167. package/server/services/wearable/service.js +1 -0
  168. package/server/utils/abort.js +96 -0
  169. package/server/utils/cloud-security.js +110 -3
  170. package/server/utils/files.js +31 -0
  171. package/server/utils/image_payload.js +95 -0
  172. package/server/utils/retry.js +107 -0
@@ -10,6 +10,7 @@ const { calendarToolDefinitions, executeCalendarTool } = require('./calendar');
10
10
  const { driveToolDefinitions, executeDriveTool } = require('./drive');
11
11
  const { docsToolDefinitions, executeDocsTool } = require('./docs');
12
12
  const { sheetsToolDefinitions, executeSheetsTool } = require('./sheets');
13
+ const { waitForAbortableResult } = require('../http');
13
14
 
14
15
  const GOOGLE_ACCOUNT_IDENTITY_SCOPES = [
15
16
  'openid',
@@ -118,7 +119,17 @@ function createOAuthClient() {
118
119
  };
119
120
  }
120
121
 
121
- async function buildAuthorizedClient(connection) {
122
+ function attachAbortSignal(client, signal) {
123
+ if (!signal) return client;
124
+ const request = client.transporter.request.bind(client.transporter);
125
+ client.transporter.request = (requestOptions = {}) => request({
126
+ ...requestOptions,
127
+ signal: requestOptions.signal || signal,
128
+ });
129
+ return client;
130
+ }
131
+
132
+ async function buildAuthorizedClient(connection, options = {}) {
122
133
  const { client } = createOAuthClient();
123
134
  let credentials = {};
124
135
  try {
@@ -142,7 +153,7 @@ async function buildAuthorizedClient(connection) {
142
153
  });
143
154
  }
144
155
  });
145
- return client;
156
+ return attachAbortSignal(client, options.signal);
146
157
  }
147
158
 
148
159
  function getApp(appId) {
@@ -247,21 +258,29 @@ function summarizeAppConnection(app, connectionRows, envStatus) {
247
258
  };
248
259
  }
249
260
 
250
- async function executeGoogleWorkspaceTool(toolName, args, connection) {
251
- const auth = await buildAuthorizedClient(connection);
261
+ async function executeGoogleWorkspaceTool(
262
+ toolName,
263
+ args,
264
+ connection,
265
+ executionOptions = {},
266
+ ) {
267
+ const auth = await buildAuthorizedClient(connection, executionOptions);
252
268
  const appId = toolAppMap.get(toolName);
253
269
  const app = getApp(appId);
254
270
  if (!app) {
255
271
  throw new Error(`Unknown tool: ${toolName}`);
256
272
  }
257
- const result = await app.executor(toolName, args, auth);
273
+ const result = await waitForAbortableResult(
274
+ app.executor(toolName, args, auth),
275
+ executionOptions.signal,
276
+ );
258
277
  if (result === null) {
259
278
  throw new Error(`Unknown tool: ${toolName}`);
260
279
  }
261
280
  return { result, credentials: auth.credentials };
262
281
  }
263
282
 
264
- async function collectGoogleMemoryDocuments({ connection, sourceTypes = [] }) {
283
+ async function collectGoogleMemoryDocuments({ connection, sourceTypes = [], signal = null }) {
265
284
  const appKey = String(connection?.app_key || '').trim();
266
285
  const documents = [];
267
286
  const collectedAt = new Date().toISOString();
@@ -271,6 +290,7 @@ async function collectGoogleMemoryDocuments({ connection, sourceTypes = [] }) {
271
290
  'google_workspace_gmail_search_threads',
272
291
  { query: 'newer_than:7d', max_results: 8 },
273
292
  connection,
293
+ { signal },
274
294
  );
275
295
  for (const thread of Array.isArray(result?.threads) ? result.threads : []) {
276
296
  const subject = String(thread.subject || 'Gmail thread').trim();
@@ -300,6 +320,7 @@ async function collectGoogleMemoryDocuments({ connection, sourceTypes = [] }) {
300
320
  'google_workspace_calendar_list_events',
301
321
  { time_min: timeMin, time_max: timeMax, max_results: 12 },
302
322
  connection,
323
+ { signal },
303
324
  );
304
325
  for (const event of Array.isArray(result?.events) ? result.events : []) {
305
326
  const title = String(event.summary || 'Calendar event').trim();
@@ -489,19 +510,23 @@ function createGoogleWorkspaceProvider() {
489
510
  });
490
511
  return { url, appId: app.id };
491
512
  },
492
- async finishOAuth({ code, codeVerifier, appKey }) {
513
+ async finishOAuth({ code, codeVerifier, appKey, signal }) {
493
514
  const app = getApp(appKey);
494
515
  if (!app) {
495
516
  throw new Error(`Unknown Google Workspace app: ${appKey}`);
496
517
  }
497
- const { client } = createOAuthClient();
498
- const { tokens } = await client.getToken({
499
- code,
500
- codeVerifier,
501
- });
518
+ const oauthClient = createOAuthClient();
519
+ const client = attachAbortSignal(oauthClient.client, signal);
520
+ const { tokens } = await waitForAbortableResult(
521
+ client.getToken({
522
+ code,
523
+ codeVerifier,
524
+ }),
525
+ signal,
526
+ );
502
527
  client.setCredentials(tokens);
503
528
  const oauth2 = google.oauth2({ version: 'v2', auth: client });
504
- const profile = await oauth2.userinfo.get();
529
+ const profile = await waitForAbortableResult(oauth2.userinfo.get(), signal);
505
530
  const accountEmail = String(profile.data.email || '').trim();
506
531
  if (!accountEmail) {
507
532
  throw new Error('Google OAuth did not return an account email address.');
@@ -523,26 +548,37 @@ function createGoogleWorkspaceProvider() {
523
548
  },
524
549
  };
525
550
  },
526
- async disconnect(connectionRow) {
527
- const auth = await buildAuthorizedClient(connectionRow);
551
+ async disconnect(connectionRow, executionOptions = {}) {
552
+ const auth = await buildAuthorizedClient(connectionRow, executionOptions);
528
553
  const refreshToken = auth.credentials.refresh_token;
529
554
  const accessToken = auth.credentials.access_token;
530
555
  if (refreshToken) {
531
- await auth.revokeToken(refreshToken).catch((error) => {
556
+ await waitForAbortableResult(
557
+ auth.revokeToken(refreshToken),
558
+ executionOptions.signal,
559
+ ).catch((error) => {
532
560
  console.warn(
533
561
  `[Google Workspace] Failed to revoke refresh token for disconnect (connection ${connectionRow?.id || 'unknown'}): ${error?.message || error}`,
534
562
  );
535
563
  });
536
564
  } else if (accessToken) {
537
- await auth.revokeToken(accessToken).catch((error) => {
565
+ await waitForAbortableResult(
566
+ auth.revokeToken(accessToken),
567
+ executionOptions.signal,
568
+ ).catch((error) => {
538
569
  console.warn(
539
570
  `[Google Workspace] Failed to revoke access token for disconnect (connection ${connectionRow?.id || 'unknown'}): ${error?.message || error}`,
540
571
  );
541
572
  });
542
573
  }
543
574
  },
544
- async executeTool(toolName, args, connectionRow) {
545
- return executeGoogleWorkspaceTool(toolName, args, connectionRow);
575
+ async executeTool(toolName, args, connectionRow, executionOptions = {}) {
576
+ return executeGoogleWorkspaceTool(
577
+ toolName,
578
+ args,
579
+ connectionRow,
580
+ executionOptions,
581
+ );
546
582
  },
547
583
  async collectMemoryDocuments(options) {
548
584
  return collectGoogleMemoryDocuments(options);
@@ -2,8 +2,11 @@
2
2
 
3
3
  const dns = require('dns').promises;
4
4
  const net = require('net');
5
+ const {
6
+ fetchResponseText,
7
+ waitForAbortableResult,
8
+ } = require('../http');
5
9
 
6
- const HTTP_TIMEOUT_MS = 15000;
7
10
  const ALLOWED_PORTS = new Set(['', '443', '8123']);
8
11
 
9
12
  function trimText(value) {
@@ -100,7 +103,7 @@ function isBlockedIpAddress(address) {
100
103
  return true;
101
104
  }
102
105
 
103
- async function assertPublicHomeAssistantEndpoint(baseUrl) {
106
+ async function assertPublicHomeAssistantEndpoint(baseUrl, options = {}) {
104
107
  const url = new URL(normalizeHomeAssistantBaseUrl(baseUrl));
105
108
  const hostname = String(url.hostname || '').replace(/^\[|\]$/g, '');
106
109
  if (net.isIP(hostname) && isBlockedIpAddress(hostname)) {
@@ -109,7 +112,10 @@ async function assertPublicHomeAssistantEndpoint(baseUrl) {
109
112
 
110
113
  let addresses;
111
114
  try {
112
- addresses = await dns.lookup(hostname, { all: true, verbatim: true });
115
+ addresses = await waitForAbortableResult(
116
+ dns.lookup(hostname, { all: true, verbatim: true }),
117
+ options.signal,
118
+ );
113
119
  } catch (error) {
114
120
  throw new Error(`Could not resolve Home Assistant host: ${error?.message || 'DNS lookup failed'}`);
115
121
  }
@@ -140,7 +146,7 @@ function buildHomeAssistantUrl(baseUrl, path, query = {}) {
140
146
  async function homeAssistantRequest(credentials, options = {}) {
141
147
  const baseUrl = normalizeHomeAssistantBaseUrl(credentials.baseUrl);
142
148
  const token = requireText(credentials.token, 'Home Assistant token');
143
- await assertPublicHomeAssistantEndpoint(baseUrl);
149
+ await assertPublicHomeAssistantEndpoint(baseUrl, { signal: options.signal });
144
150
 
145
151
  const method = String(options.method || 'GET').trim().toUpperCase();
146
152
  if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
@@ -154,31 +160,22 @@ async function homeAssistantRequest(credentials, options = {}) {
154
160
  body = JSON.stringify(options.body);
155
161
  }
156
162
 
157
- const controller = new AbortController();
158
- const timer = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
159
- let response;
160
- try {
161
- response = await fetch(buildHomeAssistantUrl(baseUrl, options.path, options.query), {
163
+ const { response, text } = await fetchResponseText(
164
+ buildHomeAssistantUrl(baseUrl, options.path, options.query),
165
+ {
162
166
  method,
163
167
  headers,
164
168
  body,
165
169
  redirect: 'manual',
166
- signal: controller.signal,
167
- });
168
- } catch (error) {
169
- if (error?.name === 'AbortError') {
170
- throw new Error(`Home Assistant request timed out after ${HTTP_TIMEOUT_MS}ms.`);
171
- }
172
- throw error;
173
- } finally {
174
- clearTimeout(timer);
175
- }
170
+ signal: options.signal,
171
+ },
172
+ { serviceName: 'Home Assistant' },
173
+ );
176
174
 
177
175
  if (response.status >= 300 && response.status < 400) {
178
176
  throw new Error('Home Assistant redirected the API request; redirects are not followed.');
179
177
  }
180
178
 
181
- const text = await response.text();
182
179
  let data = null;
183
180
  try {
184
181
  data = text ? JSON.parse(text) : null;
@@ -172,8 +172,10 @@ function createHomeAssistantProvider() {
172
172
  }
173
173
  return 'Home Assistant: native Home Assistant access is connected in this run with tools for config, states, entity state, service calls, and /api requests.';
174
174
  },
175
- async executeTool(toolName, args, connection) {
176
- return executeHomeAssistantTool(toolName, args, connection);
175
+ async executeTool(toolName, args, connection, executionOptions = {}) {
176
+ return executeHomeAssistantTool(toolName, args, connection, {
177
+ signal: executionOptions.signal || null,
178
+ });
177
179
  },
178
180
  getUserConfig({ userId, agentId }) {
179
181
  const normalizedUserId = Number(userId);
@@ -186,7 +188,7 @@ function createHomeAssistantProvider() {
186
188
  hasConnectedAccount: connection?.status === 'connected',
187
189
  };
188
190
  },
189
- async saveUserConfig({ userId, agentId, config }) {
191
+ async saveUserConfig({ userId, agentId, config, signal }) {
190
192
  const normalizedUserId = Number(userId);
191
193
  if (!Number.isInteger(normalizedUserId) || normalizedUserId <= 0) {
192
194
  throw new Error('A valid user is required to save Home Assistant configuration.');
@@ -203,10 +205,10 @@ function createHomeAssistantProvider() {
203
205
  if (!token) throw new Error('Home Assistant Long-Lived Access Token is required.');
204
206
 
205
207
  const credentials = { baseUrl, token };
206
- await assertPublicHomeAssistantEndpoint(baseUrl);
208
+ await assertPublicHomeAssistantEndpoint(baseUrl, { signal });
207
209
  let haConfig;
208
210
  try {
209
- haConfig = await fetchHomeAssistantConfig(credentials);
211
+ haConfig = await fetchHomeAssistantConfig(credentials, { signal });
210
212
  } catch (error) {
211
213
  const message = String(error?.message || '').toLowerCase();
212
214
  if (message.includes('401') || message.includes('unauthorized')) {
@@ -3,8 +3,12 @@
3
3
  const { decryptValue } = require('../secrets');
4
4
  const { homeAssistantRequest, normalizeHomeAssistantBaseUrl, requireText, trimText } = require('./network');
5
5
 
6
- async function fetchHomeAssistantConfig(credentials) {
7
- return homeAssistantRequest(credentials, { method: 'GET', path: '/api/config' });
6
+ async function fetchHomeAssistantConfig(credentials, options = {}) {
7
+ return homeAssistantRequest(credentials, {
8
+ method: 'GET',
9
+ path: '/api/config',
10
+ signal: options.signal,
11
+ });
8
12
  }
9
13
 
10
14
  function parseCredentials(connection) {
@@ -46,16 +50,21 @@ function validateServiceSegment(value, label) {
46
50
  return text;
47
51
  }
48
52
 
49
- async function executeHomeAssistantTool(toolName, args, connection) {
53
+ async function executeHomeAssistantTool(toolName, args, connection, options = {}) {
50
54
  const credentials = connectionCredentials(connection);
55
+ const signal = options.signal || null;
51
56
 
52
57
  switch (toolName) {
53
58
  case 'home_assistant_get_config':
54
- return { result: await fetchHomeAssistantConfig(credentials) };
59
+ return { result: await fetchHomeAssistantConfig(credentials, { signal }) };
55
60
  case 'home_assistant_list_states':
56
61
  return {
57
62
  result: filterStates(
58
- await homeAssistantRequest(credentials, { method: 'GET', path: '/api/states' }),
63
+ await homeAssistantRequest(credentials, {
64
+ method: 'GET',
65
+ path: '/api/states',
66
+ signal,
67
+ }),
59
68
  args,
60
69
  ),
61
70
  };
@@ -64,6 +73,7 @@ async function executeHomeAssistantTool(toolName, args, connection) {
64
73
  result: await homeAssistantRequest(credentials, {
65
74
  method: 'GET',
66
75
  path: `/api/states/${encodeURIComponent(requireText(args.entity_id, 'entity_id'))}`,
76
+ signal,
67
77
  }),
68
78
  };
69
79
  case 'home_assistant_call_service': {
@@ -77,6 +87,7 @@ async function executeHomeAssistantTool(toolName, args, connection) {
77
87
  method: 'POST',
78
88
  path: `/api/services/${domain}/${service}`,
79
89
  body: serviceData,
90
+ signal,
80
91
  }),
81
92
  };
82
93
  }
@@ -87,6 +98,7 @@ async function executeHomeAssistantTool(toolName, args, connection) {
87
98
  path: requireText(args.path, 'path'),
88
99
  query: args.query && typeof args.query === 'object' ? args.query : {},
89
100
  body: args.body && typeof args.body === 'object' ? args.body : undefined,
101
+ signal,
90
102
  }),
91
103
  };
92
104
  default:
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ const { createAbortError } = require('../../utils/abort');
4
+ const {
5
+ DEFAULT_MAX_RESPONSE_BYTES,
6
+ fetchResponseText: fetchBoundedResponseText,
7
+ readResponseText: readBoundedResponseText,
8
+ waitForAbortableResult,
9
+ waitForBoundedResult: waitForBoundedIo,
10
+ } = require('../network/http');
11
+
12
+ const DEFAULT_INTEGRATION_HTTP_TIMEOUT_MS = 15000;
13
+
14
+ function abortError(signal, fallback = 'Integration request aborted.') {
15
+ return createAbortError(signal, fallback);
16
+ }
17
+
18
+ async function readResponseText(response, options = {}) {
19
+ return readBoundedResponseText(response, {
20
+ ...options,
21
+ serviceName: String(options.serviceName || 'Integration').trim() || 'Integration',
22
+ tooLargeCode: 'INTEGRATION_RESPONSE_TOO_LARGE',
23
+ });
24
+ }
25
+
26
+ async function fetchResponseText(url, options = {}, context = {}) {
27
+ return fetchBoundedResponseText(url, {
28
+ ...options,
29
+ serviceName: String(context.serviceName || 'Integration').trim() || 'Integration',
30
+ timeoutCode: 'INTEGRATION_HTTP_TIMEOUT',
31
+ tooLargeCode: 'INTEGRATION_RESPONSE_TOO_LARGE',
32
+ });
33
+ }
34
+
35
+ async function waitForBoundedResult(promise, options = {}) {
36
+ return waitForBoundedIo(promise, {
37
+ ...options,
38
+ serviceName: String(options.serviceName || 'Integration').trim() || 'Integration',
39
+ timeoutCode: 'INTEGRATION_HTTP_TIMEOUT',
40
+ });
41
+ }
42
+
43
+ module.exports = {
44
+ DEFAULT_INTEGRATION_HTTP_TIMEOUT_MS,
45
+ DEFAULT_MAX_RESPONSE_BYTES,
46
+ abortError,
47
+ fetchResponseText,
48
+ readResponseText,
49
+ waitForAbortableResult,
50
+ waitForBoundedResult,
51
+ };