@zackbart/connecta 0.10.5 → 0.11.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 +8 -6
- package/CHANGELOG.md +150 -0
- package/README.md +5 -4
- package/bin/connecta.mjs +0 -7
- 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 +24 -0
- package/dist/catalog-service.d.ts.map +1 -1
- package/dist/catalog-service.js +68 -9
- package/dist/catalog-service.js.map +1 -1
- package/dist/connectors/api.d.ts +2 -2
- package/dist/connectors/remote-mcp.d.ts +1 -1
- package/dist/errors.d.ts +49 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +68 -1
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +73 -3
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +161 -29
- package/dist/execute.js.map +1 -1
- package/dist/index.d.ts +28 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -37
- 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 +61 -31
- package/dist/invocation.js.map +1 -1
- package/dist/meta-tools.d.ts +24 -59
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +107 -359
- 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 +12 -10
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +8 -17
- package/dist/registry.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +19 -21
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/shared.d.ts +9 -11
- package/dist/routes/shared.d.ts.map +1 -1
- package/dist/routes/shared.js.map +1 -1
- package/dist/server.js +5 -4
- package/dist/server.js.map +1 -1
- package/dist/skills.d.ts +8 -18
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +13 -60
- package/dist/skills.js.map +1 -1
- package/dist/types.d.ts +6 -20
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/code-first-exploration.md +16 -16
- package/documentation/code-mode.md +137 -63
- package/documentation/connectors.md +1 -1
- package/documentation/meta-tools.md +96 -33
- package/documentation/rich-output-design.md +212 -0
- package/ethos.md +17 -19
- package/examples/node/README.md +1 -2
- package/examples/node/src/index.ts +1 -3
- package/examples/worker/README.md +19 -16
- package/examples/worker/src/d1-activity-row.ts +40 -0
- package/examples/worker/src/d1-activity.ts +3 -2
- package/examples/worker/src/index.ts +6 -14
- package/examples/worker/wrangler.jsonc +3 -6
- package/package.json +1 -1
- package/src/activity.ts +69 -3
- package/src/catalog-service.ts +113 -20
- package/src/connectors/api.ts +2 -2
- package/src/connectors/remote-mcp.ts +1 -1
- package/src/errors.ts +104 -3
- package/src/execute.ts +237 -37
- package/src/index.ts +60 -67
- package/src/invocation.ts +61 -19
- package/src/meta-tools.ts +136 -482
- package/src/operator-ui/browser.ts +10 -2
- package/src/operator-ui/generated.ts +1 -1
- package/src/registry.ts +7 -35
- package/src/routes/mcp.ts +19 -21
- package/src/routes/shared.ts +8 -11
- package/src/server.ts +7 -7
- package/src/skills.ts +11 -74
- package/src/types.ts +6 -21
- package/src/version.ts +1 -1
- package/templates/node/README.md +2 -1
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -4,10 +4,10 @@ This is the canonical instruction file for coding agents. `CLAUDE.md` is a
|
|
|
4
4
|
symlink to this file so every agent works from the same conventions.
|
|
5
5
|
|
|
6
6
|
A single MCP endpoint aggregating downstream connectors (remote MCP servers and
|
|
7
|
-
plain HTTP APIs) behind
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
plain HTTP APIs) behind seven meta-tools, `execute_code` among them. Every
|
|
8
|
+
deployment configures an executor, and agents reach connectors by writing
|
|
9
|
+
JavaScript against it. One fetch-native core, running on both Node and
|
|
10
|
+
Cloudflare Workers.
|
|
11
11
|
|
|
12
12
|
- **[`ethos.md`](./ethos.md) is the constitution.** It states what connecta is
|
|
13
13
|
and isn't, and its decisions table carries a verdict for every shape already
|
|
@@ -15,8 +15,10 @@ tools. One fetch-native core, running on both Node and Cloudflare Workers.
|
|
|
15
15
|
designing or building anything: a `refused` row is a "no" with the reason
|
|
16
16
|
attached, and a `removed` row (toolkits
|
|
17
17
|
[#178](https://github.com/zackbart/connecta/issues/178), proactive credential
|
|
18
|
-
liveness [#179](https://github.com/zackbart/connecta/issues/179)
|
|
19
|
-
surface
|
|
18
|
+
liveness [#179](https://github.com/zackbart/connecta/issues/179), the classic
|
|
19
|
+
executor-free surface
|
|
20
|
+
[#273](https://github.com/zackbart/connecta/issues/273)) records a surface
|
|
21
|
+
that no longer exists — do not reintroduce it without a new decision.
|
|
20
22
|
- There is no TODO.md — the roadmap lives in
|
|
21
23
|
[GitHub issues](https://github.com/zackbart/connecta/issues). When you find
|
|
22
24
|
TODO items (in code comments, docs, or conversation), don't accumulate them
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,156 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.11.0 — 2026-07-31
|
|
6
|
+
|
|
7
|
+
**This is a breaking deployment release.** Connecta no longer carries the old
|
|
8
|
+
executor-free compatibility surface. Every deployment must configure an
|
|
9
|
+
executor and now exposes the same seven tools; construction refuses to boot
|
|
10
|
+
without one. Existing code-first deployments keep their model-facing surface.
|
|
11
|
+
Node deployments that omitted an executor must add `quickJsExecutor()`, while
|
|
12
|
+
Workers deployments must configure a `DynamicWorkerExecutor` and its paid-plan
|
|
13
|
+
Worker Loader binding. Remove any `surface` setting during the upgrade. (#273)
|
|
14
|
+
|
|
15
|
+
The consolidation also removes the classic-only handlers and configuration
|
|
16
|
+
that had become two implementations of the same work. Discovery and batching
|
|
17
|
+
now have one home inside `execute_code`, while `call_tool`,
|
|
18
|
+
`call_destructive_tool`, and result retrieval remain explicit host boundaries.
|
|
19
|
+
The superseded code-first gate has been reduced to its surviving measurement
|
|
20
|
+
record instead of retaining a second executable version of the product.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **An executor is required.** `ConnectaConfig.executor` is no longer optional;
|
|
25
|
+
use `quickJsExecutor()` from `@zackbart/connecta/quickjs` on Node or
|
|
26
|
+
`DynamicWorkerExecutor` from `@cloudflare/codemode` on Workers. The CLI
|
|
27
|
+
template, examples, doctor, documentation, and package smoke test all enforce
|
|
28
|
+
the same deployment shape.
|
|
29
|
+
- **Every MCP connection advertises exactly seven tools.** The former
|
|
30
|
+
top-level `list_connectors`, `describe_tools`, and `batch_call` registrations
|
|
31
|
+
are removed. Their supported equivalents are `connecta.search`,
|
|
32
|
+
`connecta.describe`, and `connecta.batch` inside `execute_code`.
|
|
33
|
+
- **The old surface controls fail explicitly.** Supplying the removed
|
|
34
|
+
`ConnectaConfig.surface` option throws instead of being ignored. The
|
|
35
|
+
classic-only `calls.maxBatchResultBytes` option also throws; program batching
|
|
36
|
+
is bounded by the executor and `connecta.batch` limits, while individual
|
|
37
|
+
calls still honor `calls.maxResultBytes` and connector overrides.
|
|
38
|
+
- **Cloudflare deployments require code mode.** The Worker example now requires
|
|
39
|
+
its `worker_loaders` binding and the Workers Paid plan rather than falling
|
|
40
|
+
back to the classic surface when the binding is absent.
|
|
41
|
+
- **The retired code-first gate is archival.** Its executable harness is
|
|
42
|
+
removed now that code-first is the sole product surface; the current-version
|
|
43
|
+
audit remains the live measurement path.
|
|
44
|
+
|
|
45
|
+
## 0.10.6 — 2026-07-31
|
|
46
|
+
|
|
47
|
+
Programs gained a rich-output channel. `execute_code` code can now call
|
|
48
|
+
`connecta.emit(block)` to deliver text, image, and audio MCP content blocks
|
|
49
|
+
alongside its JSON return value — the piece code mode was missing for output
|
|
50
|
+
that cannot be projected, like a screenshot a downstream tool returned. The
|
|
51
|
+
channel is additive: a program that never emits produces the byte-for-byte
|
|
52
|
+
prior response, no executor changed to carry it, and deployments that do
|
|
53
|
+
nothing get sensible budgets. The design record is
|
|
54
|
+
`documentation/rich-output-design.md` (#267); the contract is `code-mode.md`'s
|
|
55
|
+
"Emitted output" clauses (#270).
|
|
56
|
+
|
|
57
|
+
Agent recovery is now executable data instead of prose at the remaining local
|
|
58
|
+
failure points. Address mistakes, approval reroutes, shortcut collisions, and
|
|
59
|
+
paged results identify their next call directly; activity classifies those
|
|
60
|
+
moments without retaining payloads. Connector behavior and approval authority
|
|
61
|
+
are unchanged.
|
|
62
|
+
|
|
63
|
+
Alongside both, three pieces of runtime text stop naming tools the receiving
|
|
64
|
+
surface does not serve — a routing failure connecta authored itself, and one the
|
|
65
|
+
always-loaded-text sweep missed because it only reads descriptions, not error
|
|
66
|
+
strings and tool results. All three fixes are agent-visible wording; no wire
|
|
67
|
+
shape, tool surface, or policy changes. Operators who tightened
|
|
68
|
+
`discovery.probeTimeoutMs` also get that deadline honored inside `execute_code`,
|
|
69
|
+
which had been probing at the 30-second default no matter what was configured.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **`connecta.emit(block)` inside `execute_code`.** Strictly validated
|
|
74
|
+
`text` / `image` / `audio` blocks, collected host-side and appended to the
|
|
75
|
+
result after the JSON envelope on success only; a failed program discards
|
|
76
|
+
them visibly (`emittedDiscarded`). Budgets fail loudly at the emit call and
|
|
77
|
+
spend no host-call budget.
|
|
78
|
+
- **`ConnectaConfig.execute.maxEmittedBytes` / `.maxEmittedBlocks`.** The
|
|
79
|
+
emission budgets, defaulting to 4,000,000 serialized bytes and 32 blocks per
|
|
80
|
+
run.
|
|
81
|
+
- **`diagnostics.emitted`.** With `diagnostics: true`, one payload-free
|
|
82
|
+
aggregate (block count and serialized bytes) when a program emitted.
|
|
83
|
+
- **Local routing failures carry structured recovery.** Unknown addresses and
|
|
84
|
+
tools suggest scoped discovery, read-path policy refusals preserve the
|
|
85
|
+
canonical address for `call_destructive_tool` — with the original arguments
|
|
86
|
+
when they fit a 512-byte echo budget, and an instruction to re-send them when
|
|
87
|
+
they do not — and ambiguous code-mode aliases list every canonical
|
|
88
|
+
`connecta.call` candidate. The suggested discovery route follows the caller's
|
|
89
|
+
own surface: `search_tools` for a top-level call, `connecta.search` with the
|
|
90
|
+
same arguments for a miss inside `execute_code`, which cannot call a tool.
|
|
91
|
+
The address gets the same 512-byte budget as the argument echo but the
|
|
92
|
+
opposite rule — clamped with a `…` marker rather than dropped, since the
|
|
93
|
+
address is the thing being corrected. Short addresses, which is all real
|
|
94
|
+
ones, come back exact.
|
|
95
|
+
- **Destructive calls accept explanatory context.** An optional `reason` of at
|
|
96
|
+
most 500 characters gives the MCP host human-readable intent without entering
|
|
97
|
+
downstream arguments or granting authority. An empty or whitespace-only one
|
|
98
|
+
is treated as absent rather than failing the call.
|
|
99
|
+
- **Activity exposes coarse agent friction.** Typed codes derive
|
|
100
|
+
`tool_not_found`, `schema_retry`, `destructive_reroute`, `auth_required`, or
|
|
101
|
+
`result_too_large`; no payload or raw error text is added.
|
|
102
|
+
|
|
103
|
+
### Changed
|
|
104
|
+
|
|
105
|
+
- **Paged results name the exact next call.** `call_tool` and `batch_call`
|
|
106
|
+
truncation notices include `get_result` arguments with the generated id and
|
|
107
|
+
byte offset zero. Program results and oversized discovery responses still
|
|
108
|
+
carry no `get_result` route: paging a program's return value is refused by
|
|
109
|
+
design, and a program can shrink anything.
|
|
110
|
+
- **`nextAction` is a wider union than it was.** `search_tools` routes may now
|
|
111
|
+
omit `arguments.connector` (an unknown *connector* cannot scope discovery to
|
|
112
|
+
itself), the ambiguous-alias route is keyed `function: "connecta.call"` with
|
|
113
|
+
no `tool` at all, and in-program discovery recovery is keyed
|
|
114
|
+
`function: "connecta.search"` carrying the same `{ query, connector?,
|
|
115
|
+
includeSchemas }` arguments the tool route does. A consumer that narrowed on
|
|
116
|
+
`nextAction.tool` must handle the function-keyed shapes too.
|
|
117
|
+
- **An address that resolves to nothing is now recorded.** A call to a
|
|
118
|
+
connector id that does not exist emits one activity event at the address as
|
|
119
|
+
written, with `unknown_address` and `tool_not_found` friction — provided the
|
|
120
|
+
address splits into the `<connectorId>.<toolName>` shape activity keeps; one
|
|
121
|
+
with no interior dot still records nothing. Previously the single most common
|
|
122
|
+
address mistake left no trace at all. Addresses were already a first-class
|
|
123
|
+
activity field; nothing new is retained. Because those fields now hold
|
|
124
|
+
caller-authored text, the recording seam clamps `connectorId` and `toolName`
|
|
125
|
+
at 128 UTF-8 bytes each and `address` at 257, marked with `…` — far past any
|
|
126
|
+
real id, far short of an invented 40 KB one.
|
|
127
|
+
- **A truncated result is friction, not an error.** An oversized result reports
|
|
128
|
+
`friction: "result_too_large"` on an `outcome: "success"` event and writes no
|
|
129
|
+
`errorCode`, so consumers counting error codes stop counting truncated
|
|
130
|
+
successes as failures. The Worker D1 example gains a `friction` column;
|
|
131
|
+
existing tables need `ALTER TABLE tool_call_activity ADD COLUMN friction
|
|
132
|
+
TEXT` before deploying it.
|
|
133
|
+
|
|
134
|
+
### Fixed
|
|
135
|
+
|
|
136
|
+
- **Discovery errors stay on their advertised surface.** The over-100-address
|
|
137
|
+
rejection and the catalog-probe timeout label now name `describe_tools` or
|
|
138
|
+
`connecta.describe` according to the route the caller actually took, so a
|
|
139
|
+
program is never told to split its list across a tool it cannot call. The
|
|
140
|
+
route is passed in explicitly rather than inferred from the deployment's
|
|
141
|
+
surface: a classic deployment with an executor serves `describe_tools` at top
|
|
142
|
+
level while every in-program describe still arrives through
|
|
143
|
+
`connecta.describe`.
|
|
144
|
+
- **The OAuth handoff points at a check the caller can run.** `authorize_connector`
|
|
145
|
+
is registered on both surfaces, but its success instructions told every agent
|
|
146
|
+
to "re-run `list_connectors`" — a tool the code-first surface folded away. A
|
|
147
|
+
code-first deployment is now told to retry the original call and confirm the
|
|
148
|
+
catalog loads with `connecta.search` inside `execute_code`; the classic
|
|
149
|
+
wording is unchanged.
|
|
150
|
+
- **`discovery.probeTimeoutMs` reaches code mode.** The sandbox's catalog
|
|
151
|
+
service received the deployment's discovery concurrency but not its probe
|
|
152
|
+
deadline, so an in-program `connecta.describe` against a hung connector waited
|
|
153
|
+
the 30-second default regardless of operator configuration.
|
|
154
|
+
|
|
5
155
|
## 0.10.5 — 2026-07-30
|
|
6
156
|
|
|
7
157
|
A consumer-audit recovery release. Schema discovery now provides the bounded
|
package/README.md
CHANGED
|
@@ -69,7 +69,8 @@ my-connecta/
|
|
|
69
69
|
For an agent setting this up, the contract is:
|
|
70
70
|
|
|
71
71
|
1. Edit `src/index.ts`; do not copy Connecta internals into the deployment.
|
|
72
|
-
2. Keep `executor: quickJsExecutor()`
|
|
72
|
+
2. Keep the required `executor: quickJsExecutor()` configuration; without an
|
|
73
|
+
executor the deployment refuses to boot.
|
|
73
74
|
3. Keep secrets in environment variables or a secret store, never source.
|
|
74
75
|
4. Add code only for deliberate `api()` connectors.
|
|
75
76
|
5. Run `npm run typecheck`, start the server, and run
|
|
@@ -92,9 +93,9 @@ Other supported deployment shapes:
|
|
|
92
93
|
- [Cloudflare Worker deployment](./examples/worker/)
|
|
93
94
|
- [Subsystem documentation](./documentation/)
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
Every deployment configures a sandbox: QuickJS on Node or a Dynamic Worker on
|
|
97
|
+
Cloudflare. Construction fails with an actionable error when the executor is
|
|
98
|
+
missing, so the model-facing interface is always the same seven tools.
|
|
98
99
|
|
|
99
100
|
## Project status
|
|
100
101
|
|
package/bin/connecta.mjs
CHANGED
|
@@ -186,13 +186,6 @@ async function doctor() {
|
|
|
186
186
|
if (health.status !== "ok") {
|
|
187
187
|
throw new Error(`Unexpected health status: ${String(health.status)}`);
|
|
188
188
|
}
|
|
189
|
-
if (health.admission?.code === null) {
|
|
190
|
-
throw new Error(
|
|
191
|
-
"The server has no executor and is serving classic compatibility. " +
|
|
192
|
-
"Keep executor: quickJsExecutor() for the prescribed surface.",
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
189
|
let requestId = 0;
|
|
197
190
|
const mcp = async (method, params) =>
|
|
198
191
|
jsonResponse(
|
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,12 @@ 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 discovery route a routing failure should send a caller back through. Same
|
|
10
|
+
* catalog logic serves both the top-level `search_tools` path and the
|
|
11
|
+
* in-program `connecta.search` path, so callers pass the route they own.
|
|
12
|
+
*/
|
|
13
|
+
export type SearchRoute = "search_tools" | "connecta.search";
|
|
8
14
|
export declare class DiscoveryPolicyError extends Error {
|
|
9
15
|
readonly code: "invalid_args" | "result_too_large";
|
|
10
16
|
constructor(code: "invalid_args" | "result_too_large", message: string);
|
|
@@ -106,13 +112,31 @@ export declare class CatalogService {
|
|
|
106
112
|
readonly requestScope: object;
|
|
107
113
|
private readonly probeTimeoutMs;
|
|
108
114
|
private readonly concurrency;
|
|
115
|
+
private readonly searchRoute;
|
|
109
116
|
private readonly loaded;
|
|
110
117
|
private readonly loading;
|
|
111
118
|
constructor(registry: RegistryView, baseUrl: string, options?: {
|
|
112
119
|
requestScope?: object;
|
|
113
120
|
probeTimeoutMs?: number;
|
|
114
121
|
concurrency?: number;
|
|
122
|
+
/** The discovery route recovery records name. Default `search_tools`. */
|
|
123
|
+
searchRoute?: SearchRoute;
|
|
115
124
|
});
|
|
125
|
+
/**
|
|
126
|
+
* Send a caller back to discovery through the surface it can actually reach.
|
|
127
|
+
* Both variants carry the same scoping arguments because `connecta.search`
|
|
128
|
+
* takes the same ones `search_tools` does; only the key naming the callable
|
|
129
|
+
* differs, the way the ambiguous-alias record already names a function.
|
|
130
|
+
*
|
|
131
|
+
* Not private: `InvocationService` builds the same class of record when a
|
|
132
|
+
* call fails schema validation, and it is this catalog's route that decides
|
|
133
|
+
* which key that record carries. Duplicating the branch there would let the
|
|
134
|
+
* two drift.
|
|
135
|
+
*/
|
|
136
|
+
searchRecovery(args: {
|
|
137
|
+
query: string;
|
|
138
|
+
connector?: string;
|
|
139
|
+
}, purpose: string): NonNullable<CallErrorDetails["nextAction"]>;
|
|
116
140
|
loadConnector(id: string, callOptions?: ConnectorOperationOptions): Promise<ToolDef[]>;
|
|
117
141
|
private loadForDiscovery;
|
|
118
142
|
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";
|
|
@@ -45,10 +45,21 @@ function discoveryAddresses(args) {
|
|
|
45
45
|
throw new DiscoveryPolicyError("invalid_args", 'describe takes { address: "<connectorId>.<toolName>" } or { addresses: ["<connectorId>.<toolName>", ...] }.');
|
|
46
46
|
}
|
|
47
47
|
if (value.length > MAX_DESCRIBE_ADDRESSES) {
|
|
48
|
-
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 connecta.describe calls.`);
|
|
49
49
|
}
|
|
50
50
|
return value;
|
|
51
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
|
+
}
|
|
52
63
|
/** Serialize once and count the exact bytes the MCP adapter would emit. */
|
|
53
64
|
export function boundedDiscoveryText(value, hint) {
|
|
54
65
|
const text = JSON.stringify(value);
|
|
@@ -116,6 +127,7 @@ export class CatalogService {
|
|
|
116
127
|
requestScope;
|
|
117
128
|
probeTimeoutMs;
|
|
118
129
|
concurrency;
|
|
130
|
+
searchRoute;
|
|
119
131
|
loaded = new Map();
|
|
120
132
|
loading = new Map();
|
|
121
133
|
constructor(registry, baseUrl, options = {}) {
|
|
@@ -125,6 +137,28 @@ export class CatalogService {
|
|
|
125
137
|
this.probeTimeoutMs =
|
|
126
138
|
normalizeTimeoutMs(options.probeTimeoutMs) ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
127
139
|
this.concurrency = resolveDiscoveryConcurrency(options.concurrency);
|
|
140
|
+
this.searchRoute = options.searchRoute ?? "search_tools";
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Send a caller back to discovery through the surface it can actually reach.
|
|
144
|
+
* Both variants carry the same scoping arguments because `connecta.search`
|
|
145
|
+
* takes the same ones `search_tools` does; only the key naming the callable
|
|
146
|
+
* differs, the way the ambiguous-alias record already names a function.
|
|
147
|
+
*
|
|
148
|
+
* Not private: `InvocationService` builds the same class of record when a
|
|
149
|
+
* call fails schema validation, and it is this catalog's route that decides
|
|
150
|
+
* which key that record carries. Duplicating the branch there would let the
|
|
151
|
+
* two drift.
|
|
152
|
+
*/
|
|
153
|
+
searchRecovery(args, purpose) {
|
|
154
|
+
const searchArgs = {
|
|
155
|
+
query: args.query,
|
|
156
|
+
...(args.connector !== undefined ? { connector: args.connector } : {}),
|
|
157
|
+
includeSchemas: "compact",
|
|
158
|
+
};
|
|
159
|
+
return this.searchRoute === "connecta.search"
|
|
160
|
+
? { function: "connecta.search", arguments: searchArgs, purpose }
|
|
161
|
+
: { tool: "search_tools", arguments: searchArgs, purpose };
|
|
128
162
|
}
|
|
129
163
|
async loadConnector(id, callOptions = {}) {
|
|
130
164
|
const cached = this.loaded.get(id);
|
|
@@ -157,7 +191,10 @@ export class CatalogService {
|
|
|
157
191
|
if (!resolved) {
|
|
158
192
|
return {
|
|
159
193
|
ok: false,
|
|
160
|
-
error:
|
|
194
|
+
error: {
|
|
195
|
+
...framingError("unknown_address", `Unknown address "${boundedEchoText(address)}"`),
|
|
196
|
+
nextAction: this.searchRecovery({ query: recoveryQuery(address) }, "Find the configured canonical address before retrying."),
|
|
197
|
+
},
|
|
161
198
|
catalogMs: 0,
|
|
162
199
|
};
|
|
163
200
|
}
|
|
@@ -180,7 +217,13 @@ export class CatalogService {
|
|
|
180
217
|
if (!definition) {
|
|
181
218
|
return {
|
|
182
219
|
ok: false,
|
|
183
|
-
error:
|
|
220
|
+
error: {
|
|
221
|
+
...framingError("unknown_tool", `Unknown tool "${boundedEchoText(resolved.toolName)}" on connector "${resolved.connector.id}"`),
|
|
222
|
+
nextAction: this.searchRecovery({
|
|
223
|
+
query: recoveryQuery(resolved.toolName),
|
|
224
|
+
connector: resolved.connector.id,
|
|
225
|
+
}, "Find the connector's current canonical tool address."),
|
|
226
|
+
},
|
|
184
227
|
catalogMs: Date.now() - started,
|
|
185
228
|
connector: resolved.connector,
|
|
186
229
|
toolName: resolved.toolName,
|
|
@@ -206,7 +249,10 @@ export class CatalogService {
|
|
|
206
249
|
if (!connector) {
|
|
207
250
|
return {
|
|
208
251
|
ok: false,
|
|
209
|
-
error:
|
|
252
|
+
error: {
|
|
253
|
+
...framingError("unknown_address", `Unknown address "${boundedEchoText(`${connectorId}.${alias}`)}"`),
|
|
254
|
+
nextAction: this.searchRecovery({ query: recoveryQuery(alias) }, "Find the configured canonical address before retrying."),
|
|
255
|
+
},
|
|
210
256
|
catalogMs: 0,
|
|
211
257
|
};
|
|
212
258
|
}
|
|
@@ -229,7 +275,10 @@ export class CatalogService {
|
|
|
229
275
|
if (!definition) {
|
|
230
276
|
return {
|
|
231
277
|
ok: false,
|
|
232
|
-
error:
|
|
278
|
+
error: {
|
|
279
|
+
...framingError("unknown_tool", `Unknown tool "${boundedEchoText(alias)}" on connector "${connector.id}"`),
|
|
280
|
+
nextAction: this.searchRecovery({ query: recoveryQuery(alias), connector: connector.id }, "Find the connector's current canonical tool address."),
|
|
281
|
+
},
|
|
233
282
|
catalogMs: Date.now() - started,
|
|
234
283
|
connector,
|
|
235
284
|
toolName: alias,
|
|
@@ -243,8 +292,13 @@ export class CatalogService {
|
|
|
243
292
|
ok: false,
|
|
244
293
|
error: {
|
|
245
294
|
code: "ambiguous_tool_alias",
|
|
246
|
-
message: `Tool alias "${alias}" is ambiguous on connector "${connector.id}" because ${names} sanitize to the same name. Use connecta.call with an exact address.`,
|
|
295
|
+
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.`,
|
|
247
296
|
retryable: false,
|
|
297
|
+
nextAction: {
|
|
298
|
+
function: "connecta.call",
|
|
299
|
+
addresses: [definition, ...collisions].map((tool) => `${connector.id}.${tool.name}`),
|
|
300
|
+
purpose: "Choose the intended canonical address and call it with the original arguments.",
|
|
301
|
+
},
|
|
248
302
|
},
|
|
249
303
|
catalogMs: Date.now() - started,
|
|
250
304
|
connector,
|
|
@@ -275,7 +329,12 @@ export class CatalogService {
|
|
|
275
329
|
? [scopedConnector]
|
|
276
330
|
: []
|
|
277
331
|
: this.registry.listConnectors();
|
|
278
|
-
const catalogs = await mapSettledWithConcurrency(connectors, this.concurrency, (connector) =>
|
|
332
|
+
const catalogs = await mapSettledWithConcurrency(connectors, this.concurrency, (connector) =>
|
|
333
|
+
// Unlike the describe path, this label never reaches a caller: search
|
|
334
|
+
// only counts rejected catalogs (`unavailableCatalogs` below) and
|
|
335
|
+
// renders its own guidance, so the folded name here stays internal and
|
|
336
|
+
// needs no surface awareness.
|
|
337
|
+
this.loadForDiscovery(connector.id, `search_tools probe of "${connector.id}"`));
|
|
279
338
|
const searchableCatalogs = catalogs.map((catalog) => catalog.status === "fulfilled"
|
|
280
339
|
? {
|
|
281
340
|
status: "fulfilled",
|
|
@@ -469,7 +528,7 @@ export class CatalogService {
|
|
|
469
528
|
.map((entry) => entry.resolved?.connector.id)
|
|
470
529
|
.filter((id) => Boolean(id))),
|
|
471
530
|
];
|
|
472
|
-
const loaded = await mapSettledWithConcurrency(connectorIds, this.concurrency, (id) => this.loadForDiscovery(id, `
|
|
531
|
+
const loaded = await mapSettledWithConcurrency(connectorIds, this.concurrency, (id) => this.loadForDiscovery(id, `connecta.describe probe of "${id}"`));
|
|
473
532
|
const catalogs = new Map();
|
|
474
533
|
loaded.forEach((result, index) => {
|
|
475
534
|
const connectorId = connectorIds[index];
|