@zackbart/connecta 0.7.7 → 0.7.9

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 (59) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/dist/activity.d.ts +1 -1
  3. package/dist/activity.d.ts.map +1 -1
  4. package/dist/activity.js.map +1 -1
  5. package/dist/call-admission.d.ts +81 -0
  6. package/dist/call-admission.d.ts.map +1 -0
  7. package/dist/call-admission.js +339 -0
  8. package/dist/call-admission.js.map +1 -0
  9. package/dist/catalog.d.ts +5 -3
  10. package/dist/catalog.d.ts.map +1 -1
  11. package/dist/catalog.js +13 -4
  12. package/dist/catalog.js.map +1 -1
  13. package/dist/connectors/api.d.ts +3 -1
  14. package/dist/connectors/api.d.ts.map +1 -1
  15. package/dist/connectors/api.js +1 -0
  16. package/dist/connectors/api.js.map +1 -1
  17. package/dist/connectors/remote-mcp.d.ts +3 -1
  18. package/dist/connectors/remote-mcp.d.ts.map +1 -1
  19. package/dist/connectors/remote-mcp.js +1 -0
  20. package/dist/connectors/remote-mcp.js.map +1 -1
  21. package/dist/execute.d.ts.map +1 -1
  22. package/dist/execute.js +72 -30
  23. package/dist/execute.js.map +1 -1
  24. package/dist/index.d.ts +9 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/meta-tools.d.ts +7 -4
  29. package/dist/meta-tools.d.ts.map +1 -1
  30. package/dist/meta-tools.js +212 -54
  31. package/dist/meta-tools.js.map +1 -1
  32. package/dist/registry.d.ts +37 -0
  33. package/dist/registry.d.ts.map +1 -1
  34. package/dist/registry.js +45 -2
  35. package/dist/registry.js.map +1 -1
  36. package/dist/server.d.ts.map +1 -1
  37. package/dist/server.js +5 -0
  38. package/dist/server.js.map +1 -1
  39. package/dist/types.d.ts +55 -0
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/ui.d.ts.map +1 -1
  42. package/dist/ui.js +8 -4
  43. package/dist/ui.js.map +1 -1
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. package/package.json +1 -1
  47. package/src/activity.ts +5 -1
  48. package/src/call-admission.ts +519 -0
  49. package/src/catalog.ts +24 -4
  50. package/src/connectors/api.ts +4 -0
  51. package/src/connectors/remote-mcp.ts +4 -0
  52. package/src/execute.ts +83 -35
  53. package/src/index.ts +14 -1
  54. package/src/meta-tools.ts +246 -70
  55. package/src/registry.ts +91 -1
  56. package/src/server.ts +5 -0
  57. package/src/types.ts +61 -0
  58. package/src/ui.ts +8 -4
  59. package/src/version.ts +1 -1
package/src/execute.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  truncateExecuteText,
17
17
  } from "./executor-result.js";
18
18
  import { classifyCallError, ConnectorCallError } from "./errors.js";
19
+ import { isCallAdmissionError } from "./call-admission.js";
19
20
  import {
20
21
  ExecutorAdmissionError,
21
22
  isAdmittingExecutor,
@@ -192,27 +193,37 @@ export async function buildSandboxProviders(
192
193
  const controller = new AbortController();
193
194
  const cancel = () => controller.abort(limits.signal?.reason);
194
195
  limits.signal?.addEventListener("abort", cancel, { once: true });
196
+ if (limits.signal?.aborted) cancel();
195
197
  let timer: ReturnType<typeof setTimeout> | undefined;
196
- let rejectCancelled!: (reason: Error) => void;
197
- const cancelled = new Promise<never>((_, reject) => {
198
- rejectCancelled = reject;
199
- });
200
- const onAbort = () => {
201
- rejectCancelled(
202
- controller.signal.reason instanceof Error
203
- ? controller.signal.reason
204
- : new Error("execute_code host call cancelled"),
205
- );
206
- };
207
- controller.signal.addEventListener("abort", onAbort, { once: true });
208
- const ctx = registry.contextFor(
209
- resolved.connector.id,
210
- baseUrl,
211
- requestScope,
212
- { signal: controller.signal, timeoutMs: hostCallTimeoutMs },
213
- );
198
+ let onAbort: (() => void) | undefined;
214
199
  const started = Date.now();
200
+ let permit: Awaited<ReturnType<RegistryView["admitCall"]>> | undefined;
215
201
  try {
202
+ permit = await registry.admitCall(resolved.connector.id, {
203
+ toolName: resolved.toolName,
204
+ args: args ?? {},
205
+ signal: controller.signal,
206
+ });
207
+ let rejectCancelled!: (reason: Error) => void;
208
+ const cancelled = new Promise<never>((_, reject) => {
209
+ rejectCancelled = reject;
210
+ });
211
+ onAbort = () => {
212
+ rejectCancelled(
213
+ controller.signal.reason instanceof Error
214
+ ? controller.signal.reason
215
+ : new Error("execute_code host call cancelled"),
216
+ );
217
+ };
218
+ controller.signal.addEventListener("abort", onAbort, { once: true });
219
+ if (controller.signal.aborted) onAbort();
220
+ if (controller.signal.aborted) await cancelled;
221
+ const ctx = registry.contextFor(
222
+ resolved.connector.id,
223
+ baseUrl,
224
+ requestScope,
225
+ { signal: controller.signal, timeoutMs: hostCallTimeoutMs },
226
+ );
216
227
  timer = setTimeout(() => {
217
228
  controller.abort(
218
229
  new ConnectorCallError(
@@ -242,14 +253,34 @@ export async function buildSandboxProviders(
242
253
  });
243
254
  return value;
244
255
  } catch (err) {
245
- registry.recordFailure(resolved.connector.id, Date.now() - started, err);
246
- const details = classifyCallError(err);
256
+ const callerCancelled =
257
+ limits.signal?.aborted === true ||
258
+ (isCallAdmissionError(err) && err.admissionKind === "cancelled");
259
+ if (!callerCancelled && !isCallAdmissionError(err)) {
260
+ registry.recordFailure(
261
+ resolved.connector.id,
262
+ Date.now() - started,
263
+ err,
264
+ );
265
+ }
266
+ const details = callerCancelled
267
+ ? {
268
+ code: "cancelled",
269
+ message: "Tool call was cancelled by the caller.",
270
+ retryable: false,
271
+ }
272
+ : classifyCallError(err);
247
273
  recordToolActivity(activity, {
248
274
  connectorId: resolved.connector.id,
249
275
  toolName: resolved.toolName,
250
276
  address: `${resolved.connector.id}.${resolved.toolName}`,
251
277
  source: "execute_code",
252
- outcome: details.code === "timeout" ? "timeout" : "error",
278
+ outcome:
279
+ details.code === "timeout"
280
+ ? "timeout"
281
+ : details.code === "cancelled"
282
+ ? "cancelled"
283
+ : "error",
253
284
  durationMs: Date.now() - started,
254
285
  attempts: 1,
255
286
  errorCode: details.code,
@@ -257,8 +288,9 @@ export async function buildSandboxProviders(
257
288
  throw err;
258
289
  } finally {
259
290
  if (timer) clearTimeout(timer);
260
- controller.signal.removeEventListener("abort", onAbort);
291
+ if (onAbort) controller.signal.removeEventListener("abort", onAbort);
261
292
  limits.signal?.removeEventListener("abort", cancel);
293
+ permit?.release();
262
294
  }
263
295
  };
264
296
  for (let i = 0; i < connectors.length; i++) {
@@ -369,20 +401,33 @@ export async function buildSandboxProviders(
369
401
  score: number;
370
402
  order: number;
371
403
  }> = [];
372
- let orderBase = 0;
373
- for (const connector of connectors) {
374
- if (args.connector && connector.id !== args.connector) continue;
375
- const tools = catalogs.get(connector.id);
376
- if (!tools) continue;
377
- for (const ranked of rankTools(tools, args.query ?? "")) {
378
- matches.push({
379
- connector: connector.id,
380
- tool: ranked.tool,
381
- score: ranked.score,
382
- order: orderBase + ranked.order,
383
- });
404
+ let matchMode: "all" | "partial" = "all";
405
+ const collectMatches = (mode: "all" | "partial") => {
406
+ matches.length = 0;
407
+ let orderBase = 0;
408
+ for (const connector of connectors) {
409
+ if (args.connector && connector.id !== args.connector) continue;
410
+ const tools = catalogs.get(connector.id);
411
+ if (!tools) continue;
412
+ for (const ranked of rankTools(
413
+ tools,
414
+ args.query ?? "",
415
+ mode,
416
+ )) {
417
+ matches.push({
418
+ connector: connector.id,
419
+ tool: ranked.tool,
420
+ score: ranked.score,
421
+ order: orderBase + ranked.order,
422
+ });
423
+ }
424
+ orderBase += tools.length;
384
425
  }
385
- orderBase += tools.length;
426
+ };
427
+ collectMatches("all");
428
+ if ((args.query ?? "").trim() && matches.length === 0) {
429
+ matchMode = "partial";
430
+ collectMatches(matchMode);
386
431
  }
387
432
  matches.sort((a, b) => b.score - a.score || a.order - b.order);
388
433
  const offset = Math.max(0, Math.trunc(args.offset ?? 0));
@@ -428,6 +473,9 @@ export async function buildSandboxProviders(
428
473
  limit,
429
474
  hasMore: nextOffset !== undefined,
430
475
  ...(nextOffset !== undefined ? { nextOffset } : {}),
476
+ ...(matchMode === "partial" && matches.length > 0
477
+ ? { matchMode }
478
+ : {}),
431
479
  };
432
480
  assertDiscoveryResultSize(
433
481
  result,
package/src/index.ts CHANGED
@@ -94,7 +94,7 @@ export interface ConnectaDiscoveryConfig {
94
94
  probeTimeoutMs?: number;
95
95
  }
96
96
 
97
- /** Deployment-wide call deadlines and inline-result paging threshold. */
97
+ /** Deployment-wide call deadlines and inline-result paging thresholds. */
98
98
  export interface ConnectaCallsConfig {
99
99
  /**
100
100
  * Deadline (ms) for `call_tool`/`batch_call` calls that pass no `timeoutMs`.
@@ -111,6 +111,13 @@ export interface ConnectaCallsConfig {
111
111
  * 50_000. Connectors may override it individually.
112
112
  */
113
113
  maxResultBytes?: number;
114
+ /**
115
+ * Max serialized `batch_call` envelope size (bytes) before the full batch is
116
+ * stashed for `get_result` and only an ordered outcome summary is returned
117
+ * inline. Must be a finite whole number >= 1; invalid values warn and fall
118
+ * back to 100_000. This cap is independent of per-connector child caps.
119
+ */
120
+ maxBatchResultBytes?: number;
114
121
  }
115
122
 
116
123
  export interface AdmissionPoolConfig {
@@ -544,6 +551,7 @@ export function createConnecta(config: ConnectaConfig): Connecta {
544
551
  persistToolCatalog: config.discovery?.persistCatalog,
545
552
  toolCatalogStaleSeconds: config.discovery?.staleCatalogSeconds,
546
553
  maxResultBytes: config.calls?.maxResultBytes,
554
+ maxBatchResultBytes: config.calls?.maxBatchResultBytes,
547
555
  credentialHealth: config.credentials?.health,
548
556
  });
549
557
  // Throws on every structural mistake it can see (see resolveToolkits): a
@@ -640,6 +648,7 @@ export function createConnecta(config: ConnectaConfig): Connecta {
640
648
  closePromise ??= Promise.resolve().then(async () => {
641
649
  requestAdmission.close();
642
650
  codeAdmission?.close();
651
+ registry.closeCallAdmission();
643
652
  await config.executor?.close?.();
644
653
  });
645
654
  await closePromise;
@@ -690,6 +699,10 @@ export type {
690
699
  export type { ApiOptions, ApiTool } from "./connectors/api.js";
691
700
  export type {
692
701
  Connector,
702
+ ConnectorCallAdmissionInput,
703
+ ConnectorCallAdmissionPolicy,
704
+ ConnectorCallAdmissionRule,
705
+ ConnectorRollingWindowBudget,
693
706
  ConnectaBranding,
694
707
  ConnectorCredentialAccess,
695
708
  ConnectorCredentialConfig,