@zackbart/connecta 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/README.md +3 -2
- package/bin/connecta.mjs +4 -8
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.js +6 -4
- package/dist/agent-routes.js +231 -0
- package/dist/auth/downstream-oauth/consent.js +519 -0
- package/dist/auth/downstream-oauth/grant-store.js +439 -0
- package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
- package/dist/auth/downstream-oauth/refresh-records.js +447 -0
- package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
- package/dist/auth/downstream-oauth.js +66 -2382
- package/dist/auth/native.js +37 -0
- package/dist/auth/oauth-grant.js +13 -0
- package/dist/auth/static-oauth.js +153 -131
- package/dist/auth-elicitation.js +7 -1
- package/dist/authorization-handoff.js +11 -5
- package/dist/catalog-cache.js +219 -59
- package/dist/catalog-policy.js +18 -0
- package/dist/catalog-service.js +284 -47
- package/dist/catalog.js +18 -2
- package/dist/config.d.ts +16 -11
- package/dist/config.js +9 -6
- package/dist/connector-caller.d.ts +1 -1
- package/dist/connector-health.js +59 -0
- package/dist/connector-status.js +117 -0
- package/dist/connectors/api-connector.d.ts +7 -2
- package/dist/connectors/api-connector.js +7 -4
- package/dist/connectors/negotiation-cache.js +22 -4
- package/dist/connectors/remote-mcp/call.js +164 -0
- package/dist/connectors/remote-mcp/catalog.js +278 -0
- package/dist/connectors/remote-mcp/config.js +263 -0
- package/dist/connectors/remote-mcp/connection.js +682 -0
- package/dist/connectors/remote-mcp/errors.js +488 -0
- package/dist/connectors/remote-mcp/oauth.js +473 -0
- package/dist/connectors/remote-mcp/resources.js +473 -0
- package/dist/connectors/remote-mcp/runtime.js +1 -0
- package/dist/connectors/remote-mcp/transport.js +555 -0
- package/dist/connectors/remote-mcp.js +62 -2863
- package/dist/credential-contract.d.ts +23 -0
- package/dist/credential-reads.js +17 -0
- package/dist/credential-verification.js +53 -0
- package/dist/credentials.d.ts +7 -0
- package/dist/credentials.js +60 -25
- package/dist/describe-config.d.ts +12 -2
- package/dist/describe-config.js +2 -0
- package/dist/discovery-health.js +199 -0
- package/dist/downstream-input.js +2 -0
- package/dist/errors.d.ts +55 -17
- package/dist/errors.js +95 -41
- package/dist/execute.js +152 -114
- package/dist/executor-result.js +3 -1
- package/dist/guest-functions.js +83 -0
- package/dist/guest-runtime.js +60 -19
- package/dist/guest-types.d.ts +19 -5
- package/dist/http/argument-problems.js +35 -0
- package/dist/http/errors.js +5 -4
- package/dist/http/fields.js +192 -13
- package/dist/http/hatch-response.js +73 -0
- package/dist/http/hatch.js +88 -23
- package/dist/http/oauth.js +3 -2
- package/dist/http/provider.js +32 -4
- package/dist/http/safety.js +8 -0
- package/dist/http/tools.js +44 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -2
- package/dist/invocation.js +26 -8
- package/dist/membership.d.ts +13 -5
- package/dist/membership.js +34 -2
- package/dist/meta-tools.js +54 -31
- package/dist/native-recovery.js +4 -16
- package/dist/native-tools.js +5 -2
- package/dist/oauth-sealing.js +10 -0
- package/dist/operation-vocabulary.js +135 -13
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +1 -0
- package/dist/operator-ui/contract.d.ts +10 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +22 -1
- package/dist/provider-call-facts.js +6 -2
- package/dist/providers/_shared/rest/operation-index.js +48 -50
- package/dist/providers/_shared/rest/value-safety.js +80 -0
- package/dist/providers/breeze/api.js +7 -1
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/breeze/tools.js +4 -3
- package/dist/providers/ccb/api.js +2 -1
- package/dist/providers/ccb/index.js +5 -0
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/cloudflare/api.js +8 -1
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/lib/schemas.js +24 -22
- package/dist/providers/docs/safety.js +2 -0
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/docs/tools.js +2 -4
- package/dist/providers/drive/api.js +1 -0
- package/dist/providers/drive/lib/contracts.js +8 -5
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/drive/tools.js +11 -16
- package/dist/providers/forms/api.js +1 -0
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/forms/tools.js +10 -7
- package/dist/providers/github/api.js +1 -0
- package/dist/providers/github/lib/discovery.js +1 -1
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +61 -17
- package/dist/providers/gmail/lib/schemas.js +60 -50
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/gmail/tools.js +7 -5
- package/dist/providers/granola/index.js +2 -0
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/api.js +7 -1
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/planning-center/api.js +3 -1
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/skill.generated.js +1 -1
- package/dist/providers/quickbooks/safety.js +80 -39
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/api.js +1 -0
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/sheets/tools.js +13 -14
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/slides/tools.js +15 -13
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/api.js +3 -1
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/vercel/api.js +1 -1
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/providers/vercel/tools.js +4 -3
- package/dist/registry.d.ts +10 -4
- package/dist/registry.js +41 -13
- package/dist/routes/credentials.js +129 -60
- package/dist/routes/dispatch.js +5 -1
- package/dist/routes/mcp.js +30 -14
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +6 -1
- package/dist/routes/shared.js +7 -1
- package/dist/routes/ui-config.js +2 -2
- package/dist/routes/ui-data.js +126 -20
- package/dist/routes/ui.js +16 -0
- package/dist/runtime/call.js +7 -2
- package/dist/runtime/native-storage.js +12 -0
- package/dist/runtime/request-reads.js +207 -0
- package/dist/runtime/request-scope.js +16 -0
- package/dist/runtime/storage.js +12 -0
- package/dist/skills.js +37 -20
- package/dist/storage/keys.js +53 -3
- package/dist/storage/memory.js +8 -2
- package/dist/storage/sql-native.js +53 -1
- package/dist/storage/sql.js +12 -2
- package/dist/storage-digest.js +32 -0
- package/dist/timeout.js +8 -0
- package/dist/types.d.ts +33 -0
- package/dist/usage-guide.js +16 -35
- package/dist/validate.d.ts +3 -0
- package/dist/validate.js +220 -158
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/write-outcome.js +0 -3
- package/documentation/README.md +13 -13
- package/documentation/architecture.md +385 -20
- package/documentation/auth.md +233 -64
- package/documentation/code-mode.md +122 -39
- package/documentation/deploying.md +6 -3
- package/documentation/integrating.md +16 -2
- package/documentation/meta-tools.md +240 -49
- package/documentation/migration-0.33.md +5 -1
- package/documentation/migration-0.34.md +3 -1
- package/documentation/operator-tests.md +1 -1
- package/documentation/operator-ui.md +105 -12
- package/documentation/pool-membership-upgrade.md +44 -0
- package/documentation/providers.md +105 -0
- package/examples/worker/AGENTS.md +10 -10
- package/examples/worker/README.md +4 -3
- package/examples/worker/src/connecta.config.ts +1 -0
- package/guides/connecting-agents.md +2 -1
- package/guides/how-it-works.md +4 -2
- package/package.json +1 -1
- package/templates/node/AGENTS.md +13 -12
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +1 -0
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
// Private, request-free discovery facts. No provider, schema, handler or I/O dependency.
|
|
2
2
|
import { lexicalSearchContent, lexicalQueryTerms } from "./catalog.js";
|
|
3
|
+
/** Ready calls one vocabulary offers a search: only its top-ranked operation. */
|
|
4
|
+
const MAX_READY_PER_VOCABULARY = 1;
|
|
5
|
+
/**
|
|
6
|
+
* Normalized action words. A summary may supply these; every other query word
|
|
7
|
+
* names an object and must match the operation's resource identity.
|
|
8
|
+
*/
|
|
9
|
+
const ACTIONS = new Set(["get", "list", "count", "show", "create", "update", "delete"]);
|
|
10
|
+
/** Path tokens that version an API rather than name a resource. */
|
|
11
|
+
const VERSION = /^v?\d+$/;
|
|
3
12
|
const tools = new WeakMap();
|
|
4
13
|
const connectors = new WeakMap();
|
|
14
|
+
const synonymGroups = new WeakMap();
|
|
5
15
|
const OPERATION_VOCABULARY_BOUNDS = {
|
|
6
16
|
operations: 8192,
|
|
7
17
|
rowChars: 1024,
|
|
@@ -28,20 +38,56 @@ const aliases = {
|
|
|
28
38
|
modify: "update",
|
|
29
39
|
remove: "delete",
|
|
30
40
|
};
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
const normal = (word) => aliases[word] ?? token(word);
|
|
42
|
+
const terms = (text) => [...new Set(lexicalQueryTerms(lexicalSearchContent(text)).map(normal))];
|
|
43
|
+
/** Resource words of a path segment or operation id, without version or action words. */
|
|
44
|
+
const identity = (text) => terms(text).filter((word) => !VERSION.test(word) && !ACTIONS.has(word));
|
|
45
|
+
function resourceOf(path, operationId) {
|
|
46
|
+
const segments = path.slice(0, OPERATION_VOCABULARY_BOUNDS.rowChars).split("/").map(identity);
|
|
47
|
+
const names = [...segments, identity(operationId.slice(0, OPERATION_VOCABULARY_BOUNDS.rowChars))].filter((name) => name.length > 0);
|
|
48
|
+
return { names, last: segments.at(-1) ?? [], terms: new Set(names.flat()) };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Attach a maintained provider's lexical synonym groups to its connector. Each
|
|
52
|
+
* group's words are interchangeable query terms for that connector's tool
|
|
53
|
+
* ranking and operation vocabulary; nothing is inferred.
|
|
54
|
+
*/
|
|
55
|
+
export function registerSearchSynonyms(connector, groups) {
|
|
56
|
+
synonymGroups.set(connector, groups);
|
|
57
|
+
}
|
|
58
|
+
/** A connector's groups, including those a provider stamp inherits. */
|
|
59
|
+
function groupsOf(connector) {
|
|
60
|
+
let target = connector;
|
|
61
|
+
while (target && !synonymGroups.has(target))
|
|
62
|
+
target = Object.getPrototypeOf(target);
|
|
63
|
+
return target ? synonymGroups.get(target) : undefined;
|
|
64
|
+
}
|
|
65
|
+
/** The other words of every group holding `word`, each word under `normalize`. */
|
|
66
|
+
function synonymsIn(groups, word, normalize = (raw) => raw) {
|
|
67
|
+
return groups.flatMap((group) => {
|
|
68
|
+
const words = group.map(normalize);
|
|
69
|
+
return words.includes(word) ? words.filter((other) => other !== word) : [];
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Word to its declared synonyms for a connector's tool ranking, when its provider declares any. */
|
|
73
|
+
export function searchSynonyms(connector) {
|
|
74
|
+
const groups = groupsOf(connector);
|
|
75
|
+
return groups && ((word) => synonymsIn(groups, word));
|
|
76
|
+
}
|
|
34
77
|
/** Register only construction-authored compact summaries; never whole request/response schemas. */
|
|
35
78
|
export function registerOperationVocabulary(tool, rows) {
|
|
36
79
|
let postings;
|
|
37
80
|
const routes = [];
|
|
38
81
|
const cache = new Map();
|
|
39
|
-
tools.set(tool, (query, visible, safety, readOnly) => {
|
|
82
|
+
tools.set(tool, (query, visible, safety, readOnly, synonyms) => {
|
|
83
|
+
const none = { matched: false, ready: [] };
|
|
40
84
|
if (query.length > OPERATION_VOCABULARY_BOUNDS.queryChars)
|
|
41
|
-
return
|
|
85
|
+
return none;
|
|
42
86
|
const wanted = terms(query);
|
|
43
87
|
if (!wanted.length || wanted.length > OPERATION_VOCABULARY_BOUNDS.terms)
|
|
44
|
-
return
|
|
88
|
+
return none;
|
|
89
|
+
// Each wanted word matches through itself or any declared synonym.
|
|
90
|
+
const alternatives = wanted.map((word) => [word, ...(synonyms ? synonymsIn(synonyms, word, normal) : [])]);
|
|
45
91
|
if (!postings) {
|
|
46
92
|
postings = new Map();
|
|
47
93
|
let chars = 0;
|
|
@@ -52,7 +98,19 @@ export function registerOperationVocabulary(tool, rows) {
|
|
|
52
98
|
break;
|
|
53
99
|
chars += text.length;
|
|
54
100
|
const id = routes.length;
|
|
55
|
-
routes.push({
|
|
101
|
+
routes.push({
|
|
102
|
+
route: row.route,
|
|
103
|
+
write: row.write,
|
|
104
|
+
...(row.ready
|
|
105
|
+
? {
|
|
106
|
+
ready: {
|
|
107
|
+
path: row.ready.path,
|
|
108
|
+
summary: row.ready.summary.slice(0, OPERATION_VOCABULARY_BOUNDS.rowChars),
|
|
109
|
+
...resourceOf(row.ready.path, row.ready.operationId),
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
: {}),
|
|
113
|
+
});
|
|
56
114
|
for (const word of terms(text)) {
|
|
57
115
|
const hits = postings.get(word);
|
|
58
116
|
if (hits)
|
|
@@ -62,17 +120,20 @@ export function registerOperationVocabulary(tool, rows) {
|
|
|
62
120
|
}
|
|
63
121
|
}
|
|
64
122
|
}
|
|
65
|
-
const key =
|
|
123
|
+
const key = alternatives.map((words) => words.join("|")).join(" ");
|
|
66
124
|
let found = cache.get(key);
|
|
67
125
|
if (!found) {
|
|
68
|
-
const lists =
|
|
126
|
+
const lists = alternatives
|
|
127
|
+
.map((words) => [...new Set(words.flatMap((word) => postings.get(word) ?? []))])
|
|
128
|
+
.sort((a, b) => a.length - b.length);
|
|
69
129
|
const rest = lists.slice(1).map((list) => new Set(list));
|
|
70
130
|
found = lists[0].filter((id) => rest.every((set) => set.has(id)));
|
|
71
131
|
if (cache.size >= OPERATION_VOCABULARY_BOUNDS.cache)
|
|
72
132
|
cache.delete(cache.keys().next().value);
|
|
73
133
|
cache.set(key, found);
|
|
74
134
|
}
|
|
75
|
-
|
|
135
|
+
// The same endpoint, safety and visibility checks decide both answers.
|
|
136
|
+
const admitted = found.filter((id) => {
|
|
76
137
|
const row = routes[id];
|
|
77
138
|
if (row.write && readOnly)
|
|
78
139
|
return false;
|
|
@@ -80,6 +141,47 @@ export function registerOperationVocabulary(tool, rows) {
|
|
|
80
141
|
return false;
|
|
81
142
|
return row.route !== undefined && visible.has(row.route);
|
|
82
143
|
});
|
|
144
|
+
// A ready call must name what the query asks for. Every object word, or
|
|
145
|
+
// a declared synonym, belongs to a whole resource name whose words the
|
|
146
|
+
// query states in order. A word met only in a summary, or only as part of
|
|
147
|
+
// a compound name, can route a search, never stand in for the resource.
|
|
148
|
+
const objects = alternatives.filter((words) => !ACTIONS.has(words[0]));
|
|
149
|
+
const at = (name, start) => start + name.length <= objects.length && name.every((word, offset) => objects[start + offset].includes(word));
|
|
150
|
+
const covers = (names) => {
|
|
151
|
+
const covered = objects.map(() => false);
|
|
152
|
+
for (const name of names)
|
|
153
|
+
for (let start = 0; start < objects.length; start++)
|
|
154
|
+
if (at(name, start))
|
|
155
|
+
name.forEach((_, offset) => (covered[start + offset] = true));
|
|
156
|
+
return covered.every(Boolean);
|
|
157
|
+
};
|
|
158
|
+
const asked = new Set(alternatives.flat());
|
|
159
|
+
return {
|
|
160
|
+
matched: admitted.length > 0,
|
|
161
|
+
ready: objects.length
|
|
162
|
+
? admitted
|
|
163
|
+
.flatMap((id) => {
|
|
164
|
+
const { route, write, ready } = routes[id];
|
|
165
|
+
if (!ready || write || !route || !covers(ready.names))
|
|
166
|
+
return [];
|
|
167
|
+
return [
|
|
168
|
+
{
|
|
169
|
+
path: ready.path,
|
|
170
|
+
summary: ready.summary,
|
|
171
|
+
route,
|
|
172
|
+
id,
|
|
173
|
+
// Relevance first: the last segment names the resource asked
|
|
174
|
+
// for, then the fewest resource words the query did not ask for.
|
|
175
|
+
last: Number(objects.some((_, start) => ready.last.length > 0 && at(ready.last, start))),
|
|
176
|
+
extra: [...ready.terms].filter((word) => !asked.has(word) && !ACTIONS.has(word)).length,
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
})
|
|
180
|
+
.sort((a, b) => b.last - a.last || a.extra - b.extra || a.path.length - b.path.length || a.id - b.id)
|
|
181
|
+
.slice(0, MAX_READY_PER_VOCABULARY)
|
|
182
|
+
.map(({ path, summary, route }) => ({ path, summary, route }))
|
|
183
|
+
: [],
|
|
184
|
+
};
|
|
83
185
|
});
|
|
84
186
|
}
|
|
85
187
|
/** Move private facts across the public ApiTool -> Connector construction boundary. */
|
|
@@ -91,11 +193,31 @@ export function registerConnectorVocabulary(connector, declarations) {
|
|
|
91
193
|
if (entries.length)
|
|
92
194
|
connectors.set(connector, new Map(entries));
|
|
93
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Read tools whose operation vocabulary matches every query word, and the
|
|
198
|
+
* fully determined reads among those operations, each through a visible read
|
|
199
|
+
* tool of `catalog`: the caller's view, after endpoint, connector, hidden-tool
|
|
200
|
+
* and grant filtering.
|
|
201
|
+
*/
|
|
94
202
|
export function operationVocabularyMatches(connector, catalog, query, safety, readOnly) {
|
|
95
203
|
const registered = connectors.get(connector);
|
|
204
|
+
const tools = [];
|
|
205
|
+
const ready = [];
|
|
96
206
|
if (!registered)
|
|
97
|
-
return
|
|
207
|
+
return { tools, ready };
|
|
98
208
|
const visible = new Set(catalog.map((tool) => tool.name));
|
|
99
|
-
|
|
100
|
-
|
|
209
|
+
const synonyms = groupsOf(connector);
|
|
210
|
+
for (const tool of catalog) {
|
|
211
|
+
const vocabulary = tool.classification === "read" ? registered.get(tool.name) : undefined;
|
|
212
|
+
const found = vocabulary?.(query, visible, safety, readOnly || connector.readOnly === true, synonyms);
|
|
213
|
+
if (!found?.matched)
|
|
214
|
+
continue;
|
|
215
|
+
tools.push(tool);
|
|
216
|
+
for (const { route, ...call } of found.ready) {
|
|
217
|
+
const target = catalog.find((candidate) => candidate.name === route);
|
|
218
|
+
if (target?.classification === "read")
|
|
219
|
+
ready.push({ ...call, tool: target });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { tools, ready };
|
|
101
223
|
}
|
|
@@ -8,7 +8,7 @@ type FailureStep = "MCP handshake" | "tools/list" | "tools/call" | "resources/re
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const OAUTH_ERROR_CODES: ReadonlySet<string>;
|
|
10
10
|
/** Inbound denial facts, never identity text or provider diagnostics. */
|
|
11
|
-
export type AuthDenialReason = "authentication_abandoned_saturated" | "authentication_busy" | "authentication_failed" | "authentication_provider_missing" | "authentication_unreachable" | "access_context_missing" | "access_context_unavailable" | "access_audience_invalid" | "access_identity_unavailable" | "access_authorization_header_rejected" | "authorization_header_invalid" | "token_type_mismatch" | "oauth_verification_failed" | "oauth_client_not_allowed" | "oauth_binding_mismatch" | "session_origin_mismatch" | "token_identity_mismatch" | "organization_membership_denied" | "organization_lookup_failed" | "organization_lookup_busy" | "user_missing" | "email_lookup_failed" | "verified_email_invalid" | "email_domain_denied" | "gate_denied" | "gate_failed" | "not_maintainer" | "pool_not_declared" | "not_pool_member";
|
|
11
|
+
export type AuthDenialReason = "authentication_abandoned_saturated" | "authentication_busy" | "authentication_failed" | "authentication_provider_missing" | "authentication_unreachable" | "access_context_missing" | "access_context_unavailable" | "access_audience_invalid" | "access_identity_unavailable" | "access_authorization_header_rejected" | "authorization_header_invalid" | "token_type_mismatch" | "oauth_verification_failed" | "oauth_client_not_allowed" | "oauth_binding_mismatch" | "session_origin_mismatch" | "token_identity_mismatch" | "organization_membership_denied" | "organization_lookup_failed" | "organization_lookup_busy" | "user_missing" | "email_lookup_failed" | "verified_email_invalid" | "email_domain_denied" | "gate_denied" | "gate_failed" | "ambiguous_pool" | "not_maintainer" | "pool_not_declared" | "not_pool_member";
|
|
12
12
|
/**
|
|
13
13
|
* Name `ctor`'s instances `label` in records and fixed text. Call once, at
|
|
14
14
|
* module scope, beside the class. An error whose prototype chain reaches no
|
|
@@ -110,7 +110,7 @@ export declare function failureRecord(subject: FailureSubject, failure?: unknown
|
|
|
110
110
|
* The events an operator log reports failures as. Closed: a new one is a
|
|
111
111
|
* reviewed line here, never a string assembled at the call site.
|
|
112
112
|
*/
|
|
113
|
-
export type FailureEvent = "Authentication admission refused" | "Cloudflare Access verification unavailable" | "operator_refused" | "Authentication released after its pending work became unreachable" | "negotiation cache read failed" | "negotiation cache write failed" | "pool tool grant is unreachable" | "call failed" | "input schema unusable; arguments are not validated" | "catalog read failed" | "catalog persistence failed" | "catalog invalidation failed" | "catalog refresh observation failed" | "catalog refresh failed; serving stale catalog" | "deferred catalog refresh failed" | "deferred catalog refresh could not attach to the runtime" | "catalog drift check failed" | "schema digest check failed; serving digested reviews as writes" | "credential shape read failed" | "credential test failed" | "credential test threw" | "session termination refused or failed; the downstream session may remain until its provider timeout" | "activity record failed" | "catalog drift record failed" | "activity read failed" | "OAuth start failed" | "OAuth disconnect failed" | "OAuth callback verifyState threw; no authorization code was exchanged" | "OAuth callback handoff could not be consumed; no authorization code was exchanged" | "MCP default endpoint request denied" | "MCP pool request denied" | "MCP agent endpoint request denied" | "OAuth error callback consent could not be consumed; no authorization code was exchanged" | "MCP handler error" | "operator status" | "operator agent slots read failed" | "result paging unavailable" | "native catalog unavailable" | "native name collision" | "native tools/list refused a pool over the 1,000-tool native cap" | "request failed" | "Clerk request denied" | "Clerk authentication failed";
|
|
113
|
+
export type FailureEvent = "Authentication admission refused" | "Cloudflare Access verification unavailable" | "operator_refused" | "Authentication released after its pending work became unreachable" | "negotiation cache read failed" | "negotiation cache write failed" | "discovery health read failed" | "discovery health write failed" | "pool tool grant is unreachable" | "call failed" | "input schema unusable; arguments are not validated" | "catalog read failed" | "catalog persistence failed" | "catalog invalidation failed" | "catalog refresh observation failed" | "catalog refresh failed; serving stale catalog" | "deferred catalog refresh failed" | "deferred catalog refresh could not attach to the runtime" | "catalog drift check failed" | "schema digest check failed; serving digested reviews as writes" | "credential shape read failed" | "credential test failed" | "credential test threw" | "credential verification record failed" | "session termination refused or failed; the downstream session may remain until its provider timeout" | "activity record failed" | "catalog drift record failed" | "activity read failed" | "OAuth start failed" | "OAuth disconnect failed" | "OAuth callback verifyState threw; no authorization code was exchanged" | "OAuth callback handoff could not be consumed; no authorization code was exchanged" | "MCP default endpoint request denied" | "MCP pool request denied" | "MCP agent endpoint request denied" | "OAuth error callback consent could not be consumed; no authorization code was exchanged" | "MCP handler error" | "operator status" | "operator agent slots read failed" | "result paging unavailable" | "native catalog unavailable" | "native name collision" | "native tools/list refused a pool over the 1,000-tool native cap" | "request failed" | "Clerk request denied" | "Clerk authentication failed";
|
|
114
114
|
/**
|
|
115
115
|
* Log one failure as `[connecta] <event>` and its record. A record
|
|
116
116
|
* `failureRecord` did not build is replaced, whatever its shape, by a fixed
|
package/dist/operator-record.js
CHANGED
|
@@ -25,6 +25,16 @@ export interface OperatorConnectorOverlay {
|
|
|
25
25
|
catalogAgeMs: number | null;
|
|
26
26
|
/** Latest visible call in the bounded activity window, or null when unknown. */
|
|
27
27
|
lastCall: OperatorLastCall | null;
|
|
28
|
+
/**
|
|
29
|
+
* Age of the stored status observation this row shows, when it came from
|
|
30
|
+
* storage rather than a live read. Its tools may be empty until a live read.
|
|
31
|
+
*/
|
|
32
|
+
observedAgeMs?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Browser-only: when that stored observation was made, in epoch
|
|
35
|
+
* milliseconds, fixed once as the row lands so its age keeps counting.
|
|
36
|
+
*/
|
|
37
|
+
observedAt?: number;
|
|
28
38
|
}
|
|
29
39
|
/** Whole connector access, or exact tool grants. `requireReadOnly` is always false since guarded grants were removed. */
|
|
30
40
|
export interface OperatorGrant {
|