@zackbart/connecta 0.38.0 → 0.40.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.
- package/CHANGELOG.md +91 -0
- package/README.md +3 -2
- package/bin/connecta.mjs +4 -8
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.js +6 -4
- package/dist/agent-routes.js +231 -0
- package/dist/auth/downstream-oauth/consent.js +519 -0
- package/dist/auth/downstream-oauth/grant-store.js +439 -0
- package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
- package/dist/auth/downstream-oauth/refresh-records.js +447 -0
- package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
- package/dist/auth/downstream-oauth.js +66 -2382
- package/dist/auth/native.js +37 -0
- package/dist/auth/oauth-grant.js +13 -0
- package/dist/auth/static-oauth.js +153 -131
- package/dist/auth-elicitation.js +7 -1
- package/dist/authorization-handoff.js +11 -5
- package/dist/catalog-cache.js +219 -59
- package/dist/catalog-policy.js +18 -0
- package/dist/catalog-service.js +284 -47
- package/dist/catalog.js +18 -2
- package/dist/config.d.ts +16 -11
- package/dist/config.js +9 -6
- package/dist/connector-caller.d.ts +1 -1
- package/dist/connector-health.js +59 -0
- package/dist/connector-status.js +117 -0
- package/dist/connectors/api-connector.d.ts +7 -2
- package/dist/connectors/api-connector.js +7 -4
- package/dist/connectors/negotiation-cache.js +22 -4
- package/dist/connectors/remote-mcp/call.js +164 -0
- package/dist/connectors/remote-mcp/catalog.js +278 -0
- package/dist/connectors/remote-mcp/config.js +263 -0
- package/dist/connectors/remote-mcp/connection.js +682 -0
- package/dist/connectors/remote-mcp/errors.js +488 -0
- package/dist/connectors/remote-mcp/oauth.js +473 -0
- package/dist/connectors/remote-mcp/resources.js +473 -0
- package/dist/connectors/remote-mcp/runtime.js +1 -0
- package/dist/connectors/remote-mcp/transport.js +555 -0
- package/dist/connectors/remote-mcp.js +62 -2863
- package/dist/credential-contract.d.ts +23 -0
- package/dist/credential-reads.js +17 -0
- package/dist/credential-verification.js +53 -0
- package/dist/credentials.d.ts +7 -0
- package/dist/credentials.js +60 -25
- package/dist/describe-config.d.ts +12 -2
- package/dist/describe-config.js +2 -0
- package/dist/discovery-health.js +199 -0
- package/dist/downstream-input.js +2 -0
- package/dist/errors.d.ts +55 -17
- package/dist/errors.js +95 -41
- package/dist/execute.js +152 -114
- package/dist/executor-result.js +3 -1
- package/dist/guest-functions.js +83 -0
- package/dist/guest-runtime.js +60 -19
- package/dist/guest-types.d.ts +19 -5
- package/dist/http/argument-problems.js +35 -0
- package/dist/http/errors.js +5 -4
- package/dist/http/fields.js +192 -13
- package/dist/http/hatch-response.js +73 -0
- package/dist/http/hatch.js +88 -23
- package/dist/http/oauth.js +3 -2
- package/dist/http/provider.js +32 -4
- package/dist/http/safety.js +8 -0
- package/dist/http/tools.js +44 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -2
- package/dist/invocation.js +26 -8
- package/dist/membership.d.ts +13 -5
- package/dist/membership.js +34 -2
- package/dist/meta-tools.js +54 -31
- package/dist/native-recovery.js +4 -16
- package/dist/native-tools.js +5 -2
- package/dist/oauth-sealing.js +10 -0
- package/dist/operation-vocabulary.js +135 -13
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +1 -0
- package/dist/operator-ui/contract.d.ts +10 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +22 -1
- package/dist/provider-call-facts.js +6 -2
- package/dist/providers/_shared/rest/operation-index.js +48 -50
- package/dist/providers/_shared/rest/value-safety.js +80 -0
- package/dist/providers/breeze/api.js +7 -1
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/breeze/tools.js +4 -3
- package/dist/providers/ccb/api.js +2 -1
- package/dist/providers/ccb/index.js +5 -0
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/cloudflare/api.js +8 -1
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/lib/schemas.js +24 -22
- package/dist/providers/docs/safety.js +2 -0
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/docs/tools.js +2 -4
- package/dist/providers/drive/api.js +1 -0
- package/dist/providers/drive/lib/contracts.js +8 -5
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/drive/tools.js +11 -16
- package/dist/providers/forms/api.js +1 -0
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/forms/tools.js +10 -7
- package/dist/providers/github/api.js +1 -0
- package/dist/providers/github/lib/discovery.js +1 -1
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +61 -17
- package/dist/providers/gmail/lib/schemas.js +60 -50
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/gmail/tools.js +7 -5
- package/dist/providers/granola/index.js +2 -0
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/api.js +7 -1
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/planning-center/api.js +3 -1
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/skill.generated.js +1 -1
- package/dist/providers/quickbooks/safety.js +80 -39
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/api.js +1 -0
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/sheets/tools.js +13 -14
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/slides/tools.js +15 -13
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/api.js +3 -1
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/vercel/api.js +1 -1
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/providers/vercel/tools.js +4 -3
- package/dist/registry.d.ts +10 -4
- package/dist/registry.js +41 -13
- package/dist/routes/credentials.js +129 -60
- package/dist/routes/dispatch.js +5 -1
- package/dist/routes/mcp.js +30 -14
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +6 -1
- package/dist/routes/shared.js +7 -1
- package/dist/routes/ui-config.js +2 -2
- package/dist/routes/ui-data.js +126 -20
- package/dist/routes/ui.js +16 -0
- package/dist/runtime/call.js +7 -2
- package/dist/runtime/native-storage.js +12 -0
- package/dist/runtime/request-reads.js +207 -0
- package/dist/runtime/request-scope.js +16 -0
- package/dist/runtime/storage.js +12 -0
- package/dist/skills.js +37 -20
- package/dist/storage/keys.js +53 -3
- package/dist/storage/memory.js +8 -2
- package/dist/storage/sql-native.js +53 -1
- package/dist/storage/sql.js +12 -2
- package/dist/storage-digest.js +32 -0
- package/dist/timeout.js +8 -0
- package/dist/types.d.ts +33 -0
- package/dist/usage-guide.js +16 -35
- package/dist/validate.d.ts +3 -0
- package/dist/validate.js +220 -158
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/write-outcome.js +0 -3
- package/documentation/README.md +13 -13
- package/documentation/architecture.md +385 -20
- package/documentation/auth.md +233 -64
- package/documentation/code-mode.md +122 -39
- package/documentation/deploying.md +6 -3
- package/documentation/integrating.md +16 -2
- package/documentation/meta-tools.md +240 -49
- package/documentation/migration-0.33.md +5 -1
- package/documentation/migration-0.34.md +3 -1
- package/documentation/operator-tests.md +1 -1
- package/documentation/operator-ui.md +105 -12
- package/documentation/pool-membership-upgrade.md +44 -0
- package/documentation/providers.md +105 -0
- package/examples/worker/AGENTS.md +10 -10
- package/examples/worker/README.md +4 -3
- package/examples/worker/src/connecta.config.ts +1 -0
- package/guides/connecting-agents.md +2 -1
- package/guides/how-it-works.md +4 -2
- package/package.json +1 -1
- package/templates/node/AGENTS.md +13 -12
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +1 -0
package/dist/catalog-service.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { registerAuthMethod } from "./auth/native.js";
|
|
2
|
+
import { META_ROUTES, routeToolInPlace } from "./agent-routes.js";
|
|
2
3
|
import { withAuthorizationHandoffEffect } from "./authorization-handoff.js";
|
|
3
4
|
import { carryCatalogFreshness, catalogIsFresh } from "./catalog-freshness.js";
|
|
4
5
|
import { retainedCatalogIdentity } from "./catalog-cache.js";
|
|
@@ -13,9 +14,24 @@ import { runEdge, withDeadlineEffect } from "./runtime/run.js";
|
|
|
13
14
|
import { registryProgram } from "./runtime/registry.js";
|
|
14
15
|
import { SharedRead } from "./runtime/shared-read.js";
|
|
15
16
|
import { duringViewEffect } from "./runtime/call.js";
|
|
16
|
-
import { DEFAULT_PROBE_TIMEOUT_MS, normalizeTimeoutMs } from "./timeout.js";
|
|
17
|
+
import { DEFAULT_PROBE_TIMEOUT_MS, normalizeTimeoutMs, UNSCOPED_DISCOVERY_BUDGET_MS, UNSCOPED_PROBE_TIMEOUT_MS, } from "./timeout.js";
|
|
18
|
+
import { claimDiscoveryProbe, readDiscoveryHealth, settleDiscoveryHealth, } from "./discovery-health.js";
|
|
17
19
|
import { keepErrorName } from "./redaction-identity.js";
|
|
18
|
-
import { operationVocabularyMatches } from "./operation-vocabulary.js";
|
|
20
|
+
import { operationVocabularyMatches, searchSynonyms } from "./operation-vocabulary.js";
|
|
21
|
+
/** The slice of an unscoped search's budget kept for recording what its probes saw. */
|
|
22
|
+
const SETTLE_RESERVE_MS = 500;
|
|
23
|
+
/** How long a probe claim outlives its request's discovery budget, for settlement and clock skew. */
|
|
24
|
+
const CLAIM_MARGIN_MS = 2_000;
|
|
25
|
+
/** A connector unscoped search skipped because its discovery failed recently. */
|
|
26
|
+
class DiscoverySkipped extends Error {
|
|
27
|
+
code;
|
|
28
|
+
retryAfterMs;
|
|
29
|
+
constructor(code, retryAfterMs) {
|
|
30
|
+
super("Connector discovery skipped after a recent failure");
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.retryAfterMs = retryAfterMs;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
19
35
|
export const DEFAULT_SEARCH_LIMIT = 8;
|
|
20
36
|
export const MAX_SEARCH_LIMIT = 100;
|
|
21
37
|
export const MAX_DESCRIBE_ADDRESSES = 100;
|
|
@@ -31,6 +47,8 @@ const MAX_IDENTITY_CONNECTORS = 3;
|
|
|
31
47
|
const MAX_DESCRIBE_SUGGESTIONS = 3;
|
|
32
48
|
const MAX_API_SEARCH_POINTERS = 3;
|
|
33
49
|
const MAX_API_SEARCH_ADDRESS_BYTES = 256;
|
|
50
|
+
/** Fully determined operation reads one search page offers: one per connector, three connectors. */
|
|
51
|
+
const MAX_READY_CALLS = 3;
|
|
34
52
|
/** Recognize the visible standard family without importing provider implementations or indexes. */
|
|
35
53
|
function apiSearchTools(tools) {
|
|
36
54
|
const names = new Set(tools.map((tool) => tool.name));
|
|
@@ -70,6 +88,13 @@ const SERVICE_NAMES = [
|
|
|
70
88
|
"Google Slides",
|
|
71
89
|
"Gmail",
|
|
72
90
|
];
|
|
91
|
+
/**
|
|
92
|
+
* Leading verbs that ask to read. A query led by one ranks read rows above
|
|
93
|
+
* write rows within each relevance tier; writes stay on the page.
|
|
94
|
+
*/
|
|
95
|
+
const READ_INTENT = /^(?:list|get|find|search|show|read|fetch|count)$/;
|
|
96
|
+
/** Name words that state an action, never a resource. */
|
|
97
|
+
const ACTION_WORD = /^(?:list|get|find|search|show|read|fetch|count|create|update|delete)$/;
|
|
73
98
|
function normalizedPhrase(text) {
|
|
74
99
|
return lexicalQueryTerms(text.toLowerCase()).join(" ");
|
|
75
100
|
}
|
|
@@ -238,15 +263,13 @@ function schemaKeyMetadata(input, output) {
|
|
|
238
263
|
};
|
|
239
264
|
}
|
|
240
265
|
const stringListSchema = { type: "array", items: { type: "string" } };
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
266
|
+
const STRING = { type: "string" };
|
|
267
|
+
// The reasons enum is stated once under `$defs` and referenced from each field.
|
|
268
|
+
const guideRequiredProperties = {
|
|
244
269
|
guideRequired: { const: true },
|
|
245
|
-
guideRequiredReasons: {
|
|
246
|
-
type: "array",
|
|
247
|
-
items: { enum: ["connector_required", "approval_required", "schema_truncated"] },
|
|
248
|
-
},
|
|
270
|
+
guideRequiredReasons: { $ref: "#/$defs/reasons" },
|
|
249
271
|
};
|
|
272
|
+
const guideProperties = { guide: STRING, guideSummary: STRING, ...guideRequiredProperties };
|
|
250
273
|
const catalogFailureProperties = {
|
|
251
274
|
code: { type: "string" },
|
|
252
275
|
message: { type: "string" },
|
|
@@ -292,26 +315,31 @@ const catalogFailureProperties = {
|
|
|
292
315
|
},
|
|
293
316
|
},
|
|
294
317
|
};
|
|
318
|
+
/** The one typed failure (and auth handoff) both failure fields reference. */
|
|
319
|
+
const FAILURE_REF = { $ref: "#/$defs/failure" };
|
|
295
320
|
/**
|
|
296
321
|
* The outputSchema contract for either search adapter. Guest API registration
|
|
297
322
|
* can reuse this value instead of maintaining a second page definition.
|
|
323
|
+
* Every failure field references one local `$defs` entry, so the typed auth
|
|
324
|
+
* handoff is advertised once instead of once per field.
|
|
298
325
|
*/
|
|
299
326
|
export const CATALOG_SEARCH_RESULT_SCHEMA = {
|
|
300
327
|
type: "object",
|
|
301
328
|
required: ["catalogErrors", "tools", "total", "offset", "limit", "hasMore"],
|
|
302
329
|
additionalProperties: false,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
properties: {
|
|
311
|
-
...catalogFailureProperties,
|
|
312
|
-
},
|
|
313
|
-
},
|
|
330
|
+
$defs: {
|
|
331
|
+
reasons: { type: "array", items: { enum: ["connector_required", "approval_required", "schema_truncated"] } },
|
|
332
|
+
failure: {
|
|
333
|
+
type: "object",
|
|
334
|
+
required: ["code", "message", "retryable"],
|
|
335
|
+
additionalProperties: false,
|
|
336
|
+
properties: catalogFailureProperties,
|
|
314
337
|
},
|
|
338
|
+
},
|
|
339
|
+
properties: {
|
|
340
|
+
// `allOf` rather than a `$ref` sibling keeps `required` enforced by
|
|
341
|
+
// validators that predate 2020-12 sibling semantics.
|
|
342
|
+
catalogErrors: { type: "array", items: { allOf: [FAILURE_REF], required: ["connector"] } },
|
|
315
343
|
absence: {
|
|
316
344
|
type: "object",
|
|
317
345
|
required: ["service", "message", "configuredConnectors"],
|
|
@@ -331,7 +359,6 @@ export const CATALOG_SEARCH_RESULT_SCHEMA = {
|
|
|
331
359
|
properties: {
|
|
332
360
|
name: { type: "string" },
|
|
333
361
|
address: { type: "string" },
|
|
334
|
-
connectorTitle: { type: "string" },
|
|
335
362
|
description: { type: "string" },
|
|
336
363
|
inputSchema: {},
|
|
337
364
|
outputSchema: {},
|
|
@@ -345,10 +372,33 @@ export const CATALOG_SEARCH_RESULT_SCHEMA = {
|
|
|
345
372
|
outputKeys: stringListSchema,
|
|
346
373
|
annotations: { type: "object" },
|
|
347
374
|
classification: { enum: ["read", "write"] },
|
|
348
|
-
...
|
|
375
|
+
...guideRequiredProperties,
|
|
349
376
|
},
|
|
350
377
|
},
|
|
351
378
|
},
|
|
379
|
+
readyCalls: {
|
|
380
|
+
type: "array",
|
|
381
|
+
maxItems: MAX_READY_CALLS,
|
|
382
|
+
items: {
|
|
383
|
+
type: "object",
|
|
384
|
+
required: ["address", "args", "classification", "summary"],
|
|
385
|
+
properties: {
|
|
386
|
+
address: STRING,
|
|
387
|
+
args: { type: "object", required: ["path"], additionalProperties: false, properties: { path: STRING } },
|
|
388
|
+
classification: { const: "read" },
|
|
389
|
+
summary: STRING,
|
|
390
|
+
...guideRequiredProperties,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
connectors: {
|
|
395
|
+
type: "object",
|
|
396
|
+
additionalProperties: {
|
|
397
|
+
type: "object",
|
|
398
|
+
additionalProperties: false,
|
|
399
|
+
properties: { title: STRING, guide: STRING, guideSummary: STRING },
|
|
400
|
+
},
|
|
401
|
+
},
|
|
352
402
|
total: { type: "integer", minimum: 0 },
|
|
353
403
|
offset: { type: "integer", minimum: 0 },
|
|
354
404
|
limit: { type: "integer", minimum: 1, maximum: MAX_SEARCH_LIMIT },
|
|
@@ -368,12 +418,7 @@ export const CATALOG_SEARCH_RESULT_SCHEMA = {
|
|
|
368
418
|
unknownConnector: { const: true },
|
|
369
419
|
configuredConnectors: stringListSchema,
|
|
370
420
|
unavailableConnectorCount: { type: "integer", minimum: 0 },
|
|
371
|
-
catalogError:
|
|
372
|
-
type: "object",
|
|
373
|
-
required: ["code", "message", "retryable"],
|
|
374
|
-
additionalProperties: false,
|
|
375
|
-
properties: catalogFailureProperties,
|
|
376
|
-
},
|
|
421
|
+
catalogError: FAILURE_REF,
|
|
377
422
|
...guideProperties,
|
|
378
423
|
guidance: { type: "string" },
|
|
379
424
|
},
|
|
@@ -397,6 +442,7 @@ export class CatalogService {
|
|
|
397
442
|
requestScope;
|
|
398
443
|
defer;
|
|
399
444
|
probeTimeoutMs;
|
|
445
|
+
discoveryBudgetMs;
|
|
400
446
|
concurrency;
|
|
401
447
|
searchRoute;
|
|
402
448
|
requestSignal;
|
|
@@ -421,12 +467,18 @@ export class CatalogService {
|
|
|
421
467
|
// gets new views and indexes. Each service belongs to one request and one
|
|
422
468
|
// registry visibility view (caller/pool, hidden tools and classification).
|
|
423
469
|
searchViews = new WeakMap();
|
|
470
|
+
// This request's discovery-health verdicts, read once per connector and kept
|
|
471
|
+
// current by its own probes, so repeated searches read no storage.
|
|
472
|
+
health = new Map();
|
|
473
|
+
routes;
|
|
424
474
|
constructor(registry, baseUrl, options = {}) {
|
|
425
475
|
this.registry = registry;
|
|
426
476
|
this.baseUrl = baseUrl;
|
|
427
477
|
void CatalogService.nativePrograms;
|
|
478
|
+
this.routes = options.routes ?? META_ROUTES;
|
|
428
479
|
this.requestScope = options.requestScope ?? {};
|
|
429
480
|
this.probeTimeoutMs = normalizeTimeoutMs(options.probeTimeoutMs) ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
481
|
+
this.discoveryBudgetMs = normalizeTimeoutMs(options.discoveryBudgetMs) ?? UNSCOPED_DISCOVERY_BUDGET_MS;
|
|
430
482
|
this.concurrency = resolveDiscoveryConcurrency(options.concurrency);
|
|
431
483
|
this.searchRoute = options.searchRoute ?? "search_tools";
|
|
432
484
|
this.requestSignal = options.requestSignal;
|
|
@@ -480,7 +532,16 @@ export class CatalogService {
|
|
|
480
532
|
signal,
|
|
481
533
|
timeoutMs: this.probeTimeoutMs,
|
|
482
534
|
...(this.defer ? { defer: this.defer } : {}),
|
|
483
|
-
}).pipe(Effect.map((tools) =>
|
|
535
|
+
}).pipe(Effect.map((tools) => {
|
|
536
|
+
// An authored catalog lists its declared descriptions for this request's surface.
|
|
537
|
+
const connector = this.registry.getConnector(id);
|
|
538
|
+
return carryCatalogFreshness(tools, tools.map((tool) => {
|
|
539
|
+
// The request's own copy, which keeps the source classification.
|
|
540
|
+
const copy = cloneTool(tool);
|
|
541
|
+
routeToolInPlace(connector, copy, this.routes);
|
|
542
|
+
return copy;
|
|
543
|
+
}));
|
|
544
|
+
})), (succeeded) => {
|
|
484
545
|
// Evicted before the read resumes anyone, so an asker that
|
|
485
546
|
// retries on hearing of the failure starts a fresh read.
|
|
486
547
|
if (!succeeded && this.catalogs.get(id)?.read === started) {
|
|
@@ -515,11 +576,117 @@ export class CatalogService {
|
|
|
515
576
|
// that asked for it. On a read-only endpoint a write is absent from search
|
|
516
577
|
// and describe, as if the connector never had it. Guides are not filtered.
|
|
517
578
|
discoveryCatalogs(ids, route, includeWrites = false) {
|
|
518
|
-
return Effect.forEach(ids, (id) => Effect.result(
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
579
|
+
return Effect.forEach(ids, (id) => Effect.result(this.probe(id, includeWrites, this.probeTimeoutMs, new ConnectorCallError("timeout", `${route} probe of "${id}" timed out after ${this.probeTimeoutMs}ms`))), { concurrency: this.concurrency });
|
|
580
|
+
}
|
|
581
|
+
// Search's catalogs. A scoped search probes its connector under the full
|
|
582
|
+
// probe deadline, reads its health record beside the probe, and settles it
|
|
583
|
+
// after. An unscoped search reads health first, skips a connector whose
|
|
584
|
+
// discovery failed recently, probes the rest under a shorter deadline, and
|
|
585
|
+
// settles each record as its probe ends. One discovery budget bounds all of
|
|
586
|
+
// it, health storage included: a probe that starts late gets only what
|
|
587
|
+
// remains less a slice kept for settling, and health I/O starts only within
|
|
588
|
+
// it, then runs to its end. A lapsed record is claimed as its probe is
|
|
589
|
+
// dispatched, leased to the end of this budget plus a margin.
|
|
590
|
+
// Caller cancellation and a probe the budget cut short leave health as it was.
|
|
591
|
+
searchCatalogs(ids, scoped) {
|
|
592
|
+
return Effect.gen({ self: this }, function* () {
|
|
593
|
+
const deadline = scoped ? this.probeTimeoutMs : Math.min(this.probeTimeoutMs, UNSCOPED_PROBE_TIMEOUT_MS);
|
|
594
|
+
const budgetEnd = scoped ? Infinity : Date.now() + this.discoveryBudgetMs;
|
|
595
|
+
const settleReserve = scoped ? 0 : Math.min(SETTLE_RESERVE_MS, Math.floor(this.discoveryBudgetMs / 10));
|
|
596
|
+
// Health storage starts only while budget remains and the caller is
|
|
597
|
+
// here; running out is not a failure, health is then unknown. Once
|
|
598
|
+
// started, an operation is awaited to its end, never raced or detached:
|
|
599
|
+
// a storage write cannot be withdrawn once sent (INV-7). The budget
|
|
600
|
+
// bounds when health work may start, not when it finishes.
|
|
601
|
+
const withinBudget = (io, unknown) => Effect.suspend(() => Date.now() >= budgetEnd || this.requestSignal?.aborted ? Effect.succeed(unknown) : Effect.uninterruptible(io));
|
|
602
|
+
// A skip that has lapsed is read again, so this request can claim the probe.
|
|
603
|
+
const unread = ids.filter((id) => {
|
|
604
|
+
const known = this.health.get(id);
|
|
605
|
+
return !known || (!scoped && known.skip !== undefined && known.skip.retryAt <= Date.now());
|
|
606
|
+
});
|
|
607
|
+
const readHealth = Effect.map(withinBudget(readDiscoveryHealth(this.registry, unread, this.baseUrl, this.requestScope, { skip: !scoped }), unread.map(() => ({}))), (read) => unread.forEach((id, index) => this.health.set(id, read[index])));
|
|
608
|
+
// Record what one probe saw, unless the caller left.
|
|
609
|
+
const settleOne = (outcome) => {
|
|
610
|
+
const slot = this.health.get(outcome.id)?.slot;
|
|
611
|
+
const { result, settle } = outcome;
|
|
612
|
+
if (!settle || !slot || this.requestSignal?.aborted)
|
|
613
|
+
return Effect.void;
|
|
614
|
+
return Effect.map(withinBudget(settleDiscoveryHealth(slot, Result.isSuccess(result)
|
|
615
|
+
? { ok: true }
|
|
616
|
+
: {
|
|
617
|
+
ok: false,
|
|
618
|
+
failure: result.failure,
|
|
619
|
+
timedOut: result.failure === settle.timeoutError,
|
|
620
|
+
elapsedMs: settle.elapsedMs,
|
|
621
|
+
}), undefined), (settled) => {
|
|
622
|
+
if (settled)
|
|
623
|
+
this.health.set(outcome.id, settled);
|
|
624
|
+
});
|
|
625
|
+
};
|
|
626
|
+
const probeOne = (id) => Effect.gen({ self: this }, function* () {
|
|
627
|
+
const verdict = scoped ? undefined : this.health.get(id);
|
|
628
|
+
const skip = verdict?.skip;
|
|
629
|
+
if (skip && skip.retryAt > Date.now())
|
|
630
|
+
return {
|
|
631
|
+
id,
|
|
632
|
+
result: Result.fail(new DiscoverySkipped(skip.code, Math.ceil(skip.retryAt - Date.now()))),
|
|
633
|
+
};
|
|
634
|
+
const timeoutFor = (remaining) => {
|
|
635
|
+
const budgetCut = remaining < deadline;
|
|
636
|
+
return {
|
|
637
|
+
budgetCut,
|
|
638
|
+
timeoutMs: budgetCut ? remaining : deadline,
|
|
639
|
+
timeoutError: budgetCut
|
|
640
|
+
? new ConnectorCallError("timeout", `${this.searchRoute} stopped probing "${id}" at the ${this.discoveryBudgetMs}ms discovery budget`)
|
|
641
|
+
: new ConnectorCallError("timeout", `${this.searchRoute} probe of "${id}" timed out after ${deadline}ms`),
|
|
642
|
+
};
|
|
643
|
+
};
|
|
644
|
+
let plan = timeoutFor(Math.ceil(budgetEnd - settleReserve - Date.now()));
|
|
645
|
+
if (plan.timeoutMs <= 0)
|
|
646
|
+
return { id, result: Result.fail(plan.timeoutError) };
|
|
647
|
+
// A lapsed record: claim this probe now, as it is dispatched. The
|
|
648
|
+
// lease runs to an absolute time, the end of this request's
|
|
649
|
+
// discovery budget plus a margin, so however long the claim write
|
|
650
|
+
// takes, the probe the budget still allows ends inside it.
|
|
651
|
+
if (skip && verdict?.slot) {
|
|
652
|
+
const leaseUntil = budgetEnd + CLAIM_MARGIN_MS;
|
|
653
|
+
const claimed = yield* withinBudget(claimDiscoveryProbe(verdict.slot, leaseUntil), false);
|
|
654
|
+
if (!claimed) {
|
|
655
|
+
this.health.set(id, { slot: verdict.slot, skip: { code: skip.code, retryAt: leaseUntil } });
|
|
656
|
+
return {
|
|
657
|
+
id,
|
|
658
|
+
result: Result.fail(new DiscoverySkipped(skip.code, Math.max(1, Math.ceil(leaseUntil - Date.now())))),
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
this.health.set(id, { slot: verdict.slot });
|
|
662
|
+
plan = timeoutFor(Math.ceil(budgetEnd - settleReserve - Date.now()));
|
|
663
|
+
if (plan.timeoutMs <= 0)
|
|
664
|
+
return { id, result: Result.fail(plan.timeoutError) };
|
|
665
|
+
}
|
|
666
|
+
const started = Date.now();
|
|
667
|
+
const result = yield* Effect.result(this.probe(id, false, plan.timeoutMs, plan.timeoutError));
|
|
668
|
+
// Elapsed time is this probe's own, measured as it settles.
|
|
669
|
+
const elapsedMs = Date.now() - started;
|
|
670
|
+
const outcome = plan.budgetCut && Result.isFailure(result) && result.failure === plan.timeoutError
|
|
671
|
+
? { id, result }
|
|
672
|
+
: { id, result, settle: { timeoutError: plan.timeoutError, elapsedMs } };
|
|
673
|
+
if (scoped)
|
|
674
|
+
return outcome;
|
|
675
|
+
yield* settleOne(outcome);
|
|
676
|
+
return { id, result };
|
|
677
|
+
});
|
|
678
|
+
if (!scoped) {
|
|
679
|
+
yield* readHealth;
|
|
680
|
+
const probed = yield* Effect.forEach(ids, probeOne, { concurrency: this.concurrency });
|
|
681
|
+
return probed.map((outcome) => outcome.result);
|
|
682
|
+
}
|
|
683
|
+
const [, probed] = yield* Effect.all([readHealth, Effect.forEach(ids, probeOne, { concurrency: this.concurrency })], { concurrency: 2 });
|
|
684
|
+
yield* Effect.forEach(probed, settleOne, { concurrency: "unbounded" });
|
|
685
|
+
return probed.map((outcome) => outcome.result);
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
probe(id, includeWrites, timeoutMs, timeoutError) {
|
|
689
|
+
return withDeadlineEffect((signal) => Effect.map(this.catalog(id, { signal }), (tools) => this.searchView(id, tools, includeWrites, "all")), { ...(this.requestSignal ? { signal: this.requestSignal } : {}), timeoutMs, timeoutError });
|
|
523
690
|
}
|
|
524
691
|
/**
|
|
525
692
|
* Every visible catalog as discovery shows it, for a native `tools/list`:
|
|
@@ -619,6 +786,15 @@ export class CatalogService {
|
|
|
619
786
|
}
|
|
620
787
|
catalogFailure(connector, failure) {
|
|
621
788
|
return Effect.gen({ self: this }, function* () {
|
|
789
|
+
if (failure instanceof DiscoverySkipped) {
|
|
790
|
+
return {
|
|
791
|
+
connector: connector.id,
|
|
792
|
+
code: failure.code,
|
|
793
|
+
message: `Connector "${connector.id}" was skipped because its catalog lookup failed recently (${failure.code}). Search with connector "${connector.id}" to try it now.`,
|
|
794
|
+
retryable: true,
|
|
795
|
+
retryAfterMs: failure.retryAfterMs,
|
|
796
|
+
};
|
|
797
|
+
}
|
|
622
798
|
const classified = classifyCallError(failure, "catalog_lookup_failed");
|
|
623
799
|
const error = {
|
|
624
800
|
connector: connector.id,
|
|
@@ -700,11 +876,16 @@ export class CatalogService {
|
|
|
700
876
|
: undefined;
|
|
701
877
|
const connectors = absence ? [] : args.connector ? (scopedConnector ? [scopedConnector] : []) : visibleConnectors;
|
|
702
878
|
// Timeout messages name the route the caller used.
|
|
703
|
-
const catalogs = yield* this.
|
|
879
|
+
const catalogs = yield* this.searchCatalogs(connectors.map((connector) => connector.id), args.connector !== undefined);
|
|
704
880
|
const searchableCatalogs = catalogs.map((catalog, index) => Result.map(catalog, (tools) => this.searchView(connectors[index].id, tools, true, safety)));
|
|
705
881
|
const matches = [];
|
|
706
882
|
let matchMode = "all";
|
|
707
|
-
|
|
883
|
+
// Provider-declared synonyms widen term matching within their own catalog only.
|
|
884
|
+
const synonymsFor = (tools) => {
|
|
885
|
+
const index = searchableCatalogs.findIndex((catalog) => Result.isSuccess(catalog) && catalog.success === tools);
|
|
886
|
+
return index < 0 ? undefined : searchSynonyms(connectors[index]);
|
|
887
|
+
};
|
|
888
|
+
const statistics = lexicalCorpusStatistics(searchableCatalogs.flatMap((catalog) => (Result.isSuccess(catalog) ? [catalog.success] : [])), retrievalQuery, synonymsFor);
|
|
708
889
|
const trimmedQuery = query.trim();
|
|
709
890
|
const isBrowse = trimmedQuery.length === 0;
|
|
710
891
|
const queryTerms = lexicalQueryTerms(retrievalQuery);
|
|
@@ -716,8 +897,10 @@ export class CatalogService {
|
|
|
716
897
|
const displayTerm = (term) => boundedQueryTerm(term).text;
|
|
717
898
|
const queryMetadataTruncated = analysisTerms.length > analyzedTerms.length || analyzedTerms.some((term) => boundedQueryTerm(term).truncated);
|
|
718
899
|
const vocabularyTools = new Set();
|
|
900
|
+
const readyFound = [];
|
|
719
901
|
const collectMatches = (mode) => {
|
|
720
902
|
const collected = [];
|
|
903
|
+
readyFound.length = 0;
|
|
721
904
|
let orderBase = 0;
|
|
722
905
|
searchableCatalogs.forEach((catalog, connectorIndex) => {
|
|
723
906
|
const connector = connectors[connectorIndex];
|
|
@@ -734,7 +917,7 @@ export class CatalogService {
|
|
|
734
917
|
: []),
|
|
735
918
|
]);
|
|
736
919
|
const localStatistics = exactConnector
|
|
737
|
-
? lexicalCorpusStatistics([catalog.success], retrievalQuery)
|
|
920
|
+
? lexicalCorpusStatistics([catalog.success], retrievalQuery, synonymsFor)
|
|
738
921
|
: undefined;
|
|
739
922
|
// An exact identity is a browse even when one tool repeats that
|
|
740
923
|
// identity in its name or description. Action queries still keep
|
|
@@ -754,7 +937,12 @@ export class CatalogService {
|
|
|
754
937
|
const rankedTools = rankTools(catalog.success, toolQuery, mode, statistics, query);
|
|
755
938
|
const original = catalogs[connectorIndex];
|
|
756
939
|
if (!isBrowse && query.length <= 200 && queryTerms.length <= 8 && Result.isSuccess(original)) {
|
|
757
|
-
|
|
940
|
+
const vocabulary = operationVocabularyMatches(connector, original.success, toolTerms.filter((term) => !connectorTerms.has(term)).join(" "), safety, this.readOnly);
|
|
941
|
+
// At most one ready call per connector: its first hatch's top operation.
|
|
942
|
+
const ready = vocabulary.ready[0];
|
|
943
|
+
if (ready && !readyFound.some((found) => found.connector === connector))
|
|
944
|
+
readyFound.push({ connector, ...ready });
|
|
945
|
+
for (const tool of vocabulary.tools) {
|
|
758
946
|
vocabularyTools.add(tool);
|
|
759
947
|
if (!rankedTools.some((match) => match.tool === tool))
|
|
760
948
|
rankedTools.push({
|
|
@@ -798,9 +986,14 @@ export class CatalogService {
|
|
|
798
986
|
if (!isBrowse && !unsearchableQuery && completeMatchCount === 0) {
|
|
799
987
|
matchMode = "partial";
|
|
800
988
|
}
|
|
989
|
+
// A read-intent verb leads the query once connector names are set aside.
|
|
990
|
+
const identityTerms = new Set(connectors.flatMap(connectorNames).flatMap(lexicalQueryTerms));
|
|
991
|
+
const readIntent = READ_INTENT.test(lexicalQueryTerms(query).find((term) => !identityTerms.has(term)) ?? "");
|
|
992
|
+
const readFirst = (match) => Number(readIntent && match.tool.classification === "read");
|
|
801
993
|
// Exact tool names precede connector identity and lexical scoring. A
|
|
802
994
|
// longer name such as get_issue_status cannot beat an exact get_issue
|
|
803
|
-
// merely because its description repeats rare query terms.
|
|
995
|
+
// merely because its description repeats rare query terms. Within a
|
|
996
|
+
// tier, a read-intent query puts reads before writes.
|
|
804
997
|
matches.sort((a, b) => {
|
|
805
998
|
const aFirstTier = a.complete || a.exactName;
|
|
806
999
|
const bFirstTier = b.complete || b.exactName;
|
|
@@ -808,6 +1001,7 @@ export class CatalogService {
|
|
|
808
1001
|
Number(b.exactName) - Number(a.exactName) ||
|
|
809
1002
|
b.connectorRank - a.connectorRank ||
|
|
810
1003
|
Number(bFirstTier) - Number(aFirstTier) ||
|
|
1004
|
+
readFirst(b) - readFirst(a) ||
|
|
811
1005
|
b.score - a.score ||
|
|
812
1006
|
a.order - b.order);
|
|
813
1007
|
});
|
|
@@ -883,6 +1077,40 @@ export class CatalogService {
|
|
|
883
1077
|
},
|
|
884
1078
|
};
|
|
885
1079
|
});
|
|
1080
|
+
// An exact name already answers the query, and so does a complete match
|
|
1081
|
+
// on an authored tool (not the hatch's own) whose name adds nothing to
|
|
1082
|
+
// the query: each name word is an action, or a query word up to
|
|
1083
|
+
// plural/singular and declared synonyms. `list_teams` answers "list
|
|
1084
|
+
// teams"; `list_project_env_vars` does not answer "list projects".
|
|
1085
|
+
const top = matches[0];
|
|
1086
|
+
const synonymsOfTop = top && searchSynonyms(top.connector);
|
|
1087
|
+
const asked = queryTerms.flatMap((term) => [term, ...(synonymsOfTop?.(term) ?? [])]);
|
|
1088
|
+
const answered = top !== undefined &&
|
|
1089
|
+
(top.exactLookup ||
|
|
1090
|
+
top.exactName ||
|
|
1091
|
+
(top.complete &&
|
|
1092
|
+
!vocabularyTools.has(top.tool) &&
|
|
1093
|
+
!readyFound.some((ready) => ready.tool === top.tool) &&
|
|
1094
|
+
lexicalQueryTerms(top.tool.name).every((word) => ACTION_WORD.test(word) || asked.some((term) => matchesLexicalTerm(word, term)))));
|
|
1095
|
+
const readyCalls = offset === 0 && !answered
|
|
1096
|
+
? readyFound.slice(0, MAX_READY_CALLS).map(({ connector, tool, path, summary }) => {
|
|
1097
|
+
const reasons = guideRequiredReasons(connector, tool, false);
|
|
1098
|
+
const guideSummary = connectorGuideSummary(connector);
|
|
1099
|
+
return {
|
|
1100
|
+
connector,
|
|
1101
|
+
call: {
|
|
1102
|
+
address: `${connector.id}.${tool.name}`,
|
|
1103
|
+
args: { path },
|
|
1104
|
+
classification: "read",
|
|
1105
|
+
summary,
|
|
1106
|
+
...(reasons ? { guideRequired: true, guideRequiredReasons: reasons } : {}),
|
|
1107
|
+
},
|
|
1108
|
+
...(connectorGuide(connector)
|
|
1109
|
+
? { guide: connectorSkillName(connector.id), ...(guideSummary ? { guideSummary } : {}) }
|
|
1110
|
+
: {}),
|
|
1111
|
+
};
|
|
1112
|
+
})
|
|
1113
|
+
: [];
|
|
886
1114
|
const nextOffset = offset + entries.length < matches.length ? offset + entries.length : undefined;
|
|
887
1115
|
const pageTools = new Set(pageMatches.map((match) => match.tool));
|
|
888
1116
|
const matchingTools = (term) => new Set([
|
|
@@ -1058,6 +1286,8 @@ export class CatalogService {
|
|
|
1058
1286
|
onCatalogReference?.(connector.id);
|
|
1059
1287
|
for (const entry of entries)
|
|
1060
1288
|
onCatalogReference?.(entry.connector.id, entry.tool.name);
|
|
1289
|
+
for (const { connector, call } of readyCalls)
|
|
1290
|
+
onCatalogReference?.(connector.id, call.address.slice(connector.id.length + 1));
|
|
1061
1291
|
for (const pointer of apiSearchPointers)
|
|
1062
1292
|
onCatalogReference?.(pointer.connector, pointer.tool);
|
|
1063
1293
|
for (const connector of absence?.configuredConnectors ?? [])
|
|
@@ -1070,6 +1300,7 @@ export class CatalogService {
|
|
|
1070
1300
|
catalogErrors,
|
|
1071
1301
|
...(absence ? { absence } : {}),
|
|
1072
1302
|
entries,
|
|
1303
|
+
...(readyCalls.length ? { readyCalls } : {}),
|
|
1073
1304
|
total: matches.length,
|
|
1074
1305
|
offset,
|
|
1075
1306
|
limit,
|
|
@@ -1264,17 +1495,23 @@ function pageTail(page) {
|
|
|
1264
1495
|
};
|
|
1265
1496
|
}
|
|
1266
1497
|
export function flatSearchResult(page) {
|
|
1498
|
+
// fromEntries keeps an id such as "__proto__" an own key.
|
|
1499
|
+
const connectors = new Map();
|
|
1500
|
+
for (const { connector, guide, guideSummary } of [...page.entries, ...(page.readyCalls ?? [])]) {
|
|
1501
|
+
const title = connector.title && boundedEchoText(connector.title.replace(/\s+/g, " ").trim(), 117);
|
|
1502
|
+
if (title || guide)
|
|
1503
|
+
connectors.set(connector.id, {
|
|
1504
|
+
...(title ? { title } : {}),
|
|
1505
|
+
...(guide ? { guide } : {}),
|
|
1506
|
+
...(guideSummary ? { guideSummary } : {}),
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1267
1509
|
return {
|
|
1268
1510
|
catalogErrors: page.catalogErrors,
|
|
1269
1511
|
...(page.absence ? { absence: page.absence } : {}),
|
|
1270
|
-
tools: page.entries.map((entry) =>
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
? { connectorTitle: boundedEchoText(entry.connector.title.replace(/\s+/g, " ").trim(), 117) }
|
|
1274
|
-
: {}),
|
|
1275
|
-
...(entry.guide ? { guide: entry.guide } : {}),
|
|
1276
|
-
...(entry.guideSummary ? { guideSummary: entry.guideSummary } : {}),
|
|
1277
|
-
})),
|
|
1512
|
+
tools: page.entries.map((entry) => entry.tool),
|
|
1513
|
+
...(page.readyCalls ? { readyCalls: page.readyCalls.map((ready) => ready.call) } : {}),
|
|
1514
|
+
...(connectors.size ? { connectors: Object.fromEntries(connectors) } : {}),
|
|
1278
1515
|
...pageTail(page),
|
|
1279
1516
|
};
|
|
1280
1517
|
}
|
package/dist/catalog.js
CHANGED
|
@@ -255,7 +255,23 @@ export function matchesLexicalTerm(text, term) {
|
|
|
255
255
|
* connector rank, so ubiquitous words contribute less than discriminative
|
|
256
256
|
* ones without making any action word a stopword.
|
|
257
257
|
*/
|
|
258
|
-
|
|
258
|
+
/** Whole-token candidates for one term, widened by a catalog's lexical synonyms. */
|
|
259
|
+
function synonymCandidates(term, synonyms) {
|
|
260
|
+
const own = matchingTokenCandidates(term);
|
|
261
|
+
if (!synonyms)
|
|
262
|
+
return own;
|
|
263
|
+
const candidates = new Set(own);
|
|
264
|
+
for (const candidate of own)
|
|
265
|
+
for (const synonym of synonyms(candidate))
|
|
266
|
+
for (const variant of matchingTokenCandidates(synonym))
|
|
267
|
+
candidates.add(variant);
|
|
268
|
+
return candidates;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* `synonymsFor` gives a catalog's lexical synonyms, word to equivalent words:
|
|
272
|
+
* a term then matches a tool through any of them, as plain whole tokens.
|
|
273
|
+
*/
|
|
274
|
+
export function lexicalCorpusStatistics(toolSets, query, synonymsFor) {
|
|
259
275
|
const terms = lexicalQueryTerms(query);
|
|
260
276
|
if (terms.length === 0) {
|
|
261
277
|
return {
|
|
@@ -272,7 +288,7 @@ export function lexicalCorpusStatistics(toolSets, query) {
|
|
|
272
288
|
const termDescriptionMatches = new Set();
|
|
273
289
|
for (const tools of toolSets) {
|
|
274
290
|
const index = indexFor(tools);
|
|
275
|
-
for (const candidate of
|
|
291
|
+
for (const candidate of synonymCandidates(term, synonymsFor?.(tools))) {
|
|
276
292
|
for (const tool of index.nameTokenDocuments.get(candidate) ?? []) {
|
|
277
293
|
termNameMatches.add(tool);
|
|
278
294
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -18,8 +18,10 @@ declare const discovery: {
|
|
|
18
18
|
/**
|
|
19
19
|
* Deadline (ms) for each downstream probe/catalog call fanned out by
|
|
20
20
|
* `search_tools` and by `connecta.search`/`connecta.describe` inside
|
|
21
|
-
* `execute_code`. Defaults to 30_000.
|
|
22
|
-
*
|
|
21
|
+
* `execute_code`. Defaults to 30_000. An unscoped search caps each probe at
|
|
22
|
+
* 5_000 and its whole fan-out at 9_000, and skips a connector whose
|
|
23
|
+
* discovery recently timed out for up to 60 seconds. A timed-out connector
|
|
24
|
+
* degrades independently; this does not apply to tool calls. Catalog walks receive the
|
|
23
25
|
* same cancellation signal, which aborts an in-flight page where supported and
|
|
24
26
|
* prevents another from starting.
|
|
25
27
|
*/
|
|
@@ -191,8 +193,9 @@ declare const pool: {
|
|
|
191
193
|
/**
|
|
192
194
|
* Who may open `/mcp/<name>`: principal ids in the namespace of the one
|
|
193
195
|
* interactive auth provider, `{ namespace, id }`, or `"agent:<name>"` for a
|
|
194
|
-
* declared agent.
|
|
195
|
-
* `tools` to these members and
|
|
196
|
+
* declared agent. All members must be people or all must be agents. Required
|
|
197
|
+
* and non-empty; the pool serves exactly its `tools` to these members and
|
|
198
|
+
* the same 404 to everyone else.
|
|
196
199
|
*/
|
|
197
200
|
members: import("./config-schema.js").Field<readonly MemberRef[], readonly MemberRef[], false>;
|
|
198
201
|
/**
|
|
@@ -200,8 +203,8 @@ declare const pool: {
|
|
|
200
203
|
* connector whose `subject` is a mapping function. Humans always act as
|
|
201
204
|
* themselves through that mapping, and a fixed `subject` address applies to
|
|
202
205
|
* everyone. Without an owner, a mapping-subject Workspace connector is
|
|
203
|
-
* absent for this pool's agents, as a personal connector is
|
|
204
|
-
* pool
|
|
206
|
+
* absent for this pool's agents, as a personal connector is. People use a
|
|
207
|
+
* separate pool and always act as themselves.
|
|
205
208
|
*/
|
|
206
209
|
owner: import("./config-schema.js").Field<string, string | undefined, true>;
|
|
207
210
|
/**
|
|
@@ -218,7 +221,7 @@ declare const pool: {
|
|
|
218
221
|
* `<connector>__<tool>` and annotated from its classification, for clients
|
|
219
222
|
* that run their own tool search; calls take the same invocation path.
|
|
220
223
|
*/
|
|
221
|
-
surface: import("./config-schema.js").Field<"
|
|
224
|
+
surface: import("./config-schema.js").Field<"meta" | "native", "meta" | "native" | undefined, true>;
|
|
222
225
|
/**
|
|
223
226
|
* `false` drops `execute_code` from a native pool and lists `read_result`
|
|
224
227
|
* for paging instead. Only valid with `surface: "native"`: the meta surface
|
|
@@ -254,7 +257,7 @@ declare const connectaConfig: {
|
|
|
254
257
|
readOnly?: boolean | undefined;
|
|
255
258
|
owner?: string | undefined;
|
|
256
259
|
description?: string | undefined;
|
|
257
|
-
surface?: "
|
|
260
|
+
surface?: "meta" | "native" | undefined;
|
|
258
261
|
executeCode?: boolean | undefined;
|
|
259
262
|
tools: readonly string[];
|
|
260
263
|
members: readonly MemberRef[];
|
|
@@ -264,7 +267,7 @@ declare const connectaConfig: {
|
|
|
264
267
|
readonly members: readonly MemberRef[];
|
|
265
268
|
readonly owner: string | undefined;
|
|
266
269
|
readonly description: string | undefined;
|
|
267
|
-
readonly surface: "
|
|
270
|
+
readonly surface: "meta" | "native" | undefined;
|
|
268
271
|
readonly executeCode: boolean | undefined;
|
|
269
272
|
}>> | undefined, true>;
|
|
270
273
|
/** Exact connector id -> tool name -> verdict. Hidden tools cannot be discovered or called.
|
|
@@ -326,8 +329,10 @@ declare const connectaConfig: {
|
|
|
326
329
|
/**
|
|
327
330
|
* Deadline (ms) for each downstream probe/catalog call fanned out by
|
|
328
331
|
* `search_tools` and by `connecta.search`/`connecta.describe` inside
|
|
329
|
-
* `execute_code`. Defaults to 30_000.
|
|
330
|
-
*
|
|
332
|
+
* `execute_code`. Defaults to 30_000. An unscoped search caps each probe at
|
|
333
|
+
* 5_000 and its whole fan-out at 9_000, and skips a connector whose
|
|
334
|
+
* discovery recently timed out for up to 60 seconds. A timed-out connector
|
|
335
|
+
* degrades independently; this does not apply to tool calls. Catalog walks receive the
|
|
331
336
|
* same cancellation signal, which aborts an in-flight page where supported and
|
|
332
337
|
* prevents another from starting.
|
|
333
338
|
*/
|