@zackbart/connecta 0.10.4 → 0.10.6
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 +135 -0
- package/dist/activity.d.ts +11 -1
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +44 -3
- package/dist/activity.js.map +1 -1
- package/dist/catalog-service.d.ts +40 -0
- package/dist/catalog-service.d.ts.map +1 -1
- package/dist/catalog-service.js +97 -15
- package/dist/catalog-service.js.map +1 -1
- package/dist/errors.d.ts +48 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +67 -0
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +72 -0
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +163 -10
- package/dist/execute.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/invocation.d.ts +9 -2
- package/dist/invocation.d.ts.map +1 -1
- package/dist/invocation.js +59 -29
- package/dist/invocation.js.map +1 -1
- package/dist/meta-tools.d.ts +12 -3
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +185 -30
- package/dist/meta-tools.js.map +1 -1
- package/dist/operator-ui/generated.d.ts +1 -1
- package/dist/operator-ui/generated.d.ts.map +1 -1
- package/dist/operator-ui/generated.js +1 -1
- package/dist/operator-ui/generated.js.map +1 -1
- package/dist/registry.d.ts +11 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +5 -2
- package/dist/registry.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +9 -0
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/shared.d.ts +4 -0
- package/dist/routes/shared.d.ts.map +1 -1
- package/dist/routes/shared.js.map +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/code-mode.md +125 -34
- package/documentation/meta-tools.md +91 -9
- package/documentation/rich-output-design.md +212 -0
- package/ethos.md +17 -19
- package/examples/worker/README.md +11 -3
- package/examples/worker/src/d1-activity-row.ts +40 -0
- package/examples/worker/src/d1-activity.ts +3 -2
- package/package.json +1 -1
- package/src/activity.ts +64 -3
- package/src/catalog-service.ts +166 -26
- package/src/errors.ts +102 -1
- package/src/execute.ts +240 -10
- package/src/index.ts +22 -0
- package/src/invocation.ts +59 -17
- package/src/meta-tools.ts +239 -37
- package/src/operator-ui/browser.ts +10 -2
- package/src/operator-ui/generated.ts +1 -1
- package/src/registry.ts +5 -2
- package/src/routes/mcp.ts +9 -0
- package/src/routes/shared.ts +4 -0
- package/src/skills.ts +1 -1
- package/src/version.ts +1 -1
- package/templates/node/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,141 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.10.6 — 2026-07-31
|
|
6
|
+
|
|
7
|
+
Programs gained a rich-output channel. `execute_code` code can now call
|
|
8
|
+
`connecta.emit(block)` to deliver text, image, and audio MCP content blocks
|
|
9
|
+
alongside its JSON return value — the piece code mode was missing for output
|
|
10
|
+
that cannot be projected, like a screenshot a downstream tool returned. The
|
|
11
|
+
channel is additive: a program that never emits produces the byte-for-byte
|
|
12
|
+
prior response, no executor changed to carry it, and deployments that do
|
|
13
|
+
nothing get sensible budgets. The design record is
|
|
14
|
+
`documentation/rich-output-design.md` (#267); the contract is `code-mode.md`'s
|
|
15
|
+
"Emitted output" clauses (#270).
|
|
16
|
+
|
|
17
|
+
Agent recovery is now executable data instead of prose at the remaining local
|
|
18
|
+
failure points. Address mistakes, approval reroutes, shortcut collisions, and
|
|
19
|
+
paged results identify their next call directly; activity classifies those
|
|
20
|
+
moments without retaining payloads. Connector behavior and approval authority
|
|
21
|
+
are unchanged.
|
|
22
|
+
|
|
23
|
+
Alongside both, three pieces of runtime text stop naming tools the receiving
|
|
24
|
+
surface does not serve — a routing failure connecta authored itself, and one the
|
|
25
|
+
always-loaded-text sweep missed because it only reads descriptions, not error
|
|
26
|
+
strings and tool results. All three fixes are agent-visible wording; no wire
|
|
27
|
+
shape, tool surface, or policy changes. Operators who tightened
|
|
28
|
+
`discovery.probeTimeoutMs` also get that deadline honored inside `execute_code`,
|
|
29
|
+
which had been probing at the 30-second default no matter what was configured.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **`connecta.emit(block)` inside `execute_code`.** Strictly validated
|
|
34
|
+
`text` / `image` / `audio` blocks, collected host-side and appended to the
|
|
35
|
+
result after the JSON envelope on success only; a failed program discards
|
|
36
|
+
them visibly (`emittedDiscarded`). Budgets fail loudly at the emit call and
|
|
37
|
+
spend no host-call budget.
|
|
38
|
+
- **`ConnectaConfig.execute.maxEmittedBytes` / `.maxEmittedBlocks`.** The
|
|
39
|
+
emission budgets, defaulting to 4,000,000 serialized bytes and 32 blocks per
|
|
40
|
+
run.
|
|
41
|
+
- **`diagnostics.emitted`.** With `diagnostics: true`, one payload-free
|
|
42
|
+
aggregate (block count and serialized bytes) when a program emitted.
|
|
43
|
+
- **Local routing failures carry structured recovery.** Unknown addresses and
|
|
44
|
+
tools suggest scoped discovery, read-path policy refusals preserve the
|
|
45
|
+
canonical address for `call_destructive_tool` — with the original arguments
|
|
46
|
+
when they fit a 512-byte echo budget, and an instruction to re-send them when
|
|
47
|
+
they do not — and ambiguous code-mode aliases list every canonical
|
|
48
|
+
`connecta.call` candidate. The suggested discovery route follows the caller's
|
|
49
|
+
own surface: `search_tools` for a top-level call, `connecta.search` with the
|
|
50
|
+
same arguments for a miss inside `execute_code`, which cannot call a tool.
|
|
51
|
+
The address gets the same 512-byte budget as the argument echo but the
|
|
52
|
+
opposite rule — clamped with a `…` marker rather than dropped, since the
|
|
53
|
+
address is the thing being corrected. Short addresses, which is all real
|
|
54
|
+
ones, come back exact.
|
|
55
|
+
- **Destructive calls accept explanatory context.** An optional `reason` of at
|
|
56
|
+
most 500 characters gives the MCP host human-readable intent without entering
|
|
57
|
+
downstream arguments or granting authority. An empty or whitespace-only one
|
|
58
|
+
is treated as absent rather than failing the call.
|
|
59
|
+
- **Activity exposes coarse agent friction.** Typed codes derive
|
|
60
|
+
`tool_not_found`, `schema_retry`, `destructive_reroute`, `auth_required`, or
|
|
61
|
+
`result_too_large`; no payload or raw error text is added.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **Paged results name the exact next call.** `call_tool` and `batch_call`
|
|
66
|
+
truncation notices include `get_result` arguments with the generated id and
|
|
67
|
+
byte offset zero. Program results and oversized discovery responses still
|
|
68
|
+
carry no `get_result` route: paging a program's return value is refused by
|
|
69
|
+
design, and a program can shrink anything.
|
|
70
|
+
- **`nextAction` is a wider union than it was.** `search_tools` routes may now
|
|
71
|
+
omit `arguments.connector` (an unknown *connector* cannot scope discovery to
|
|
72
|
+
itself), the ambiguous-alias route is keyed `function: "connecta.call"` with
|
|
73
|
+
no `tool` at all, and in-program discovery recovery is keyed
|
|
74
|
+
`function: "connecta.search"` carrying the same `{ query, connector?,
|
|
75
|
+
includeSchemas }` arguments the tool route does. A consumer that narrowed on
|
|
76
|
+
`nextAction.tool` must handle the function-keyed shapes too.
|
|
77
|
+
- **An address that resolves to nothing is now recorded.** A call to a
|
|
78
|
+
connector id that does not exist emits one activity event at the address as
|
|
79
|
+
written, with `unknown_address` and `tool_not_found` friction — provided the
|
|
80
|
+
address splits into the `<connectorId>.<toolName>` shape activity keeps; one
|
|
81
|
+
with no interior dot still records nothing. Previously the single most common
|
|
82
|
+
address mistake left no trace at all. Addresses were already a first-class
|
|
83
|
+
activity field; nothing new is retained. Because those fields now hold
|
|
84
|
+
caller-authored text, the recording seam clamps `connectorId` and `toolName`
|
|
85
|
+
at 128 UTF-8 bytes each and `address` at 257, marked with `…` — far past any
|
|
86
|
+
real id, far short of an invented 40 KB one.
|
|
87
|
+
- **A truncated result is friction, not an error.** An oversized result reports
|
|
88
|
+
`friction: "result_too_large"` on an `outcome: "success"` event and writes no
|
|
89
|
+
`errorCode`, so consumers counting error codes stop counting truncated
|
|
90
|
+
successes as failures. The Worker D1 example gains a `friction` column;
|
|
91
|
+
existing tables need `ALTER TABLE tool_call_activity ADD COLUMN friction
|
|
92
|
+
TEXT` before deploying it.
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
|
|
96
|
+
- **Discovery errors stay on their advertised surface.** The over-100-address
|
|
97
|
+
rejection and the catalog-probe timeout label now name `describe_tools` or
|
|
98
|
+
`connecta.describe` according to the route the caller actually took, so a
|
|
99
|
+
program is never told to split its list across a tool it cannot call. The
|
|
100
|
+
route is passed in explicitly rather than inferred from the deployment's
|
|
101
|
+
surface: a classic deployment with an executor serves `describe_tools` at top
|
|
102
|
+
level while every in-program describe still arrives through
|
|
103
|
+
`connecta.describe`.
|
|
104
|
+
- **The OAuth handoff points at a check the caller can run.** `authorize_connector`
|
|
105
|
+
is registered on both surfaces, but its success instructions told every agent
|
|
106
|
+
to "re-run `list_connectors`" — a tool the code-first surface folded away. A
|
|
107
|
+
code-first deployment is now told to retry the original call and confirm the
|
|
108
|
+
catalog loads with `connecta.search` inside `execute_code`; the classic
|
|
109
|
+
wording is unchanged.
|
|
110
|
+
- **`discovery.probeTimeoutMs` reaches code mode.** The sandbox's catalog
|
|
111
|
+
service received the deployment's discovery concurrency but not its probe
|
|
112
|
+
deadline, so an in-program `connecta.describe` against a hung connector waited
|
|
113
|
+
the 30-second default regardless of operator configuration.
|
|
114
|
+
|
|
115
|
+
## 0.10.5 — 2026-07-30
|
|
116
|
+
|
|
117
|
+
A consumer-audit recovery release. Schema discovery now provides the bounded
|
|
118
|
+
key contracts an agent needs before its first read, a single-address describe
|
|
119
|
+
no longer requires plural ceremony, and field-projection failures teach the
|
|
120
|
+
array syntax when that is the likely mistake. Discovery payload ceilings and
|
|
121
|
+
write admission are unchanged.
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
|
|
125
|
+
- **Schema search exposes usable key contracts on both surfaces.**
|
|
126
|
+
`search_tools` now includes `inputKeys`, `requiredInputKeys`, and `outputKeys`
|
|
127
|
+
alongside bounded plain-object schemas, matching code-mode discovery.
|
|
128
|
+
Truncated shapes omit their corresponding key list instead of repeating a
|
|
129
|
+
large partial inventory.
|
|
130
|
+
- **One address has a singular describe form.**
|
|
131
|
+
`connecta.describe({ address: "connector.tool" })` complements the bounded
|
|
132
|
+
plural form. Supplying both forms is rejected with a direct conflict error.
|
|
133
|
+
|
|
134
|
+
### Fixed
|
|
135
|
+
|
|
136
|
+
- **Projection misses teach array traversal when applicable.** Tool guidance
|
|
137
|
+
documents `results[].id`, and a plausible `results.id` miss gains a targeted
|
|
138
|
+
hint when the declared output schema confirms the array path.
|
|
139
|
+
|
|
5
140
|
## 0.10.4 — 2026-07-30
|
|
6
141
|
|
|
7
142
|
A small code-mode recovery release. The `execute_code` contract now shows the
|
package/dist/activity.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Logger } from "./types.js";
|
|
2
2
|
export type ActivityCallSource = "call_tool" | "call_destructive_tool" | "batch_call" | "execute_code";
|
|
3
3
|
export type ActivityOutcome = "success" | "error" | "timeout" | "cancelled";
|
|
4
|
+
export type AgentFriction = "tool_not_found" | "schema_retry" | "destructive_reroute" | "auth_required" | "result_too_large";
|
|
5
|
+
/** Coarse recovery class derived without inspecting payloads or error prose. */
|
|
6
|
+
export declare function agentFrictionForCode(code: string | undefined): AgentFriction | undefined;
|
|
4
7
|
/**
|
|
5
8
|
* Authenticated identity attached to an activity event. `id` is intentionally
|
|
6
9
|
* optional: open deployments and shared bearer tokens cannot honestly identify
|
|
@@ -36,7 +39,14 @@ export interface ToolCallActivityEvent {
|
|
|
36
39
|
outcome: ActivityOutcome;
|
|
37
40
|
durationMs: number;
|
|
38
41
|
attempts: number;
|
|
42
|
+
/** Set only when the call actually failed; a truncated success has none. */
|
|
39
43
|
errorCode?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Payload-free recovery class. Usually derived from `errorCode`, but it can
|
|
46
|
+
* also stand alone: a result too large to return inline is friction for the
|
|
47
|
+
* agent while remaining an `outcome: "success"` call with no error code.
|
|
48
|
+
*/
|
|
49
|
+
friction?: AgentFriction;
|
|
40
50
|
serverName: string;
|
|
41
51
|
serverVersion: string;
|
|
42
52
|
deploymentId?: string;
|
|
@@ -89,7 +99,7 @@ export interface ActivityRequestContext {
|
|
|
89
99
|
defer?: (promise: Promise<unknown>) => void;
|
|
90
100
|
logger: Logger;
|
|
91
101
|
}
|
|
92
|
-
export type ActivityEventInput = Pick<ToolCallActivityEvent, "connectorId" | "toolName" | "address" | "source" | "outcome" | "durationMs" | "attempts" | "errorCode">;
|
|
102
|
+
export type ActivityEventInput = Pick<ToolCallActivityEvent, "connectorId" | "toolName" | "address" | "source" | "outcome" | "durationMs" | "attempts" | "errorCode" | "friction">;
|
|
93
103
|
/**
|
|
94
104
|
* Best-effort by design: activity storage can never change a tool result.
|
|
95
105
|
* Workers attach async sinks to waitUntil; synchronous sinks such as Analytics
|
package/dist/activity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"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"}
|
package/dist/activity.js
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
import { boundedEchoText } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* How long an identity field may be before the store stops believing it.
|
|
4
|
+
*
|
|
5
|
+
* `connectorId` and `toolName` are ordinarily operator- and connector-authored,
|
|
6
|
+
* and 128 bytes is far past any real one. But an address that resolved to
|
|
7
|
+
* nothing is recorded *as written*, which puts a caller-authored string in both
|
|
8
|
+
* fields — and "payload-free by construction" has to mean the event type has
|
|
9
|
+
* nowhere to put a payload, not merely that connecta declines to. A 40 KB
|
|
10
|
+
* invented connector id is a payload wearing an id's clothing.
|
|
11
|
+
*
|
|
12
|
+
* Clamped rather than dropped: the invented id is precisely what an operator
|
|
13
|
+
* needs to see, and its first 128 bytes identify the mistake as well as all
|
|
14
|
+
* 40,000 would. The `…` marker keeps a clamped value from reading as a real one.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_ACTIVITY_NAME_BYTES = 128;
|
|
17
|
+
/** Two names and the dot between them. */
|
|
18
|
+
const MAX_ACTIVITY_ADDRESS_BYTES = MAX_ACTIVITY_NAME_BYTES * 2 + 1;
|
|
19
|
+
/** Coarse recovery class derived without inspecting payloads or error prose. */
|
|
20
|
+
export function agentFrictionForCode(code) {
|
|
21
|
+
switch (code) {
|
|
22
|
+
case "unknown_address":
|
|
23
|
+
case "unknown_tool":
|
|
24
|
+
case "ambiguous_tool_alias":
|
|
25
|
+
return "tool_not_found";
|
|
26
|
+
case "invalid_args":
|
|
27
|
+
return "schema_retry";
|
|
28
|
+
case "destructive_tool_requires_approval":
|
|
29
|
+
return "destructive_reroute";
|
|
30
|
+
case "auth_required":
|
|
31
|
+
return "auth_required";
|
|
32
|
+
case "result_too_large":
|
|
33
|
+
return "result_too_large";
|
|
34
|
+
default:
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
1
38
|
/** Reader implementations throw this for an opaque cursor they cannot decode. */
|
|
2
39
|
export class InvalidActivityCursorError extends Error {
|
|
3
40
|
name = "InvalidActivityCursorError";
|
|
@@ -13,20 +50,24 @@ export class InvalidActivityCursorError extends Error {
|
|
|
13
50
|
export function recordToolActivity(context, input) {
|
|
14
51
|
if (!context)
|
|
15
52
|
return;
|
|
53
|
+
// A caller-supplied class wins because it knows something the code table
|
|
54
|
+
// cannot: friction that belongs to a call which did not fail.
|
|
55
|
+
const friction = input.friction ?? agentFrictionForCode(input.errorCode);
|
|
16
56
|
const event = {
|
|
17
57
|
schemaVersion: 1,
|
|
18
58
|
id: crypto.randomUUID(),
|
|
19
59
|
occurredAt: new Date().toISOString(),
|
|
20
60
|
requestId: context.requestId,
|
|
21
61
|
actor: context.actor,
|
|
22
|
-
connectorId: input.connectorId,
|
|
23
|
-
toolName: input.toolName,
|
|
24
|
-
address: input.address,
|
|
62
|
+
connectorId: boundedEchoText(input.connectorId, MAX_ACTIVITY_NAME_BYTES),
|
|
63
|
+
toolName: boundedEchoText(input.toolName, MAX_ACTIVITY_NAME_BYTES),
|
|
64
|
+
address: boundedEchoText(input.address, MAX_ACTIVITY_ADDRESS_BYTES),
|
|
25
65
|
source: input.source,
|
|
26
66
|
outcome: input.outcome,
|
|
27
67
|
durationMs: Math.max(0, Math.trunc(input.durationMs)),
|
|
28
68
|
attempts: Math.max(1, Math.trunc(input.attempts)),
|
|
29
69
|
...(input.errorCode ? { errorCode: input.errorCode } : {}),
|
|
70
|
+
...(friction ? { friction } : {}),
|
|
30
71
|
serverName: context.serverInfo.name,
|
|
31
72
|
serverVersion: context.serverInfo.version,
|
|
32
73
|
...(context.deploymentId
|
package/dist/activity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,24 @@ export declare const DEFAULT_SEARCH_LIMIT = 8;
|
|
|
5
5
|
export declare const MAX_SEARCH_LIMIT = 100;
|
|
6
6
|
export declare const MAX_DESCRIBE_ADDRESSES = 100;
|
|
7
7
|
export declare const MAX_DISCOVERY_RESULT_BYTES = 256000;
|
|
8
|
+
/**
|
|
9
|
+
* The tool a describe-path error should name when it tells a caller to retry.
|
|
10
|
+
* This is the route the *caller* took, not the deployment's advertised surface:
|
|
11
|
+
* a classic deployment with an executor serves `describe_tools` at top level
|
|
12
|
+
* while every in-program describe still arrives through `connecta.describe`, so
|
|
13
|
+
* one CatalogService cannot infer the answer from `surface` alone. Callers pass
|
|
14
|
+
* the route they own.
|
|
15
|
+
*/
|
|
16
|
+
export type DescribeRoute = "describe_tools" | "connecta.describe";
|
|
17
|
+
/**
|
|
18
|
+
* The discovery route a routing failure should send a caller back through. Same
|
|
19
|
+
* rule as {@link DescribeRoute} — the route the *caller* took, not the
|
|
20
|
+
* deployment's advertised surface — with one difference worth keeping the two
|
|
21
|
+
* options separate for: `search_tools` exists on both advertised surfaces, so a
|
|
22
|
+
* top-level handler never has to derive this one, while an in-program caller
|
|
23
|
+
* still has to be told about `connecta.search` because it cannot call a tool.
|
|
24
|
+
*/
|
|
25
|
+
export type SearchRoute = "search_tools" | "connecta.search";
|
|
8
26
|
export declare class DiscoveryPolicyError extends Error {
|
|
9
27
|
readonly code: "invalid_args" | "result_too_large";
|
|
10
28
|
constructor(code: "invalid_args" | "result_too_large", message: string);
|
|
@@ -27,6 +45,7 @@ export interface CatalogSearchArgs {
|
|
|
27
45
|
includeSchemaKeys?: boolean;
|
|
28
46
|
}
|
|
29
47
|
export interface CatalogDescribeArgs {
|
|
48
|
+
address?: unknown;
|
|
30
49
|
addresses?: unknown;
|
|
31
50
|
format?: "compact" | "json";
|
|
32
51
|
fullDescriptions?: boolean;
|
|
@@ -105,13 +124,34 @@ export declare class CatalogService {
|
|
|
105
124
|
readonly requestScope: object;
|
|
106
125
|
private readonly probeTimeoutMs;
|
|
107
126
|
private readonly concurrency;
|
|
127
|
+
private readonly describeRoute;
|
|
128
|
+
private readonly searchRoute;
|
|
108
129
|
private readonly loaded;
|
|
109
130
|
private readonly loading;
|
|
110
131
|
constructor(registry: RegistryView, baseUrl: string, options?: {
|
|
111
132
|
requestScope?: object;
|
|
112
133
|
probeTimeoutMs?: number;
|
|
113
134
|
concurrency?: number;
|
|
135
|
+
/** The tool describe-path errors name. Default `describe_tools`. */
|
|
136
|
+
describeRoute?: DescribeRoute;
|
|
137
|
+
/** The discovery route recovery records name. Default `search_tools`. */
|
|
138
|
+
searchRoute?: SearchRoute;
|
|
114
139
|
});
|
|
140
|
+
/**
|
|
141
|
+
* Send a caller back to discovery through the surface it can actually reach.
|
|
142
|
+
* Both variants carry the same scoping arguments because `connecta.search`
|
|
143
|
+
* takes the same ones `search_tools` does; only the key naming the callable
|
|
144
|
+
* differs, the way the ambiguous-alias record already names a function.
|
|
145
|
+
*
|
|
146
|
+
* Not private: `InvocationService` builds the same class of record when a
|
|
147
|
+
* call fails schema validation, and it is this catalog's route that decides
|
|
148
|
+
* which key that record carries. Duplicating the branch there would let the
|
|
149
|
+
* two drift.
|
|
150
|
+
*/
|
|
151
|
+
searchRecovery(args: {
|
|
152
|
+
query: string;
|
|
153
|
+
connector?: string;
|
|
154
|
+
}, purpose: string): NonNullable<CallErrorDetails["nextAction"]>;
|
|
115
155
|
loadConnector(id: string, callOptions?: ConnectorOperationOptions): Promise<ToolDef[]>;
|
|
116
156
|
private loadForDiscovery;
|
|
117
157
|
resolveTool(address: string, callOptions?: ConnectorOperationOptions): Promise<CatalogResolution>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-service.d.ts","sourceRoot":"","sources":["../src/catalog-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"catalog-service.d.ts","sourceRoot":"","sources":["../src/catalog-service.ts"],"names":[],"mappings":"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"}
|
package/dist/catalog-service.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { compactDiscoverySchema, compactSchema, lexicalCorpusStatistics, lexicalQueryTerms, lexicalSearchQuery, rankTools, schemaObjectKeys, summarizeDiscoveryDescription, summarizeDescription, } from "./catalog.js";
|
|
2
2
|
import { mapSettledWithConcurrency, resolveDiscoveryConcurrency, } from "./concurrency.js";
|
|
3
|
-
import { classifyCallError, framingError } from "./errors.js";
|
|
3
|
+
import { boundedEchoText, classifyCallError, framingError, } from "./errors.js";
|
|
4
4
|
import { connectorGuide, connectorSkillName, } from "./skills.js";
|
|
5
5
|
import { DEFAULT_PROBE_TIMEOUT_MS, normalizeTimeoutMs, withAbortableTimeout, } from "./timeout.js";
|
|
6
6
|
import { isExplicitlyReadOnly } from "./tool-safety.js";
|
|
@@ -31,16 +31,35 @@ function discoverySearchLimit(value) {
|
|
|
31
31
|
}
|
|
32
32
|
return value;
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
35
|
-
function discoveryAddresses(
|
|
34
|
+
/** Normalize the single-address convenience form, then validate the bounded list. */
|
|
35
|
+
function discoveryAddresses(args, describeRoute) {
|
|
36
|
+
if (args.address !== undefined && args.addresses !== undefined) {
|
|
37
|
+
throw new DiscoveryPolicyError("invalid_args", "describe takes either address or addresses, not both.");
|
|
38
|
+
}
|
|
39
|
+
const value = args.address !== undefined
|
|
40
|
+
? typeof args.address === "string"
|
|
41
|
+
? [args.address]
|
|
42
|
+
: undefined
|
|
43
|
+
: args.addresses;
|
|
36
44
|
if (!Array.isArray(value)) {
|
|
37
|
-
throw new DiscoveryPolicyError("invalid_args", 'describe takes { addresses: ["<connectorId>.<toolName>", ...] }
|
|
45
|
+
throw new DiscoveryPolicyError("invalid_args", 'describe takes { address: "<connectorId>.<toolName>" } or { addresses: ["<connectorId>.<toolName>", ...] }.');
|
|
38
46
|
}
|
|
39
47
|
if (value.length > MAX_DESCRIBE_ADDRESSES) {
|
|
40
|
-
throw new DiscoveryPolicyError("invalid_args", `addresses must contain at most ${MAX_DESCRIBE_ADDRESSES} entries. Split a larger list across
|
|
48
|
+
throw new DiscoveryPolicyError("invalid_args", `addresses must contain at most ${MAX_DESCRIBE_ADDRESSES} entries. Split a larger list across ${describeRoute} calls.`);
|
|
41
49
|
}
|
|
42
50
|
return value;
|
|
43
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Search terms derived from an address the catalog could not resolve. Bounded
|
|
54
|
+
* because the address is entirely caller-authored: an invented one can be any
|
|
55
|
+
* length, and this string is copied into a recovery record that is itself
|
|
56
|
+
* copied into both halves of the result envelope.
|
|
57
|
+
*/
|
|
58
|
+
function recoveryQuery(address) {
|
|
59
|
+
const separator = address.indexOf(".");
|
|
60
|
+
const candidate = separator >= 0 ? address.slice(separator + 1) : address;
|
|
61
|
+
return boundedEchoText(candidate.replaceAll(/[._-]+/g, " ").trim() || address);
|
|
62
|
+
}
|
|
44
63
|
/** Serialize once and count the exact bytes the MCP adapter would emit. */
|
|
45
64
|
export function boundedDiscoveryText(value, hint) {
|
|
46
65
|
const text = JSON.stringify(value);
|
|
@@ -108,6 +127,8 @@ export class CatalogService {
|
|
|
108
127
|
requestScope;
|
|
109
128
|
probeTimeoutMs;
|
|
110
129
|
concurrency;
|
|
130
|
+
describeRoute;
|
|
131
|
+
searchRoute;
|
|
111
132
|
loaded = new Map();
|
|
112
133
|
loading = new Map();
|
|
113
134
|
constructor(registry, baseUrl, options = {}) {
|
|
@@ -117,6 +138,29 @@ export class CatalogService {
|
|
|
117
138
|
this.probeTimeoutMs =
|
|
118
139
|
normalizeTimeoutMs(options.probeTimeoutMs) ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
119
140
|
this.concurrency = resolveDiscoveryConcurrency(options.concurrency);
|
|
141
|
+
this.describeRoute = options.describeRoute ?? "describe_tools";
|
|
142
|
+
this.searchRoute = options.searchRoute ?? "search_tools";
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Send a caller back to discovery through the surface it can actually reach.
|
|
146
|
+
* Both variants carry the same scoping arguments because `connecta.search`
|
|
147
|
+
* takes the same ones `search_tools` does; only the key naming the callable
|
|
148
|
+
* differs, the way the ambiguous-alias record already names a function.
|
|
149
|
+
*
|
|
150
|
+
* Not private: `InvocationService` builds the same class of record when a
|
|
151
|
+
* call fails schema validation, and it is this catalog's route that decides
|
|
152
|
+
* which key that record carries. Duplicating the branch there would let the
|
|
153
|
+
* two drift.
|
|
154
|
+
*/
|
|
155
|
+
searchRecovery(args, purpose) {
|
|
156
|
+
const searchArgs = {
|
|
157
|
+
query: args.query,
|
|
158
|
+
...(args.connector !== undefined ? { connector: args.connector } : {}),
|
|
159
|
+
includeSchemas: "compact",
|
|
160
|
+
};
|
|
161
|
+
return this.searchRoute === "connecta.search"
|
|
162
|
+
? { function: "connecta.search", arguments: searchArgs, purpose }
|
|
163
|
+
: { tool: "search_tools", arguments: searchArgs, purpose };
|
|
120
164
|
}
|
|
121
165
|
async loadConnector(id, callOptions = {}) {
|
|
122
166
|
const cached = this.loaded.get(id);
|
|
@@ -149,7 +193,10 @@ export class CatalogService {
|
|
|
149
193
|
if (!resolved) {
|
|
150
194
|
return {
|
|
151
195
|
ok: false,
|
|
152
|
-
error:
|
|
196
|
+
error: {
|
|
197
|
+
...framingError("unknown_address", `Unknown address "${boundedEchoText(address)}"`),
|
|
198
|
+
nextAction: this.searchRecovery({ query: recoveryQuery(address) }, "Find the configured canonical address before retrying."),
|
|
199
|
+
},
|
|
153
200
|
catalogMs: 0,
|
|
154
201
|
};
|
|
155
202
|
}
|
|
@@ -172,7 +219,13 @@ export class CatalogService {
|
|
|
172
219
|
if (!definition) {
|
|
173
220
|
return {
|
|
174
221
|
ok: false,
|
|
175
|
-
error:
|
|
222
|
+
error: {
|
|
223
|
+
...framingError("unknown_tool", `Unknown tool "${boundedEchoText(resolved.toolName)}" on connector "${resolved.connector.id}"`),
|
|
224
|
+
nextAction: this.searchRecovery({
|
|
225
|
+
query: recoveryQuery(resolved.toolName),
|
|
226
|
+
connector: resolved.connector.id,
|
|
227
|
+
}, "Find the connector's current canonical tool address."),
|
|
228
|
+
},
|
|
176
229
|
catalogMs: Date.now() - started,
|
|
177
230
|
connector: resolved.connector,
|
|
178
231
|
toolName: resolved.toolName,
|
|
@@ -198,7 +251,10 @@ export class CatalogService {
|
|
|
198
251
|
if (!connector) {
|
|
199
252
|
return {
|
|
200
253
|
ok: false,
|
|
201
|
-
error:
|
|
254
|
+
error: {
|
|
255
|
+
...framingError("unknown_address", `Unknown address "${boundedEchoText(`${connectorId}.${alias}`)}"`),
|
|
256
|
+
nextAction: this.searchRecovery({ query: recoveryQuery(alias) }, "Find the configured canonical address before retrying."),
|
|
257
|
+
},
|
|
202
258
|
catalogMs: 0,
|
|
203
259
|
};
|
|
204
260
|
}
|
|
@@ -221,7 +277,10 @@ export class CatalogService {
|
|
|
221
277
|
if (!definition) {
|
|
222
278
|
return {
|
|
223
279
|
ok: false,
|
|
224
|
-
error:
|
|
280
|
+
error: {
|
|
281
|
+
...framingError("unknown_tool", `Unknown tool "${boundedEchoText(alias)}" on connector "${connector.id}"`),
|
|
282
|
+
nextAction: this.searchRecovery({ query: recoveryQuery(alias), connector: connector.id }, "Find the connector's current canonical tool address."),
|
|
283
|
+
},
|
|
225
284
|
catalogMs: Date.now() - started,
|
|
226
285
|
connector,
|
|
227
286
|
toolName: alias,
|
|
@@ -235,8 +294,13 @@ export class CatalogService {
|
|
|
235
294
|
ok: false,
|
|
236
295
|
error: {
|
|
237
296
|
code: "ambiguous_tool_alias",
|
|
238
|
-
message: `Tool alias "${alias}" is ambiguous on connector "${connector.id}" because ${names} sanitize to the same name. Use connecta.call with an exact address.`,
|
|
297
|
+
message: `Tool alias "${boundedEchoText(alias)}" is ambiguous on connector "${connector.id}" because ${names} sanitize to the same name. Use connecta.call with an exact address.`,
|
|
239
298
|
retryable: false,
|
|
299
|
+
nextAction: {
|
|
300
|
+
function: "connecta.call",
|
|
301
|
+
addresses: [definition, ...collisions].map((tool) => `${connector.id}.${tool.name}`),
|
|
302
|
+
purpose: "Choose the intended canonical address and call it with the original arguments.",
|
|
303
|
+
},
|
|
240
304
|
},
|
|
241
305
|
catalogMs: Date.now() - started,
|
|
242
306
|
connector,
|
|
@@ -267,7 +331,12 @@ export class CatalogService {
|
|
|
267
331
|
? [scopedConnector]
|
|
268
332
|
: []
|
|
269
333
|
: this.registry.listConnectors();
|
|
270
|
-
const catalogs = await mapSettledWithConcurrency(connectors, this.concurrency, (connector) =>
|
|
334
|
+
const catalogs = await mapSettledWithConcurrency(connectors, this.concurrency, (connector) =>
|
|
335
|
+
// Unlike the describe path, this label never reaches a caller: search
|
|
336
|
+
// only counts rejected catalogs (`unavailableCatalogs` below) and
|
|
337
|
+
// renders its own guidance, so the folded name here stays internal and
|
|
338
|
+
// needs no surface awareness.
|
|
339
|
+
this.loadForDiscovery(connector.id, `search_tools probe of "${connector.id}"`));
|
|
271
340
|
const searchableCatalogs = catalogs.map((catalog) => catalog.status === "fulfilled"
|
|
272
341
|
? {
|
|
273
342
|
status: "fulfilled",
|
|
@@ -314,6 +383,9 @@ export class CatalogService {
|
|
|
314
383
|
const renderedOutput = args.includeSchemas && match.tool.outputSchema
|
|
315
384
|
? renderSearchSchema(match.tool.outputSchema, args.includeSchemas)
|
|
316
385
|
: undefined;
|
|
386
|
+
const schemaKeys = args.includeSchemas && args.includeSchemaKeys
|
|
387
|
+
? schemaKeyMetadata(input, match.tool.outputSchema)
|
|
388
|
+
: undefined;
|
|
317
389
|
const description = summarizeDiscoveryDescription(match.tool.description, args.fullDescriptions === true);
|
|
318
390
|
return {
|
|
319
391
|
connector: match.connector,
|
|
@@ -340,8 +412,18 @@ export class CatalogService {
|
|
|
340
412
|
...(renderedOutput?.truncated
|
|
341
413
|
? { outputSchemaTruncated: true }
|
|
342
414
|
: {}),
|
|
343
|
-
...(
|
|
344
|
-
?
|
|
415
|
+
...(schemaKeys && !renderedInput?.truncated
|
|
416
|
+
? {
|
|
417
|
+
...(schemaKeys.inputKeys
|
|
418
|
+
? { inputKeys: schemaKeys.inputKeys }
|
|
419
|
+
: {}),
|
|
420
|
+
...(schemaKeys.requiredInputKeys
|
|
421
|
+
? { requiredInputKeys: schemaKeys.requiredInputKeys }
|
|
422
|
+
: {}),
|
|
423
|
+
}
|
|
424
|
+
: {}),
|
|
425
|
+
...(schemaKeys?.outputKeys && !renderedOutput?.truncated
|
|
426
|
+
? { outputKeys: schemaKeys.outputKeys }
|
|
345
427
|
: {}),
|
|
346
428
|
...(match.tool.annotations
|
|
347
429
|
? { annotations: match.tool.annotations }
|
|
@@ -437,7 +519,7 @@ export class CatalogService {
|
|
|
437
519
|
};
|
|
438
520
|
}
|
|
439
521
|
async describe(args) {
|
|
440
|
-
const addresses = discoveryAddresses(args.
|
|
522
|
+
const addresses = discoveryAddresses(args, this.describeRoute);
|
|
441
523
|
const format = args.format ?? "compact";
|
|
442
524
|
const resolved = addresses.map((rawAddress) => {
|
|
443
525
|
const address = String(rawAddress);
|
|
@@ -448,7 +530,7 @@ export class CatalogService {
|
|
|
448
530
|
.map((entry) => entry.resolved?.connector.id)
|
|
449
531
|
.filter((id) => Boolean(id))),
|
|
450
532
|
];
|
|
451
|
-
const loaded = await mapSettledWithConcurrency(connectorIds, this.concurrency, (id) => this.loadForDiscovery(id,
|
|
533
|
+
const loaded = await mapSettledWithConcurrency(connectorIds, this.concurrency, (id) => this.loadForDiscovery(id, `${this.describeRoute} probe of "${id}"`));
|
|
452
534
|
const catalogs = new Map();
|
|
453
535
|
loaded.forEach((result, index) => {
|
|
454
536
|
const connectorId = connectorIds[index];
|