newmark-agent 0.3.10 → 0.3.12

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 (50) hide show
  1. package/config.example.json +6 -0
  2. package/dist/cli-commands.d.ts +7 -0
  3. package/dist/cli-commands.js +206 -15
  4. package/dist/cli-discovery.d.ts +15 -0
  5. package/dist/cli-discovery.js +182 -0
  6. package/dist/cli-help.d.ts +4 -0
  7. package/dist/cli-help.js +46 -0
  8. package/dist/conversation-utility-host.bundle.cjs +548 -137
  9. package/dist/core/agent.d.ts +18 -3
  10. package/dist/core/agent.js +236 -34
  11. package/dist/core/agentKernelRunner.js +72 -10
  12. package/dist/core/browserControl.d.ts +8 -0
  13. package/dist/core/browserUsePageAdapter.d.ts +3 -0
  14. package/dist/core/browserUsePageAdapter.js +19 -2
  15. package/dist/core/computerUseSession.d.ts +44 -0
  16. package/dist/core/computerUseSession.js +105 -0
  17. package/dist/core/config.d.ts +7 -2
  18. package/dist/core/config.js +24 -6
  19. package/dist/core/conversationKernel.d.ts +1 -0
  20. package/dist/core/conversationKernel.js +39 -1
  21. package/dist/core/electronBrowserUseHost.js +7 -0
  22. package/dist/core/electronUtilityAgentClient.js +84 -2
  23. package/dist/core/electronUtilityRuntimePool.d.ts +11 -0
  24. package/dist/core/electronUtilityRuntimePool.js +62 -0
  25. package/dist/core/flow-runner.js +1 -1
  26. package/dist/core/modelValidationStore.d.ts +4 -1
  27. package/dist/core/modelValidationStore.js +7 -1
  28. package/dist/core/utilityHostToolRouter.d.ts +7 -0
  29. package/dist/core/utilityHostToolRouter.js +25 -33
  30. package/dist/core/workspace.d.ts +6 -0
  31. package/dist/core/workspace.js +14 -0
  32. package/dist/core/wslAgentRuntimePool.d.ts +4 -0
  33. package/dist/core/wslAgentRuntimePool.js +56 -0
  34. package/dist/launcher.js +51 -10
  35. package/dist/llm/provider.d.ts +8 -5
  36. package/dist/llm/provider.js +85 -33
  37. package/dist/main.js +297 -61
  38. package/dist/preload.js +10 -2
  39. package/dist/providers/chat-completions.adapter.js +1 -5
  40. package/dist/providers/provider-events.d.ts +7 -0
  41. package/dist/providers/provider-events.js +44 -0
  42. package/dist/providers/responses.adapter.js +1 -3
  43. package/dist/tools/index.js +36 -49
  44. package/dist/tui/src/adapters/core-runtime-adapter.js +40 -3
  45. package/dist/tui/src/app.js +47 -13
  46. package/dist/tui/src/render.js +23 -7
  47. package/dist/tui/src/state.js +61 -9
  48. package/dist/ui/index.html +545 -122
  49. package/dist/wsl-agent-host.bundle.cjs +548 -137
  50. package/package.json +14 -5
@@ -43,6 +43,19 @@ const child_process_1 = require("child_process");
43
43
  const agentKernelDiagnostics_1 = require("../core/agentKernelDiagnostics");
44
44
  const chat_messages_1 = require("../providers/chat-messages");
45
45
  const providers_1 = require("../providers");
46
+ // Keep provider requests below the release-harness/user-visible command
47
+ // deadline. A provider that does not answer must produce one bounded error;
48
+ // it must not restart the same request through every Windows transport.
49
+ const DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS = 90_000;
50
+ const MIN_PROVIDER_REQUEST_TIMEOUT_MS = 50;
51
+ function providerTimeoutError(timeoutMs) {
52
+ const error = new Error(`Provider request timed out after ${timeoutMs}ms`);
53
+ error.name = 'TimeoutError';
54
+ return error;
55
+ }
56
+ function isProviderTimeoutError(error) {
57
+ return error instanceof Error && error.name === 'TimeoutError';
58
+ }
46
59
  function abortFailure(signal) {
47
60
  const reason = signal?.reason;
48
61
  const error = reason instanceof Error ? reason : new Error(reason ? String(reason) : 'LLM request aborted');
@@ -84,15 +97,37 @@ class LLMProvider {
84
97
  explicitProtocol;
85
98
  openAIMode;
86
99
  useProviderAdaptersV2;
100
+ requestTimeoutMs;
87
101
  static nodeHttpTransport = null;
88
102
  static powershellTransport = null;
89
- constructor(name, baseUrl, apiKey, explicitProtocol, openAIMode = 'chat_stream', useProviderAdaptersV2 = false) {
103
+ constructor(name, baseUrl, apiKey, explicitProtocol, openAIMode = 'chat_stream', useProviderAdaptersV2 = false, requestTimeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
90
104
  this.name = name;
91
105
  this.baseUrl = baseUrl;
92
106
  this.apiKey = apiKey;
93
107
  this.explicitProtocol = explicitProtocol;
94
108
  this.openAIMode = openAIMode;
95
109
  this.useProviderAdaptersV2 = useProviderAdaptersV2;
110
+ this.requestTimeoutMs = requestTimeoutMs;
111
+ }
112
+ effectiveRequestTimeout(timeoutMs) {
113
+ const requested = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
114
+ const configured = Number.isFinite(this.requestTimeoutMs) && this.requestTimeoutMs > 0
115
+ ? this.requestTimeoutMs
116
+ : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
117
+ return Math.max(MIN_PROVIDER_REQUEST_TIMEOUT_MS, Math.min(requested, configured));
118
+ }
119
+ async withRequestTimeout(promise, timeoutMs, signal) {
120
+ let timer;
121
+ const timeoutPromise = new Promise((_, reject) => {
122
+ timer = setTimeout(() => reject(providerTimeoutError(timeoutMs)), timeoutMs);
123
+ });
124
+ try {
125
+ return await abortable(Promise.race([promise, timeoutPromise]), signal);
126
+ }
127
+ finally {
128
+ if (timer)
129
+ clearTimeout(timer);
130
+ }
96
131
  }
97
132
  intelligenceConfig(tier) {
98
133
  switch (tier) {
@@ -212,6 +247,7 @@ class LLMProvider {
212
247
  };
213
248
  }
214
249
  async postJsonWithFetchFallback(url, headers, body, timeoutMs = 120000, signal) {
250
+ const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
215
251
  // Electron utility processes can leave an undici response body pending when
216
252
  // several isolated workers concurrently call a plain-HTTP local provider.
217
253
  // Node's HTTP client owns the full body lifecycle and is deterministic for
@@ -224,7 +260,7 @@ class LLMProvider {
224
260
  return '';
225
261
  } })();
226
262
  this.transportDiagnostic('loopback:start', pathname);
227
- const local = await this.nodeHttpJson('POST', url, headers, JSON.stringify(body), signal);
263
+ const local = await this.nodeHttpJson('POST', url, headers, JSON.stringify(body), signal, effectiveTimeout);
228
264
  this.transportDiagnostic('loopback:complete', `status=${local.status} bytes=${Buffer.byteLength(local.body || '')}`);
229
265
  return {
230
266
  ok: local.status >= 200 && local.status < 300,
@@ -240,7 +276,7 @@ class LLMProvider {
240
276
  forwardAbort();
241
277
  else
242
278
  signal?.addEventListener('abort', forwardAbort, { once: true });
243
- const timer = setTimeout(() => abort.abort(), timeoutMs);
279
+ const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
244
280
  try {
245
281
  const response = await fetch(url, {
246
282
  method: 'POST',
@@ -253,9 +289,11 @@ class LLMProvider {
253
289
  catch (e) {
254
290
  if (signal?.aborted)
255
291
  throw abortFailure(signal);
292
+ if (abort.signal.aborted)
293
+ throw abortFailure(abort.signal);
256
294
  if (!this.shouldUseNodeHttpFallback(e))
257
295
  throw e;
258
- const fallback = await this.nodeHttpJson('POST', url, headers, JSON.stringify(body), signal);
296
+ const fallback = await this.nodeHttpJson('POST', url, headers, JSON.stringify(body), signal, effectiveTimeout);
259
297
  return {
260
298
  ok: fallback.status >= 200 && fallback.status < 300,
261
299
  status: fallback.status,
@@ -270,16 +308,19 @@ class LLMProvider {
270
308
  }
271
309
  }
272
310
  async getJsonWithFetchFallback(url, headers, timeoutMs = 30000) {
311
+ const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
273
312
  const abort = new AbortController();
274
- const timer = setTimeout(() => abort.abort(), timeoutMs);
313
+ const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
275
314
  try {
276
315
  const response = await fetch(url, { method: 'GET', headers, signal: abort.signal });
277
316
  return response;
278
317
  }
279
318
  catch (e) {
319
+ if (abort.signal.aborted)
320
+ throw abortFailure(abort.signal);
280
321
  if (!this.shouldUseNodeHttpFallback(e))
281
322
  throw e;
282
- const fallback = await this.nodeHttpJson('GET', url, headers);
323
+ const fallback = await this.nodeHttpJson('GET', url, headers, '', undefined, effectiveTimeout);
283
324
  return {
284
325
  ok: fallback.status >= 200 && fallback.status < 300,
285
326
  status: fallback.status,
@@ -293,16 +334,21 @@ class LLMProvider {
293
334
  }
294
335
  }
295
336
  shouldUseNodeHttpFallback(error) {
296
- return ((error instanceof TypeError && /fetch failed/i.test(error.message)) ||
297
- (error instanceof Error && /abort/i.test(error.name || error.message)));
337
+ // Abort is a completed control decision (user cancellation or our own
338
+ // deadline), not evidence that a second transport can succeed. Retrying it
339
+ // on Windows used to create a second 120s request after the first timeout.
340
+ return error instanceof TypeError && /fetch failed/i.test(error.message);
298
341
  }
299
- nodeHttpJson(method, urlValue, headers, body = '', signal) {
342
+ nodeHttpJson(method, urlValue, headers, body = '', signal, timeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
343
+ const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
300
344
  if (LLMProvider.nodeHttpTransport) {
301
- return abortable(LLMProvider.nodeHttpTransport(method, urlValue, headers, body), signal).catch(error => {
345
+ return this.withRequestTimeout(LLMProvider.nodeHttpTransport(method, urlValue, headers, body), effectiveTimeout, signal).catch(error => {
302
346
  if (signal?.aborted)
303
347
  throw abortFailure(signal);
348
+ if (isProviderTimeoutError(error))
349
+ throw error;
304
350
  if (process.platform === 'win32') {
305
- return this.powershellJson(method, urlValue, headers, body, signal);
351
+ return this.powershellJson(method, urlValue, headers, body, signal, effectiveTimeout);
306
352
  }
307
353
  throw error;
308
354
  });
@@ -350,8 +396,8 @@ class LLMProvider {
350
396
  fail(new Error('Node HTTP response closed before completion'));
351
397
  });
352
398
  });
353
- req.setTimeout(120000, () => {
354
- req.destroy(new Error('Node HTTP fallback timeout'));
399
+ req.setTimeout(effectiveTimeout, () => {
400
+ req.destroy(providerTimeoutError(effectiveTimeout));
355
401
  });
356
402
  req.on('error', reject);
357
403
  const onAbort = () => req.destroy(abortFailure(signal));
@@ -366,15 +412,18 @@ class LLMProvider {
366
412
  }).catch(error => {
367
413
  if (signal?.aborted)
368
414
  throw abortFailure(signal);
415
+ if (isProviderTimeoutError(error))
416
+ throw error;
369
417
  if (process.platform === 'win32') {
370
- return this.powershellJson(method, urlValue, headers, body, signal);
418
+ return this.powershellJson(method, urlValue, headers, body, signal, effectiveTimeout);
371
419
  }
372
420
  throw error;
373
421
  });
374
422
  }
375
- powershellJson(method, urlValue, headers, body = '', signal) {
423
+ powershellJson(method, urlValue, headers, body = '', signal, timeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
424
+ const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
376
425
  if (LLMProvider.powershellTransport) {
377
- return LLMProvider.powershellTransport(method, urlValue, headers, body);
426
+ return this.withRequestTimeout(LLMProvider.powershellTransport(method, urlValue, headers, body), effectiveTimeout, signal);
378
427
  }
379
428
  return new Promise((resolve, reject) => {
380
429
  const headerJson = JSON.stringify(headers);
@@ -403,7 +452,7 @@ class LLMProvider {
403
452
  ' $raw = $headerJson | ConvertFrom-Json',
404
453
  ' foreach ($p in $raw.PSObject.Properties) { $headers[$p.Name] = [string]$p.Value }',
405
454
  '}',
406
- '$params = @{ Uri = $uri; Method = $method; Headers = $headers; UseBasicParsing = $true; TimeoutSec = 120 }',
455
+ `'$params = @{ Uri = $uri; Method = $method; Headers = $headers; UseBasicParsing = $true; TimeoutSec = ${Math.max(1, Math.ceil(effectiveTimeout / 1000))} }`,
407
456
  'if ($method -eq "POST") { $params["Body"] = $bodyJson }',
408
457
  'if ($method -eq "POST") { $params["ContentType"] = "application/json; charset=utf-8" }',
409
458
  '$resp = Invoke-WebRequest @params',
@@ -437,8 +486,8 @@ class LLMProvider {
437
486
  const timer = setTimeout(() => {
438
487
  child.kill();
439
488
  cleanup();
440
- reject(new Error('PowerShell HTTP fallback timeout'));
441
- }, 130000);
489
+ reject(providerTimeoutError(effectiveTimeout));
490
+ }, effectiveTimeout + 5000);
442
491
  child.stdout.setEncoding('utf8');
443
492
  child.stderr.setEncoding('utf8');
444
493
  child.stdout.on('data', chunk => { stdout += chunk; });
@@ -926,10 +975,10 @@ class LLMProvider {
926
975
  return this.shouldUseResponsesFallback(Number(match[1]), errorText);
927
976
  }
928
977
  /**
929
- * Loopback-aware transport injected into adapter `execute`. Mirrors the
930
- * legacy orchestration exactly: streaming requests go through fetch with a
931
- * 120s timeout and degrade to a non-streaming node-http request on fetch
932
- * failure; non-streaming requests reuse postJsonWithFetchFallback.
978
+ * Loopback-aware transport injected into adapter `execute`. Streaming
979
+ * requests retain the fetch-to-node fallback for transport failures, while
980
+ * a local deadline is returned directly so one request cannot become a
981
+ * second Windows fallback request.
933
982
  */
934
983
  buildProviderAdapterTransport() {
935
984
  return async (request, signal) => {
@@ -940,7 +989,8 @@ class LLMProvider {
940
989
  forwardAbort();
941
990
  else
942
991
  signal?.addEventListener('abort', forwardAbort, { once: true });
943
- const timer = setTimeout(() => abort.abort(), 120000);
992
+ const effectiveTimeout = this.effectiveRequestTimeout(120000);
993
+ const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
944
994
  try {
945
995
  try {
946
996
  return await fetch(request.url, {
@@ -953,11 +1003,13 @@ class LLMProvider {
953
1003
  catch (error) {
954
1004
  if (signal?.aborted)
955
1005
  throw abortFailure(signal);
1006
+ if (abort.signal.aborted)
1007
+ throw abortFailure(abort.signal);
956
1008
  if (!this.shouldUseNodeHttpFallback(error))
957
1009
  throw error;
958
1010
  const fallbackHeaders = { ...request.headers };
959
1011
  delete fallbackHeaders['Accept'];
960
- const fallback = await this.postJsonWithFetchFallback(request.url, fallbackHeaders, { ...request.body, stream: false }, 120000, signal);
1012
+ const fallback = await this.postJsonWithFetchFallback(request.url, fallbackHeaders, { ...request.body, stream: false }, effectiveTimeout, signal);
961
1013
  return this.toTransportResponse(fallback);
962
1014
  }
963
1015
  }
@@ -1071,7 +1123,8 @@ class LLMProvider {
1071
1123
  forwardAbort();
1072
1124
  else
1073
1125
  signal?.addEventListener('abort', forwardAbort, { once: true });
1074
- const timeout = setTimeout(() => abort.abort(), 120000);
1126
+ const effectiveTimeout = this.effectiveRequestTimeout(120000);
1127
+ const timeout = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
1075
1128
  let reader = null;
1076
1129
  try {
1077
1130
  let response;
@@ -1084,11 +1137,13 @@ class LLMProvider {
1084
1137
  });
1085
1138
  }
1086
1139
  catch (e) {
1140
+ if (signal?.aborted)
1141
+ throw abortFailure(signal);
1142
+ if (abort.signal.aborted)
1143
+ throw abortFailure(abort.signal);
1087
1144
  if (!this.shouldUseNodeHttpFallback(e))
1088
1145
  throw e;
1089
1146
  clearTimeout(timeout);
1090
- if (signal?.aborted)
1091
- throw abortFailure(signal);
1092
1147
  yield* this.githubModelsChatNonStreaming(url, body, signal);
1093
1148
  return;
1094
1149
  }
@@ -1108,12 +1163,9 @@ class LLMProvider {
1108
1163
  let currentReasoningContent = '';
1109
1164
  let contentPolicyBlocked = false;
1110
1165
  let emittedContent = false;
1166
+ const streamSignal = signal || new AbortController().signal;
1111
1167
  while (true) {
1112
- if (signal?.aborted)
1113
- throw abortFailure(signal);
1114
- const readPromise = reader.read();
1115
- const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Stream read timeout')), 30000));
1116
- const { done, value } = await Promise.race([readPromise, timeoutPromise]);
1168
+ const { done, value } = await (0, providers_1.readProviderStreamChunk)(reader, streamSignal);
1117
1169
  if (done)
1118
1170
  break;
1119
1171
  buffer += decoder.decode(value, { stream: true });