@zackbart/connecta 0.24.4 → 0.26.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/AGENTS.md +43 -12
- package/CHANGELOG.md +524 -0
- package/README.md +32 -15
- package/SECURITY.md +6 -5
- package/bin/connecta.mjs +5 -1
- package/dist/activity.d.ts +10 -3
- package/dist/activity.js +10 -1
- package/dist/advertised-schema.js +53 -0
- package/dist/artifacts/connector.d.ts +55 -0
- package/dist/artifacts/connector.js +724 -0
- package/dist/artifacts/document.js +76 -0
- package/dist/artifacts/guide.js +140 -0
- package/dist/artifacts/html-scan.js +329 -0
- package/dist/artifacts/json.js +68 -0
- package/dist/artifacts/kv-store.d.ts +20 -0
- package/dist/artifacts/kv-store.js +140 -0
- package/dist/artifacts/markdown.js +449 -0
- package/dist/artifacts/module.d.ts +37 -0
- package/dist/artifacts/module.js +60 -0
- package/dist/artifacts/operations.d.ts +195 -0
- package/dist/artifacts/operations.js +944 -0
- package/dist/artifacts/refresh.d.ts +53 -0
- package/dist/artifacts/refresh.js +235 -0
- package/dist/artifacts/routes.js +194 -0
- package/dist/artifacts/types.d.ts +214 -0
- package/dist/artifacts/types.js +22 -0
- package/dist/artifacts/validate.d.ts +62 -0
- package/dist/artifacts/validate.js +652 -0
- package/dist/artifacts.d.ts +6 -0
- package/dist/artifacts.js +9 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +152 -106
- package/dist/catalog.js +297 -38
- package/dist/concurrency.js +24 -23
- package/dist/connector-access.d.ts +18 -4
- package/dist/connector-access.js +65 -20
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +19 -0
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +2 -2
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +330 -240
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/errors.d.ts +16 -1
- package/dist/errors.js +28 -0
- package/dist/execute.js +745 -282
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +145 -63
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +255 -212
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +72 -7
- package/dist/index.js +281 -26
- package/dist/invocation.js +371 -286
- package/dist/meta-tools.js +213 -94
- package/dist/module-contracts.d.ts +21 -1
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +103 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +128 -3
- package/dist/program-source.js +321 -0
- package/dist/providers/notion.js +2 -2
- package/dist/providers/revenuecat.js +32 -5
- package/dist/providers/stripe.d.ts +3 -2
- package/dist/providers/vercel.js +1 -1
- package/dist/registry.d.ts +43 -7
- package/dist/registry.js +434 -288
- package/dist/resumable.js +1291 -0
- package/dist/routes/activity.js +74 -154
- package/dist/routes/actor-labels.js +114 -0
- package/dist/routes/credentials.js +196 -163
- package/dist/routes/mcp.js +228 -112
- package/dist/routes/oauth-management.js +73 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +83 -0
- package/dist/routes/shared.d.ts +25 -1
- package/dist/routes/shared.js +20 -2
- package/dist/routes/ui-data.js +211 -0
- package/dist/routes/ui.js +76 -35
- package/dist/run-journal.js +347 -0
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/shared-read.js +106 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +137 -89
- package/dist/skills.js +28 -9
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +39 -43
- package/dist/tool-safety.d.ts +27 -1
- package/dist/tool-safety.js +31 -0
- package/dist/types.d.ts +45 -7
- package/dist/ui.d.ts +25 -4
- package/dist/ui.js +77 -167
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +367 -16
- package/documentation/auth.md +163 -2
- package/documentation/code-mode.md +274 -56
- package/documentation/meta-tools.md +200 -44
- package/ethos.md +46 -47
- package/examples/worker/AGENTS.md +6 -0
- package/examples/worker/README.md +224 -14
- package/examples/worker/src/d1-activity-row.ts +14 -2
- package/examples/worker/src/d1-activity.ts +4 -2
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +42 -7
- package/examples/worker/src/r2-artifact-blobs.ts +29 -0
- package/examples/worker/wrangler.jsonc +21 -5
- package/package.json +16 -4
- package/templates/node/AGENTS.md +9 -2
- package/templates/node/README.md +31 -3
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +27 -0
- package/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -96
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -188
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
package/dist/catalog-service.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Effect, Result } from "effect";
|
|
2
|
+
import { compactDiscoverySchema, compactDescriptionSchema, lexicalCorpusStatistics, lexicalQueryTerms, lexicalSearchQuery, matchesLexicalTerm, rankTools, schemaObjectKeys, summarizeDiscoveryDescription, summarizeDescription, typescriptSignature, } from "./catalog.js";
|
|
3
|
+
import { resolveDiscoveryConcurrency } from "./concurrency.js";
|
|
3
4
|
import { boundedEchoText, classifyCallError, framingError, } from "./errors.js";
|
|
4
5
|
import { connectorGuide, connectorGuideRequired, connectorGuideSummary, connectorSkillName, } from "./skills.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { runEdge, withDeadlineEffect } from "./runtime/run.js";
|
|
7
|
+
import { SharedRead } from "./runtime/shared-read.js";
|
|
8
|
+
import { DEFAULT_PROBE_TIMEOUT_MS, normalizeTimeoutMs, } from "./timeout.js";
|
|
9
|
+
import { isApprovalExempt, isExplicitlyReadOnly, NO_EXEMPTIONS, } from "./tool-safety.js";
|
|
7
10
|
export const DEFAULT_SEARCH_LIMIT = 8;
|
|
8
11
|
export const MAX_SEARCH_LIMIT = 100;
|
|
9
12
|
export const MAX_DESCRIBE_ADDRESSES = 100;
|
|
@@ -199,16 +202,31 @@ export class CatalogService {
|
|
|
199
202
|
concurrency;
|
|
200
203
|
searchRoute;
|
|
201
204
|
readOptions;
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
requestSignal;
|
|
206
|
+
approval;
|
|
207
|
+
// The request-scoped catalog cache: one shared read per connector asked
|
|
208
|
+
// about, started by the first asker and joined by every later one. A
|
|
209
|
+
// success stays for the rest of the request, so a search and the call after
|
|
210
|
+
// it see one catalog; a failure, or a read cancelled because every asker
|
|
211
|
+
// left it, is dropped so the next ask reads again. The service lives and
|
|
212
|
+
// dies with one request, so no read here is ever awaited from another.
|
|
213
|
+
//
|
|
214
|
+
// Registry.getTools also joins concurrent reads in one request scope, which
|
|
215
|
+
// the operator UI and status reads rely on. This map overlaps it only for
|
|
216
|
+
// reads still in flight and is kept for what the registry's cannot do: hold
|
|
217
|
+
// the success for the rest of the request, recorded by the read itself
|
|
218
|
+
// rather than by an asker, whose probe deadline may have ended its wait.
|
|
219
|
+
catalogs = new Map();
|
|
204
220
|
constructor(registry, baseUrl, options = {}) {
|
|
205
221
|
this.registry = registry;
|
|
206
222
|
this.baseUrl = baseUrl;
|
|
223
|
+
this.approval = options.approval ?? NO_EXEMPTIONS;
|
|
207
224
|
this.requestScope = options.requestScope ?? {};
|
|
208
225
|
this.probeTimeoutMs =
|
|
209
226
|
normalizeTimeoutMs(options.probeTimeoutMs) ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
210
227
|
this.concurrency = resolveDiscoveryConcurrency(options.concurrency);
|
|
211
228
|
this.searchRoute = options.searchRoute ?? "search_tools";
|
|
229
|
+
this.requestSignal = options.requestSignal;
|
|
212
230
|
this.readOptions = options.defer
|
|
213
231
|
? {
|
|
214
232
|
defer: options.defer,
|
|
@@ -237,34 +255,44 @@ export class CatalogService {
|
|
|
237
255
|
? { function: "connecta.search", arguments: searchArgs, purpose }
|
|
238
256
|
: { tool: "search_tools", arguments: searchArgs, purpose };
|
|
239
257
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
258
|
+
loadConnector(id, callOptions = {}) {
|
|
259
|
+
return runEdge(this.catalog(id, callOptions));
|
|
260
|
+
}
|
|
261
|
+
// One connector's catalog from the request-scoped cache, reading it through
|
|
262
|
+
// the registry when this request has not yet. No asker's options govern the
|
|
263
|
+
// read: it carries its own signal and the catalog deadline, the probe
|
|
264
|
+
// timeout, whichever asker starts it. Each asker waits under its own signal
|
|
265
|
+
// and deadline, so a short-deadline call that starts the read and a search
|
|
266
|
+
// that joins it each end on their own terms, and the read is cancelled only
|
|
267
|
+
// once every asker has gone (#571).
|
|
268
|
+
catalog(id, callOptions) {
|
|
269
|
+
return Effect.suspend(() => {
|
|
270
|
+
let read = this.catalogs.get(id);
|
|
271
|
+
if (!read) {
|
|
272
|
+
const started = new SharedRead((signal) => this.registry.getTools(id, this.baseUrl, this.requestScope, { signal, timeoutMs: this.probeTimeoutMs }, this.readOptions), (succeeded) => {
|
|
273
|
+
// Evicted before the read resumes anyone, so an asker that
|
|
274
|
+
// retries on hearing of the failure starts a fresh read.
|
|
275
|
+
if (!succeeded && this.catalogs.get(id) === started) {
|
|
276
|
+
this.catalogs.delete(id);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
this.catalogs.set(id, started);
|
|
280
|
+
read = started;
|
|
281
|
+
}
|
|
282
|
+
return read.join(callOptions.signal);
|
|
256
283
|
});
|
|
257
|
-
this.loading.set(id, loading);
|
|
258
|
-
return loading;
|
|
259
284
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
285
|
+
// Every named catalog under its own probe deadline, `concurrency` at a time,
|
|
286
|
+
// each settling in its input slot: an unavailable catalog is a Failure
|
|
287
|
+
// there, not a failed discovery. `route` names the surface in a timeout.
|
|
288
|
+
// A probe also ends with its request, so no listing outlives the caller
|
|
289
|
+
// that asked for it.
|
|
290
|
+
discoveryCatalogs(ids, route) {
|
|
291
|
+
return Effect.forEach(ids, (id) => Effect.result(withDeadlineEffect((signal) => this.catalog(id, { signal }), {
|
|
292
|
+
...(this.requestSignal ? { signal: this.requestSignal } : {}),
|
|
265
293
|
timeoutMs: this.probeTimeoutMs,
|
|
266
|
-
timeoutError: new Error(`${
|
|
267
|
-
});
|
|
294
|
+
timeoutError: new Error(`${route} probe of "${id}" timed out after ${this.probeTimeoutMs}ms`),
|
|
295
|
+
})), { concurrency: this.concurrency });
|
|
268
296
|
}
|
|
269
297
|
outputSchema(connectorId, tool) {
|
|
270
298
|
if (tool.outputSchema)
|
|
@@ -306,32 +334,35 @@ export class CatalogService {
|
|
|
306
334
|
toolName,
|
|
307
335
|
};
|
|
308
336
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
337
|
+
resolveTool(address, callOptions = {}) {
|
|
338
|
+
return runEdge(this.resolve(address, callOptions));
|
|
339
|
+
}
|
|
340
|
+
// resolveTool without the Promise, for InvocationService's pipeline, which
|
|
341
|
+
// yields it inside the call's deadline rather than crossing an edge. Never
|
|
342
|
+
// fails: an unloadable catalog is a resolution like any other refusal.
|
|
343
|
+
resolve(address, callOptions = {}) {
|
|
344
|
+
return Effect.suspend(() => {
|
|
345
|
+
const resolved = this.registry.resolveAddress(address);
|
|
346
|
+
if (!resolved) {
|
|
347
|
+
return Effect.succeed(this.unknownAddressFailure(address, address));
|
|
348
|
+
}
|
|
349
|
+
const { connector, toolName } = resolved;
|
|
350
|
+
const started = Date.now();
|
|
351
|
+
return Effect.match(this.catalog(connector.id, callOptions), {
|
|
352
|
+
onFailure: (cause) => this.catalogLoadFailure(cause, started, connector, toolName),
|
|
353
|
+
onSuccess: (tools) => {
|
|
354
|
+
const definition = tools.find((tool) => tool.name === toolName);
|
|
355
|
+
if (!definition) {
|
|
356
|
+
return this.unknownToolFailure(toolName, connector, started);
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
ok: true,
|
|
360
|
+
resolved: { connector, toolName, definition },
|
|
361
|
+
catalogMs: Date.now() - started,
|
|
362
|
+
};
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
});
|
|
335
366
|
}
|
|
336
367
|
async search(args) {
|
|
337
368
|
if (args.query !== undefined && typeof args.query !== "string") {
|
|
@@ -361,21 +392,14 @@ export class CatalogService {
|
|
|
361
392
|
? [scopedConnector]
|
|
362
393
|
: []
|
|
363
394
|
: this.registry.listConnectors();
|
|
364
|
-
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
this.loadForDiscovery(connector.id, `search_tools probe of "${connector.id}"`));
|
|
370
|
-
const searchableCatalogs = catalogs.map((catalog) => catalog.status === "fulfilled"
|
|
371
|
-
? {
|
|
372
|
-
status: "fulfilled",
|
|
373
|
-
value: toolsForSafety(catalog.value, safety),
|
|
374
|
-
}
|
|
375
|
-
: catalog);
|
|
395
|
+
// Named for this service's route: a scoped search echoes a timed-out
|
|
396
|
+
// probe's message back as `queryAnalysis.catalogError`, and a program
|
|
397
|
+
// cannot call search_tools.
|
|
398
|
+
const catalogs = await runEdge(this.discoveryCatalogs(connectors.map((connector) => connector.id), this.searchRoute));
|
|
399
|
+
const searchableCatalogs = catalogs.map((catalog) => Result.map(catalog, (tools) => toolsForSafety(tools, safety)));
|
|
376
400
|
const matches = [];
|
|
377
401
|
let matchMode = "all";
|
|
378
|
-
const statistics = lexicalCorpusStatistics(searchableCatalogs.flatMap((catalog) => catalog
|
|
402
|
+
const statistics = lexicalCorpusStatistics(searchableCatalogs.flatMap((catalog) => Result.isSuccess(catalog) ? [catalog.success] : []), retrievalQuery);
|
|
379
403
|
const trimmedQuery = query.trim();
|
|
380
404
|
const isBrowse = trimmedQuery.length === 0;
|
|
381
405
|
const queryTerms = lexicalQueryTerms(retrievalQuery);
|
|
@@ -394,8 +418,8 @@ export class CatalogService {
|
|
|
394
418
|
if (!connector) {
|
|
395
419
|
throw new Error("Catalog result has no corresponding connector");
|
|
396
420
|
}
|
|
397
|
-
if (catalog
|
|
398
|
-
for (const ranked of rankTools(catalog.
|
|
421
|
+
if (Result.isSuccess(catalog)) {
|
|
422
|
+
for (const ranked of rankTools(catalog.success, retrievalQuery, mode, statistics, query)) {
|
|
399
423
|
collected.push({
|
|
400
424
|
connector,
|
|
401
425
|
tool: ranked.tool,
|
|
@@ -407,8 +431,7 @@ export class CatalogService {
|
|
|
407
431
|
});
|
|
408
432
|
}
|
|
409
433
|
}
|
|
410
|
-
orderBase +=
|
|
411
|
-
catalog.status === "fulfilled" ? catalog.value.length : 1;
|
|
434
|
+
orderBase += Result.isSuccess(catalog) ? catalog.success.length : 1;
|
|
412
435
|
});
|
|
413
436
|
return collected;
|
|
414
437
|
};
|
|
@@ -444,12 +467,23 @@ export class CatalogService {
|
|
|
444
467
|
? this.outputSchema(match.connector.id, match.tool)
|
|
445
468
|
: {};
|
|
446
469
|
const input = match.tool.inputSchema ?? { type: "object" };
|
|
447
|
-
const
|
|
448
|
-
?
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
470
|
+
const signature = withSchemas === "typescript"
|
|
471
|
+
? typescriptSignature(input, output.schema, {
|
|
472
|
+
observed: output.source === "observed",
|
|
473
|
+
description: false,
|
|
474
|
+
})
|
|
452
475
|
: undefined;
|
|
476
|
+
const schemaFormat = withSchemas === "typescript" ? undefined : withSchemas;
|
|
477
|
+
const renderedInput = signature
|
|
478
|
+
? { schema: undefined, truncated: signature.inputTruncated }
|
|
479
|
+
: schemaFormat
|
|
480
|
+
? renderSearchSchema(input, schemaFormat)
|
|
481
|
+
: undefined;
|
|
482
|
+
const renderedOutput = signature
|
|
483
|
+
? { schema: undefined, truncated: signature.outputTruncated }
|
|
484
|
+
: schemaFormat && output.schema
|
|
485
|
+
? renderSearchSchema(output.schema, schemaFormat)
|
|
486
|
+
: undefined;
|
|
453
487
|
const schemaKeys = withSchemas && args.includeSchemaKeys
|
|
454
488
|
? schemaKeyMetadata(input, output.schema)
|
|
455
489
|
: undefined;
|
|
@@ -468,15 +502,16 @@ export class CatalogService {
|
|
|
468
502
|
name: match.tool.name,
|
|
469
503
|
address: `${match.connector.id}.${match.tool.name}`,
|
|
470
504
|
...(description !== undefined ? { description } : {}),
|
|
471
|
-
...(
|
|
505
|
+
...(schemaFormat
|
|
472
506
|
? {
|
|
473
507
|
inputSchema: renderedInput?.schema,
|
|
474
508
|
}
|
|
475
509
|
: {}),
|
|
510
|
+
...(signature ? { signature: signature.text } : {}),
|
|
476
511
|
...(renderedInput?.truncated
|
|
477
512
|
? { inputSchemaTruncated: true }
|
|
478
513
|
: {}),
|
|
479
|
-
...(
|
|
514
|
+
...(schemaFormat && output.schema
|
|
480
515
|
? {
|
|
481
516
|
outputSchema: renderedOutput?.schema,
|
|
482
517
|
}
|
|
@@ -503,6 +538,11 @@ export class CatalogService {
|
|
|
503
538
|
...(match.tool.annotations
|
|
504
539
|
? { annotations: match.tool.annotations }
|
|
505
540
|
: {}),
|
|
541
|
+
// Config lets a program call this write without pausing (#566).
|
|
542
|
+
// A marker, not a class: the tool stays approval-required.
|
|
543
|
+
...(isApprovalExempt(this.approval, match.connector, match.tool.name, match.tool)
|
|
544
|
+
? { approval: "exempt" }
|
|
545
|
+
: {}),
|
|
506
546
|
...(requiredReasons
|
|
507
547
|
? {
|
|
508
548
|
guideRequired: true,
|
|
@@ -535,16 +575,19 @@ export class CatalogService {
|
|
|
535
575
|
unmatchedTerms.push(displayTerm(term));
|
|
536
576
|
}
|
|
537
577
|
}
|
|
538
|
-
const unavailableCatalogs = catalogs.filter(
|
|
578
|
+
const unavailableCatalogs = catalogs.filter(Result.isFailure).length;
|
|
539
579
|
// Named field by field rather than spread: `CallErrorDetails` also carries
|
|
540
580
|
// connector, operation, recovery, and nextAction, and a discovery read is
|
|
541
581
|
// not a call — widening the classifier must not silently widen what a
|
|
542
582
|
// catalog search hands back.
|
|
543
583
|
const scopedCatalogError = (() => {
|
|
544
|
-
|
|
584
|
+
const scopedCatalog = catalogs[0];
|
|
585
|
+
if (!scopedConnector ||
|
|
586
|
+
!scopedCatalog ||
|
|
587
|
+
Result.isSuccess(scopedCatalog)) {
|
|
545
588
|
return undefined;
|
|
546
589
|
}
|
|
547
|
-
const error = classifyCallError(
|
|
590
|
+
const error = classifyCallError(scopedCatalog.failure, "catalog_lookup_failed");
|
|
548
591
|
return {
|
|
549
592
|
code: error.code,
|
|
550
593
|
message: boundedEchoText(error.message),
|
|
@@ -717,19 +760,8 @@ export class CatalogService {
|
|
|
717
760
|
.map((entry) => entry.resolved?.connector.id)
|
|
718
761
|
.filter((id) => Boolean(id))),
|
|
719
762
|
];
|
|
720
|
-
const loaded = await
|
|
721
|
-
const catalogs = new Map();
|
|
722
|
-
loaded.forEach((result, index) => {
|
|
723
|
-
const connectorId = connectorIds[index];
|
|
724
|
-
if (connectorId === undefined) {
|
|
725
|
-
throw new Error("Catalog result has no corresponding connector id");
|
|
726
|
-
}
|
|
727
|
-
catalogs.set(connectorId, result.status === "fulfilled"
|
|
728
|
-
? result.value
|
|
729
|
-
: result.reason instanceof Error
|
|
730
|
-
? result.reason
|
|
731
|
-
: new Error(String(result.reason)));
|
|
732
|
-
});
|
|
763
|
+
const loaded = await runEdge(this.discoveryCatalogs(connectorIds, "connecta.describe"));
|
|
764
|
+
const catalogs = new Map(connectorIds.map((id, index) => [id, loaded[index]]));
|
|
733
765
|
return resolved.map(({ address, resolved: addressResolution }) => {
|
|
734
766
|
if (!addressResolution) {
|
|
735
767
|
const message = `Unknown address "${boundedEchoText(address)}"`;
|
|
@@ -743,8 +775,8 @@ export class CatalogService {
|
|
|
743
775
|
};
|
|
744
776
|
}
|
|
745
777
|
const catalog = catalogs.get(addressResolution.connector.id);
|
|
746
|
-
if (catalog
|
|
747
|
-
const classified = classifyCallError(catalog, "catalog_lookup_failed");
|
|
778
|
+
if (catalog && Result.isFailure(catalog)) {
|
|
779
|
+
const classified = classifyCallError(catalog.failure, "catalog_lookup_failed");
|
|
748
780
|
const message = boundedEchoText(classified.message);
|
|
749
781
|
return {
|
|
750
782
|
address: boundedEchoText(address),
|
|
@@ -759,10 +791,11 @@ export class CatalogService {
|
|
|
759
791
|
},
|
|
760
792
|
};
|
|
761
793
|
}
|
|
762
|
-
const
|
|
794
|
+
const tools = catalog?.success ?? [];
|
|
795
|
+
const tool = tools.find((item) => item.name === addressResolution.toolName);
|
|
763
796
|
if (!tool) {
|
|
764
797
|
const message = `Unknown tool "${boundedEchoText(addressResolution.toolName)}" on connector "${addressResolution.connector.id}"`;
|
|
765
|
-
const suggestions = describeSuggestions(addressResolution.connector.id, addressResolution.toolName,
|
|
798
|
+
const suggestions = describeSuggestions(addressResolution.connector.id, addressResolution.toolName, tools);
|
|
766
799
|
return {
|
|
767
800
|
address: boundedEchoText(address),
|
|
768
801
|
error: message,
|
|
@@ -779,11 +812,19 @@ export class CatalogService {
|
|
|
779
812
|
const input = tool.inputSchema ?? { type: "object" };
|
|
780
813
|
const output = this.outputSchema(addressResolution.connector.id, tool);
|
|
781
814
|
const description = summarizeDescription(tool.description, args.fullDescriptions === true);
|
|
815
|
+
const signature = format === "typescript"
|
|
816
|
+
? typescriptSignature(input, output.schema, {
|
|
817
|
+
observed: output.source === "observed",
|
|
818
|
+
description: true,
|
|
819
|
+
})
|
|
820
|
+
: undefined;
|
|
782
821
|
const compactInput = format === "compact"
|
|
783
822
|
? compactDescriptionSchema(input) : undefined;
|
|
784
823
|
const compactOutput = format === "compact" && output.schema
|
|
785
824
|
? compactDescriptionSchema(output.schema) : undefined;
|
|
786
|
-
const
|
|
825
|
+
const inputTruncated = compactInput?.truncated === true || signature?.inputTruncated === true;
|
|
826
|
+
const outputTruncated = compactOutput?.truncated === true || signature?.outputTruncated === true;
|
|
827
|
+
const requiredReasons = guideRequiredReasons(addressResolution.connector, tool, inputTruncated || outputTruncated);
|
|
787
828
|
const guideSummary = connectorGuideSummary(addressResolution.connector);
|
|
788
829
|
return {
|
|
789
830
|
address,
|
|
@@ -801,16 +842,21 @@ export class CatalogService {
|
|
|
801
842
|
guideRequiredReasons: requiredReasons,
|
|
802
843
|
}
|
|
803
844
|
: {}),
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
...(
|
|
845
|
+
...(signature
|
|
846
|
+
? { signature: signature.text }
|
|
847
|
+
: { inputSchema: compactInput?.text ?? input }),
|
|
848
|
+
...(inputTruncated ? { inputSchemaTruncated: true } : {}),
|
|
849
|
+
...(outputTruncated ? { outputSchemaTruncated: true } : {}),
|
|
850
|
+
...(output.schema && !signature
|
|
808
851
|
? {
|
|
809
852
|
outputSchema: compactOutput?.text ?? output.schema,
|
|
810
853
|
}
|
|
811
854
|
: {}),
|
|
812
855
|
...(output.source ? { outputSchemaSource: output.source } : {}),
|
|
813
856
|
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
857
|
+
...(isApprovalExempt(this.approval, addressResolution.connector, tool.name, tool)
|
|
858
|
+
? { approval: "exempt" }
|
|
859
|
+
: {}),
|
|
814
860
|
};
|
|
815
861
|
});
|
|
816
862
|
}
|