@zackbart/connecta 0.40.0 → 0.42.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 +148 -0
- package/bin/connecta.mjs +26 -17
- package/bin/meta-tool-names.d.mts +3 -1
- package/bin/meta-tool-names.mjs +4 -1
- package/bin/version.mjs +1 -1
- package/dist/activity.d.ts +32 -5
- package/dist/activity.js +32 -6
- package/dist/advertised-schema.js +15 -0
- package/dist/agent-observation.js +24 -2
- package/dist/agent-routes.js +33 -44
- package/dist/auth/clerk.d.ts +6 -1
- package/dist/auth/clerk.js +16 -0
- package/dist/auth/cloudflare-access.js +49 -7
- package/dist/auth/downstream-oauth/consent.js +79 -29
- package/dist/auth/downstream-oauth/grant-store.js +58 -7
- package/dist/auth/downstream-oauth/refresh-coordinator.js +4 -1
- package/dist/auth/downstream-oauth/refresh-records.js +6 -5
- package/dist/auth/downstream-oauth.js +15 -4
- package/dist/auth/oauth-request-not-sent.js +4 -0
- package/dist/auth/static-oauth.js +77 -39
- package/dist/auth-elicitation.js +118 -61
- package/dist/authorization-handoff.js +36 -3
- package/dist/catalog-cache.js +352 -43
- package/dist/catalog-freshness.js +25 -0
- package/dist/catalog-service.js +408 -78
- package/dist/catalog.js +216 -62
- package/dist/config-defaults.d.ts +2 -1
- package/dist/config-defaults.js +4 -1
- package/dist/config.d.ts +53 -21
- package/dist/config.js +56 -17
- package/dist/connectors/remote-mcp/call.js +1 -1
- package/dist/connectors/remote-mcp/catalog.js +52 -28
- package/dist/connectors/remote-mcp/connection.js +113 -22
- package/dist/connectors/remote-mcp/oauth.js +2 -2
- package/dist/connectors/remote-mcp/resources.js +5 -2
- package/dist/connectors/remote-mcp/transport.js +24 -6
- package/dist/connectors/remote-mcp.js +14 -5
- package/dist/credential-contract.d.ts +3 -1
- package/dist/describe-config.d.ts +4 -2
- package/dist/describe-config.js +1 -0
- package/dist/discovery-health.js +26 -11
- package/dist/downstream-input.js +11 -2
- package/dist/errors.d.ts +60 -4
- package/dist/errors.js +88 -3
- package/dist/execute.js +491 -202
- package/dist/executor-admission.js +2 -0
- package/dist/executor-contract.js +6 -0
- package/dist/executor-result.js +3 -3
- package/dist/executors/quickjs-child.js +14 -5
- package/dist/executors/quickjs-runtime.d.ts +2 -0
- package/dist/executors/quickjs-runtime.js +37 -20
- package/dist/executors/quickjs.js +24 -7
- package/dist/guarded-transport.js +2 -0
- package/dist/guest-functions.js +5 -4
- package/dist/guest-runtime.js +37 -1
- package/dist/guest-types.d.ts +18 -0
- package/dist/http/client.js +14 -2
- package/dist/http/errors.js +9 -4
- package/dist/http/fields.js +17 -5
- package/dist/http/hatch.js +11 -3
- package/dist/http/node-transport.js +110 -4
- package/dist/http/oauth.js +17 -6
- package/dist/http/paging.js +1 -1
- package/dist/http/provider.js +2 -6
- package/dist/http/vendor.js +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -21
- package/dist/invocation-failure.js +28 -0
- package/dist/invocation.js +167 -64
- package/dist/membership.d.ts +1 -1
- package/dist/meta-output.js +24 -18
- package/dist/meta-tool-names.d.ts +1 -1
- package/dist/meta-tool-names.js +1 -1
- package/dist/meta-tools.js +286 -73
- package/dist/module-contracts.d.ts +3 -1
- package/dist/native-recovery.js +4 -1
- package/dist/native-tools.js +177 -55
- package/dist/node.d.ts +7 -0
- package/dist/node.js +85 -4
- package/dist/oauth-handoff.js +53 -43
- package/dist/operation-activity.js +104 -0
- package/dist/operation-vocabulary.js +6 -0
- package/dist/operator-record.d.ts +9 -1
- package/dist/operator-record.js +16 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/program-result.js +2 -0
- package/dist/program-source.js +1 -1
- package/dist/program-writes.js +112 -19
- package/dist/provider.d.ts +1 -14
- package/dist/provider.js +0 -3
- package/dist/providers/_shared/google/kit.js +2 -2
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/breeze/api.js +2 -2
- package/dist/providers/breeze/index.js +1 -1
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/ccb/api.js +2 -2
- package/dist/providers/ccb/index.js +1 -1
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/cloudflare/api.js +2 -2
- package/dist/providers/cloudflare/index.js +3 -1
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/index.js +1 -1
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/forms/index.js +2 -1
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/github/api.js +98 -6
- package/dist/providers/github/index.js +8 -3
- package/dist/providers/github/lib/auth.d.ts +3 -1
- package/dist/providers/github/lib/auth.js +43 -12
- package/dist/providers/github/lib/discovery.js +11 -1
- package/dist/providers/github/lib/operations.js +8 -0
- package/dist/providers/github/safety.js +39 -0
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +12 -5
- package/dist/providers/gmail/index.js +2 -1
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/granola/index.js +1 -1
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/index.js +3 -1
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/planning-center/index.js +12 -6
- 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/api.js +28 -25
- package/dist/providers/quickbooks/index.js +3 -6
- package/dist/providers/quickbooks/lib/writes.js +55 -0
- package/dist/providers/quickbooks/safety.js +17 -5
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/index.js +2 -1
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/slides/index.js +3 -3
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/stripe/index.js +2 -2
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/index.js +2 -1
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/vercel/api.js +3 -2
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/registry.d.ts +36 -4
- package/dist/registry.js +142 -30
- package/dist/resource-read.js +21 -2
- package/dist/result-preview.js +210 -0
- package/dist/result-seal.js +168 -0
- package/dist/route-names.js +53 -0
- package/dist/routes/activity-context.js +1 -0
- package/dist/routes/activity-disclosure.js +26 -4
- package/dist/routes/activity.js +2 -1
- package/dist/routes/connect.js +21 -13
- package/dist/routes/mcp.js +83 -35
- package/dist/routes/shared.d.ts +6 -0
- package/dist/routes/ui-config.js +1 -1
- package/dist/routes/ui-data.js +3 -2
- package/dist/runtime/call.js +15 -10
- package/dist/runtime/native-storage.js +12 -0
- package/dist/runtime/request-reads.js +66 -14
- package/dist/runtime/request-scope.js +38 -2
- package/dist/runtime/run.js +4 -1
- package/dist/sent-secrets.d.ts +20 -0
- package/dist/sent-secrets.js +49 -1
- package/dist/skill-origin.js +16 -0
- package/dist/skills.js +213 -57
- package/dist/storage/d1-driver.js +33 -5
- package/dist/storage/keys.js +16 -7
- package/dist/storage/sql-native.js +60 -3
- package/dist/storage/sql.js +6 -0
- package/dist/timing-trace.js +84 -0
- package/dist/tool-safety.d.ts +33 -0
- package/dist/tool-safety.js +51 -15
- package/dist/types.d.ts +36 -0
- package/dist/url-safety.js +38 -20
- package/dist/usage-guide.js +14 -13
- package/dist/utility-recovery.js +33 -0
- package/dist/validate.js +29 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +60 -15
- package/documentation/architecture.md +152 -43
- package/documentation/auth.md +160 -53
- package/documentation/code-mode.md +396 -119
- package/documentation/deploying.md +35 -10
- package/documentation/hosted-mcp-migration.md +9 -3
- package/documentation/integrating.md +27 -5
- package/documentation/meta-tools.md +761 -157
- package/documentation/operating.md +91 -4
- package/documentation/operator-ui.md +51 -2
- package/documentation/providers.md +34 -20
- package/examples/worker/README.md +3 -3
- package/guides/how-it-works.md +2 -1
- package/guides/safety-and-trust.md +1 -3
- package/package.json +10 -8
- package/templates/node/AGENTS.md +3 -3
- package/templates/node/README.md +1 -1
- package/templates/node/package.json +1 -1
package/dist/program-result.js
CHANGED
|
@@ -3,3 +3,5 @@ export const PROGRAM_RESULT_INLINE_BYTES = 1024 * 1024;
|
|
|
3
3
|
/** Leave room for the bridge envelope and six-character JSON escapes per byte. */
|
|
4
4
|
export const PROGRAM_RESULT_PAGE_BYTES = 32 * 1024;
|
|
5
5
|
export const PROGRAM_RESULT_WIRE_BYTES = 240 * 1024;
|
|
6
|
+
/** What `connecta.result(id, { all: true })` delivers unless the executor says less. */
|
|
7
|
+
export const PROGRAM_RESULT_READ_BYTES = 1.5 * 1024 * 1024;
|
package/dist/program-source.js
CHANGED
|
@@ -6,7 +6,7 @@ const FENCE = /^```[\w-]*\s*\n([\s\S]*?)\n?```$/;
|
|
|
6
6
|
/** Intake accepts one JavaScript fence, optionally surrounded by prose. */
|
|
7
7
|
const PROGRAM_FENCE = /^```(?:js|javascript)?[ \t]*\r?\n([\s\S]*?)\r?\n```[ \t]*$/gm;
|
|
8
8
|
/** Leading whitespace and comments, which may precede the arrow expression. */
|
|
9
|
-
const LEADING_TRIVIA = /^(?:\s+|\/\/[^\n]*|\/\*[\s\S]*?\*\/)+/;
|
|
9
|
+
export const LEADING_TRIVIA = /^(?:\s+|\/\/[^\n]*|\/\*[\s\S]*?\*\/)+/;
|
|
10
10
|
/** One character of an identifier, keyword, or number; `\\` for escapes. */
|
|
11
11
|
const WORD = /[\w$\\\u0080-\uffff]/;
|
|
12
12
|
/**
|
package/dist/program-writes.js
CHANGED
|
@@ -3,9 +3,35 @@ import { toolError } from "./tool-error.js";
|
|
|
3
3
|
// Await each dispatched write once; never conceal an unknown outcome.
|
|
4
4
|
import { CONFIG_DEFAULTS } from "./config-defaults.js";
|
|
5
5
|
import { Effect } from "effect";
|
|
6
|
+
import { clipText, MAX_TOOL_ERROR_BYTES } from "./errors.js";
|
|
6
7
|
import { classifyWriteOutcome } from "./write-outcome.js";
|
|
7
8
|
/** Writes one program may send (`execute.maxWrites`), unless configured. */
|
|
8
9
|
export const DEFAULT_MAX_WRITES = CONFIG_DEFAULTS.execute.maxWrites;
|
|
10
|
+
/** Unknown writes one run retains and reports. */
|
|
11
|
+
const MAX_REPORTED_WRITES = 10;
|
|
12
|
+
/** UTF-16 code units per write, so its UTF-8 size can reach three times this. */
|
|
13
|
+
const RETAINED_UNITS = 4 * MAX_TOOL_ERROR_BYTES;
|
|
14
|
+
/** Serialized bytes of the whole report: two full messages, or more shorter ones. */
|
|
15
|
+
const MAX_REPORT_BYTES = 2 * MAX_TOOL_ERROR_BYTES;
|
|
16
|
+
/** Room kept for what joins the result after the report: counters and a quoted address. */
|
|
17
|
+
const RESULT_RESERVE = 256;
|
|
18
|
+
const CHECK_UNKNOWN_WRITE = "Read its target to see whether it applied before anything depends on it.";
|
|
19
|
+
const UNKNOWN_WRITE_ARGS = "Echoed arguments may be partial or absent; use your original arguments for further calls.";
|
|
20
|
+
const NO_PROGRAM_RERUN = "Do not rerun this program: writes it already sent would repeat. If a tool's words call for a further call, send only that operation, as a new call.";
|
|
21
|
+
const encoder = new TextEncoder();
|
|
22
|
+
const serializedBytes = (value) => encoder.encode(JSON.stringify(value)).length;
|
|
23
|
+
/** `text` clipped so its JSON string fits `room` bytes, or nothing when not even the marker does. */
|
|
24
|
+
function fitted(text, room) {
|
|
25
|
+
let budget = room;
|
|
26
|
+
let clipped = clipText(text, budget);
|
|
27
|
+
for (let size = serializedBytes(clipped); size > room; size = serializedBytes(clipped)) {
|
|
28
|
+
if (budget <= 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
budget = Math.floor((budget * room) / size) - 1;
|
|
31
|
+
clipped = clipText(text, Math.max(0, budget));
|
|
32
|
+
}
|
|
33
|
+
return clipped;
|
|
34
|
+
}
|
|
9
35
|
/** What a dispatched write's outcome says about whether it landed. */
|
|
10
36
|
export function writeStateOf(outcome) {
|
|
11
37
|
const state = classifyWriteOutcome(outcome.ok
|
|
@@ -19,22 +45,33 @@ export function writeStateOf(outcome) {
|
|
|
19
45
|
if (state !== "unknown" || outcome.ok)
|
|
20
46
|
return state;
|
|
21
47
|
const { error } = outcome;
|
|
48
|
+
const text = outcome.failureText ?? error.message;
|
|
22
49
|
const handoff = (error.code === "auth_required" || error.code === "downstream_oauth_required") &&
|
|
23
50
|
error.nextAction &&
|
|
24
|
-
|
|
25
|
-
error.nextAction.tool === "authorize_connector"
|
|
51
|
+
error.connector !== undefined
|
|
26
52
|
? {
|
|
27
53
|
code: error.code,
|
|
28
|
-
|
|
54
|
+
connector: error.connector,
|
|
29
55
|
...(error.recovery !== undefined ? { recovery: error.recovery } : {}),
|
|
30
|
-
|
|
56
|
+
// This terminal result carries no handoff fields, so it never holds a link:
|
|
57
|
+
// the agent fetches one, whatever the call's own error was given.
|
|
58
|
+
nextAction: {
|
|
59
|
+
tool: "authorize_connector",
|
|
60
|
+
arguments: { connector: error.connector },
|
|
61
|
+
operatorHandoff: "Give the URL and instructions it returns to the operator.",
|
|
62
|
+
},
|
|
31
63
|
...(error.retry !== undefined ? { retry: error.retry } : {}),
|
|
32
64
|
}
|
|
33
65
|
: undefined;
|
|
34
|
-
if (!error.uncertainCall && !handoff)
|
|
35
|
-
return state;
|
|
36
66
|
return {
|
|
37
67
|
state,
|
|
68
|
+
failure: {
|
|
69
|
+
...(outcome.resolved ? { address: `${outcome.resolved.connector.id}.${outcome.resolved.toolName}` } : {}),
|
|
70
|
+
code: error.code,
|
|
71
|
+
...(error.toolAnswered ? { toolAnswered: true } : {}),
|
|
72
|
+
text: text.slice(0, RETAINED_UNITS),
|
|
73
|
+
cut: text.length > RETAINED_UNITS,
|
|
74
|
+
},
|
|
38
75
|
...(error.uncertainCall ? { uncertainCall: error.uncertainCall } : {}),
|
|
39
76
|
...(error.uncertainCall && error.details?.stage !== undefined
|
|
40
77
|
? {
|
|
@@ -61,6 +98,8 @@ export class ProgramWrites {
|
|
|
61
98
|
closed = false;
|
|
62
99
|
inFlight = new Set();
|
|
63
100
|
states = [];
|
|
101
|
+
unknownWrites = [];
|
|
102
|
+
unknownWritesTruncated = false;
|
|
64
103
|
uncertainCalls = [];
|
|
65
104
|
uncertainCallsTruncated = false;
|
|
66
105
|
deadlines = [];
|
|
@@ -90,6 +129,10 @@ export class ProgramWrites {
|
|
|
90
129
|
recorded = true;
|
|
91
130
|
const state = typeof completion === "string" ? completion : completion.state;
|
|
92
131
|
if (typeof completion !== "string") {
|
|
132
|
+
if (this.unknownWrites.length < MAX_REPORTED_WRITES)
|
|
133
|
+
this.unknownWrites.push(completion.failure);
|
|
134
|
+
else
|
|
135
|
+
this.unknownWritesTruncated = true;
|
|
93
136
|
if (completion.uncertainCall) {
|
|
94
137
|
if (this.uncertainCalls.length < 10)
|
|
95
138
|
this.uncertainCalls.push(completion.uncertainCall);
|
|
@@ -105,6 +148,10 @@ export class ProgramWrites {
|
|
|
105
148
|
done();
|
|
106
149
|
};
|
|
107
150
|
}
|
|
151
|
+
/** Whether this run dispatched any write, whatever became of it. */
|
|
152
|
+
get sent() {
|
|
153
|
+
return this.states.length > 0 || this.inFlight.size > 0;
|
|
154
|
+
}
|
|
108
155
|
/** Wait for dispatched writes, each bounded by its host-call deadline. */
|
|
109
156
|
drain() {
|
|
110
157
|
return Effect.promise(async () => {
|
|
@@ -112,7 +159,29 @@ export class ProgramWrites {
|
|
|
112
159
|
await Promise.all(this.inFlight);
|
|
113
160
|
});
|
|
114
161
|
}
|
|
115
|
-
|
|
162
|
+
/**
|
|
163
|
+
* What each unknown write said, checked against the run's final credentials and only
|
|
164
|
+
* then cut, so a credential a later call registered matches whole. A retained text
|
|
165
|
+
* was itself cut, so it also loses any unfinished tail. `room` bounds the serialized report.
|
|
166
|
+
*/
|
|
167
|
+
report(secrets, room) {
|
|
168
|
+
const entries = [];
|
|
169
|
+
let truncated = this.unknownWritesTruncated;
|
|
170
|
+
let left = room;
|
|
171
|
+
for (const { text, cut, ...facts } of this.unknownWrites) {
|
|
172
|
+
left -= serializedBytes(facts) + 12;
|
|
173
|
+
if (left < 0)
|
|
174
|
+
return { entries, truncated: true };
|
|
175
|
+
const message = fitted(cut ? secrets.redactOpenTail(text) : secrets.text(text), Math.min(left, MAX_TOOL_ERROR_BYTES));
|
|
176
|
+
if (message === undefined)
|
|
177
|
+
truncated = true;
|
|
178
|
+
else
|
|
179
|
+
left -= serializedBytes(message);
|
|
180
|
+
entries.push({ ...facts, ...(message === undefined ? {} : { message }) });
|
|
181
|
+
}
|
|
182
|
+
return { entries, truncated };
|
|
183
|
+
}
|
|
184
|
+
finish(result, secrets, maxResultChars) {
|
|
116
185
|
if (this.states.length === 0)
|
|
117
186
|
return result;
|
|
118
187
|
const writes = {
|
|
@@ -125,11 +194,8 @@ export class ProgramWrites {
|
|
|
125
194
|
// the result, as a direct call reports it: its code and handoff, never
|
|
126
195
|
// retryable, with the write's outcome unknown.
|
|
127
196
|
const handoff = this.handoff;
|
|
128
|
-
|
|
197
|
+
const facts = {
|
|
129
198
|
code: handoff?.code ?? "write_outcome_unknown",
|
|
130
|
-
message: handoff
|
|
131
|
-
? "A write this program sent failed authorization after it left, so whether it took effect is unknown, and that is the result rather than what the program returned. It will not be sent again. Check its target before doing anything that depends on it, and give nextAction to the operator to recover the connector. Argument echoes may be partial or absent; use the original arguments if reconciliation requires another call."
|
|
132
|
-
: "A write this program sent has no known outcome, so that is the result rather than what the program returned. It will not be sent again. Check its target before doing anything that depends on it. Argument echoes may be partial or absent; use the original arguments if reconciliation requires another call.",
|
|
133
199
|
retryable: false,
|
|
134
200
|
...(handoff ? { ...handoff, reconciliationRequired: true, writeOutcome: "unknown" } : {}),
|
|
135
201
|
...(this.deadlines.length > 0 ? { details: this.deadlines[0] } : {}),
|
|
@@ -141,6 +207,24 @@ export class ProgramWrites {
|
|
|
141
207
|
: {}),
|
|
142
208
|
...(this.uncertainCallsTruncated ? { uncertainCallsTruncated: true } : {}),
|
|
143
209
|
writes,
|
|
210
|
+
};
|
|
211
|
+
const advice = handoff
|
|
212
|
+
? `A write this program sent failed authorization after it left, so its outcome is unknown and the program's return was discarded. It will not be resent. ${CHECK_UNKNOWN_WRITE} Then give what nextAction returns to the operator to recover the connector. ${NO_PROGRAM_RERUN} ${UNKNOWN_WRITE_ARGS}`
|
|
213
|
+
: `A write this program sent has an unknown outcome, and the program's return was discarded. It will not be resent. ${CHECK_UNKNOWN_WRITE} ${NO_PROGRAM_RERUN} ${UNKNOWN_WRITE_ARGS}`;
|
|
214
|
+
const { entries, truncated } = this.report(secrets, Math.min(MAX_REPORT_BYTES, maxResultChars - RESULT_RESERVE - serializedBytes({ error: { ...facts, message: advice } })));
|
|
215
|
+
// The first tool's own words lead, once: they may be the only way forward. An
|
|
216
|
+
// authorization verdict keeps its lead, since recovery starts with the connector.
|
|
217
|
+
const quoted = handoff ? undefined : entries.find((entry) => entry.toolAnswered && entry.message !== undefined);
|
|
218
|
+
const words = quoted?.message;
|
|
219
|
+
if (quoted)
|
|
220
|
+
delete quoted.message;
|
|
221
|
+
return toolError({
|
|
222
|
+
message: quoted
|
|
223
|
+
? `${quoted.address ?? "A tool"} answered: ${words}\n\nGive the user any approval link or request id above. ${advice}`
|
|
224
|
+
: advice,
|
|
225
|
+
...(entries.length > 0 ? { unknownWrites: entries } : {}),
|
|
226
|
+
...(truncated ? { unknownWritesTruncated: true } : {}),
|
|
227
|
+
...facts,
|
|
144
228
|
});
|
|
145
229
|
}
|
|
146
230
|
return result.isError ? withWrites(result, writes) : result;
|
|
@@ -149,20 +233,29 @@ export class ProgramWrites {
|
|
|
149
233
|
/**
|
|
150
234
|
* An error result from a program that sent writes, carrying their counts: a
|
|
151
235
|
* program that fails after writing is not a program that is safe to repeat.
|
|
236
|
+
* Whatever ended the run, once a write has landed the program is not retryable
|
|
237
|
+
* and no wait window invites it (INV-9). A failed write is proven not applied,
|
|
238
|
+
* so failed writes alone leave the failure's own verdict standing.
|
|
152
239
|
*/
|
|
153
240
|
function withWrites(result, writes) {
|
|
154
241
|
const structured = result.structuredContent;
|
|
155
242
|
const error = structured?.error;
|
|
243
|
+
const { retryAfterMs, ...failure } = error !== null && typeof error === "object"
|
|
244
|
+
? error
|
|
245
|
+
: {
|
|
246
|
+
retryAfterMs: undefined,
|
|
247
|
+
code: "executor_failed",
|
|
248
|
+
message: result.content[0]?.text ?? "",
|
|
249
|
+
retryable: false,
|
|
250
|
+
};
|
|
251
|
+
const landed = writes.succeeded > 0;
|
|
156
252
|
const annotated = toolError({
|
|
157
|
-
...
|
|
158
|
-
|
|
159
|
-
: {
|
|
160
|
-
code: "executor_failed",
|
|
161
|
-
message: result.content[0]?.text ?? "",
|
|
162
|
-
retryable: false,
|
|
163
|
-
}),
|
|
253
|
+
...failure,
|
|
254
|
+
...(landed ? { retryable: false } : retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
164
255
|
writes,
|
|
165
|
-
note:
|
|
256
|
+
note: landed
|
|
257
|
+
? "This run completed writes, counted in writes, and they are not undone. Running this program again would repeat them: check them, then send only what is left."
|
|
258
|
+
: "This run attempted writes, counted in writes, and none applied. Check them before running the task again.",
|
|
166
259
|
});
|
|
167
260
|
if (structured && error !== null && typeof error === "object") {
|
|
168
261
|
const { error: _replaced, ...rest } = structured;
|
package/dist/provider.d.ts
CHANGED
|
@@ -89,8 +89,8 @@ export declare const PROVIDER_COMMON: {
|
|
|
89
89
|
};
|
|
90
90
|
instructions: Field<unknown, unknown, true>;
|
|
91
91
|
title: Field<unknown, unknown, true>;
|
|
92
|
-
authScope: Field<unknown, unknown, true>;
|
|
93
92
|
purpose: Field<unknown, unknown, true>;
|
|
93
|
+
authScope: Field<unknown, unknown, true>;
|
|
94
94
|
maxResultBytes: Field<unknown, unknown, true>;
|
|
95
95
|
};
|
|
96
96
|
/** The provider's maintained, connection-independent usage guide. */
|
|
@@ -127,17 +127,10 @@ export interface ProviderGuideBlocks<O> {
|
|
|
127
127
|
export interface ProviderKitFacts {
|
|
128
128
|
/** `manifest(vendor)`: every operation the provider's tools send, as `"METHOD /path"`. */
|
|
129
129
|
readonly operations: readonly string[];
|
|
130
|
-
/** The `limit` default and maximum every paged read declares. */
|
|
131
|
-
readonly pageSize?: {
|
|
132
|
-
readonly default: number;
|
|
133
|
-
readonly max: number;
|
|
134
|
-
};
|
|
135
130
|
/** The hatch's tool prefix and its reviewed verdict per `"METHOD /path"`. */
|
|
136
131
|
readonly hatch?: {
|
|
137
132
|
readonly name: string;
|
|
138
133
|
readonly verdicts: Readonly<Record<string, "read" | "write" | "destructive" | "refuse">>;
|
|
139
|
-
/** Separate-cursor paging opts into the generated hatch paging convention. */
|
|
140
|
-
readonly paging?: "argument" | "parameter";
|
|
141
134
|
readonly fieldsRoot?: string;
|
|
142
135
|
};
|
|
143
136
|
/**
|
|
@@ -152,12 +145,6 @@ export interface ProviderKitFacts {
|
|
|
152
145
|
};
|
|
153
146
|
/** Whether a write takes an idempotency key the vendor honors, checked against the built tools. */
|
|
154
147
|
readonly idempotencyKey?: boolean;
|
|
155
|
-
/**
|
|
156
|
-
* Set only when the hatch refuses a write carrying a withheld placeholder
|
|
157
|
-
* value; the Withheld values convention promises exactly that. The kit sets
|
|
158
|
-
* it for its standard hatch; kit write builders refuse the same markers.
|
|
159
|
-
*/
|
|
160
|
-
readonly refusesWithheld?: true;
|
|
161
148
|
}
|
|
162
149
|
/** Connection facts a provider renders around its maintained guide. */
|
|
163
150
|
export interface ProviderGuideInput {
|
package/dist/provider.js
CHANGED
|
@@ -112,13 +112,11 @@ function kitFacts(kit) {
|
|
|
112
112
|
}
|
|
113
113
|
return Object.freeze({
|
|
114
114
|
operations: Object.freeze([...kit.operations]),
|
|
115
|
-
...(kit.pageSize ? { pageSize: Object.freeze({ ...kit.pageSize }) } : {}),
|
|
116
115
|
...(kit.hatch
|
|
117
116
|
? {
|
|
118
117
|
hatch: Object.freeze({
|
|
119
118
|
name: kit.hatch.name,
|
|
120
119
|
verdicts: Object.freeze({ ...kit.hatch.verdicts }),
|
|
121
|
-
...(kit.hatch.paging ? { paging: kit.hatch.paging } : {}),
|
|
122
120
|
...(kit.hatch.fieldsRoot ? { fieldsRoot: kit.hatch.fieldsRoot } : {}),
|
|
123
121
|
}),
|
|
124
122
|
}
|
|
@@ -127,7 +125,6 @@ function kitFacts(kit) {
|
|
|
127
125
|
? { errors: Object.freeze({ ...kit.errors, conflicts: Object.freeze([...kit.errors.conflicts]) }) }
|
|
128
126
|
: {}),
|
|
129
127
|
...(kit.idempotencyKey !== undefined ? { idempotencyKey: kit.idempotencyKey } : {}),
|
|
130
|
-
...(kit.refusesWithheld ? { refusesWithheld: true } : {}),
|
|
131
128
|
});
|
|
132
129
|
}
|
|
133
130
|
/**
|
|
@@ -9,7 +9,7 @@ import { asRecord, asArray } from "../json.js";
|
|
|
9
9
|
// bytes, and failure phases for product recovery.
|
|
10
10
|
import { Effect, Option } from "effect";
|
|
11
11
|
import { deriveContext } from "../../../derived-context.js";
|
|
12
|
-
import { ConnectorCallError } from "../../../errors.js";
|
|
12
|
+
import { ConnectorCallError, tokenPrefix } from "../../../errors.js";
|
|
13
13
|
import { Refusal, StatusFailure, toConnectorCallError, } from "../../../http/errors.js";
|
|
14
14
|
import { Classification, Dispatches, Signal } from "../../../http/services.js";
|
|
15
15
|
import { callMemo, Operation } from "../../../runtime/call.js";
|
|
@@ -65,7 +65,7 @@ export function googleErrors(product, options = {}) {
|
|
|
65
65
|
detail: ({ status, body }) => {
|
|
66
66
|
const message = asRecord(asRecord(body)["error"])["message"];
|
|
67
67
|
// The body arrives redacted whole, before anything here cuts it.
|
|
68
|
-
const said = typeof message === "string" ? message.trim()
|
|
68
|
+
const said = typeof message === "string" ? tokenPrefix(message.trim(), 500) : "";
|
|
69
69
|
return said ? `${provider}: ${said}${/[.!?]$/.test(said) ? "" : "."}` : `${provider} returned HTTP ${status}.`;
|
|
70
70
|
},
|
|
71
71
|
classify: [
|
|
@@ -92,8 +92,8 @@ export declare const GOOGLE_WORKSPACE_OPTIONS: import("../../../config-schema.js
|
|
|
92
92
|
};
|
|
93
93
|
instructions: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
94
94
|
title: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
95
|
-
baseUrl: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
96
95
|
purpose: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
96
|
+
baseUrl: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
97
97
|
maxResultBytes: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
98
98
|
readOnly: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
99
99
|
subject: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
@@ -3,7 +3,7 @@ import { pathWithholds } from "../_shared/rest/value-safety.js";
|
|
|
3
3
|
import { asRecord } from "../_shared/json.js";
|
|
4
4
|
// Breeze's reviewed GET-only vendor. HTTP methods do not establish safety: see tools.ts.
|
|
5
5
|
import { Effect, Schema } from "effect";
|
|
6
|
-
import { ConnectorCallError, resendAdvice } from "../../errors.js";
|
|
6
|
+
import { ConnectorCallError, resendAdvice, tokenPrefix } from "../../errors.js";
|
|
7
7
|
import { Refusal } from "../../http/errors.js";
|
|
8
8
|
import { Credentials, RequestSecrets } from "../../http/services.js";
|
|
9
9
|
import { get, Summary, vendor } from "../../http/vendor.js";
|
|
@@ -555,7 +555,7 @@ function shortText(value, secrets) {
|
|
|
555
555
|
// The kit redacted the answer's values and kept its keys; serialized, a key is text
|
|
556
556
|
// the cut below could split, so the whole text is checked before it is cut.
|
|
557
557
|
const text = secrets.redact(JSON.stringify(value));
|
|
558
|
-
return text.length > 300 ? `${text
|
|
558
|
+
return text.length > 300 ? `${tokenPrefix(text, 300)}…` : text;
|
|
559
559
|
}
|
|
560
560
|
/**
|
|
561
561
|
* Breeze can report a failure inside a 200: `{ success: false, errors: … }`
|
|
@@ -75,7 +75,7 @@ export const breeze = defineApiProvider({
|
|
|
75
75
|
? [`Paged lists return ${options.defaultPageSize} items by default on this connection.`]
|
|
76
76
|
: []),
|
|
77
77
|
],
|
|
78
|
-
summary: "
|
|
78
|
+
summary: "Load for Breeze people, giving records, paging and guarded updates.",
|
|
79
79
|
required: true,
|
|
80
80
|
}),
|
|
81
81
|
hatch: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Church records\n\nBreeze ChMS (now Tithely Church Management) holds people, tags, events,\nattendance, forms, volunteers and giving. People carry contact information,\nbirthdates and family ties; giving reveals donors and amounts. Reduce records\nin code and return only the names, counts or totals the task needs.\n\n## Ids and joins\n\nIds are numeric strings. Resolve them from the relevant inventory before\nwriting. Events have an occurrence id and a series `eventId`; attendance,\nvolunteers and check-in use the occurrence id. Event and pledge lists can lag\nup to 15 minutes behind the site. An all-zero MySQL date means no time was\nrecorded, including no check-out.\n\nProfile `details` use field ids, not names. Resolve field and option ids from\n`list_profile_fields`; multiple-choice filters join option ids with `-`.\nA person-name lookup requires listing people and matching in code. Form entry\nresponses similarly join to that form's field ids.\n\n## Giving\n\nGiving, funds and pledges use archival endpoints removed from the public\nreference in 2023. They can change or vanish without notice. Amounts are\nexact decimal strings: reduce them with integer-cent arithmetic, and read the\ncurrency from `get_account_summary`. A split gift's fund amounts belong to\nits fund ids. Tithe.ly deposits sync in batches, so recent gifts can be absent.\nEditing a giving transaction may return a new payment id.\nUse `limit: 1000` when reducing broad giving ranges: each local page refetches\nthe bounded vendor list, so larger windows avoid repeated full-list fetches.\n\n## Required sequences\n\nCheck for an existing person before adding one: creation does not deduplicate.\nResolve profile fields before updating them. Text, dates (M/D/YYYY) and option\nids use `response`; email, phone, address and family-role values use `details`.\nNamed person writes take these entries in `values`.\nFamily changes can remove previous associations. Attendance check-out records\na departure time; removing attendance deletes the record instead.\n\nBreeze sends writes as GET requests too. Query names ending in `_json` take\nJSON-encoded strings. The named person writes encode their typed profile values\nfor you. The account-log `object_json` is a serialized affected-object value;\nits free-text description has no stable schema. Events and account logs have\nvendor caps: narrow the date range when the result fills its limit.\n\n## Conventions\n\n- **
|
|
3
|
+
"content": "## Church records\n\nBreeze ChMS (now Tithely Church Management) holds people, tags, events,\nattendance, forms, volunteers and giving. People carry contact information,\nbirthdates and family ties; giving reveals donors and amounts. Reduce records\nin code and return only the names, counts or totals the task needs.\n\n## Ids and joins\n\nIds are numeric strings. Resolve them from the relevant inventory before\nwriting. Events have an occurrence id and a series `eventId`; attendance,\nvolunteers and check-in use the occurrence id. Event and pledge lists can lag\nup to 15 minutes behind the site. An all-zero MySQL date means no time was\nrecorded, including no check-out.\n\nProfile `details` use field ids, not names. Resolve field and option ids from\n`list_profile_fields`; multiple-choice filters join option ids with `-`.\nA person-name lookup requires listing people and matching in code. Form entry\nresponses similarly join to that form's field ids.\n\n## Giving\n\nGiving, funds and pledges use archival endpoints removed from the public\nreference in 2023. They can change or vanish without notice. Amounts are\nexact decimal strings: reduce them with integer-cent arithmetic, and read the\ncurrency from `get_account_summary`. A split gift's fund amounts belong to\nits fund ids. Tithe.ly deposits sync in batches, so recent gifts can be absent.\nEditing a giving transaction may return a new payment id.\nUse `limit: 1000` when reducing broad giving ranges: each local page refetches\nthe bounded vendor list, so larger windows avoid repeated full-list fetches.\n\n## Required sequences\n\nCheck for an existing person before adding one: creation does not deduplicate.\nResolve profile fields before updating them. Text, dates (M/D/YYYY) and option\nids use `response`; email, phone, address and family-role values use `details`.\nNamed person writes take these entries in `values`.\nFamily changes can remove previous associations. Attendance check-out records\na departure time; removing attendance deletes the record instead.\n\nBreeze sends writes as GET requests too. Query names ending in `_json` take\nJSON-encoded strings. The named person writes encode their typed profile values\nfor you. The account-log `object_json` is a serialized affected-object value;\nits free-text description has no stable schema. Events and account logs have\nvendor caps: narrow the date range when the result fills its limit.\n\n## Conventions\n\n- **Hatch:** `breeze_api_search` finds operations, `breeze_api_details` shows one contract, `breeze_api_read` calls a reviewed read and `breeze_api_write` a reviewed write.\n- **Errors:** `not_found` means the record does not exist; this API tells absence from a permission gap.",
|
|
4
4
|
"instructionsHeading": "Church instructions"
|
|
5
5
|
};
|
|
@@ -4,7 +4,7 @@ import { CCB_MEDIA_TYPE, CCB_ENVIRONMENTS, CCB_READ_SCOPES, CCB_WRITE_SCOPES, CC
|
|
|
4
4
|
// Pushpay ChMS v2 JSON contracts, OAuth, status meanings, admission and privacy.
|
|
5
5
|
// The legacy per-church XML API is outside this provider's scope.
|
|
6
6
|
import { Effect, Schema } from "effect";
|
|
7
|
-
import { ConnectorCallError } from "../../errors.js";
|
|
7
|
+
import { ConnectorCallError, tokenPrefix } from "../../errors.js";
|
|
8
8
|
import { OperationIndex } from "../_shared/rest/operation-index.js";
|
|
9
9
|
import { checked, indexedVendor } from "../_shared/rest/kit.js";
|
|
10
10
|
import { valueSafety } from "../_shared/rest/value-safety.js";
|
|
@@ -432,7 +432,7 @@ function errorDetail(payload, status) {
|
|
|
432
432
|
parts.push(message.trim());
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
|
-
const detail = parts.join("; ")
|
|
435
|
+
const detail = tokenPrefix(parts.join("; "), 500);
|
|
436
436
|
return detail ? `CCB HTTP ${status}: ${detail}${/[.!?]$/.test(detail) ? "" : "."}` : `CCB returned HTTP ${status}.`;
|
|
437
437
|
}
|
|
438
438
|
export const errors = {
|
|
@@ -81,7 +81,7 @@ export const ccb = defineApiProvider({
|
|
|
81
81
|
? [`Named lists default to ${options.defaultPerPage} rows on this connection.`]
|
|
82
82
|
: []),
|
|
83
83
|
],
|
|
84
|
-
summary:
|
|
84
|
+
summary: "Load for CCB person ids, sensitive data, paging, rate limits and guarded writes.",
|
|
85
85
|
required: true,
|
|
86
86
|
}),
|
|
87
87
|
pageSize: { default: 25, max: 100 },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## People, households and groups\n\nCCB v2 addresses records by integer ids. Resolve a person from the individual\nlist and a household from families; never guess ids. Family membership and\nchurch group membership join by individual id. A membership role belongs to\nthat group, rather than being a property of the person everywhere.\n\nIdentity Auth acts with the signed-in person's permissions. A permission\nrefusal can mean that person lacks access even when the connection is healthy.\n\n## Events and attendance\n\nAn event is a series; its dated occurrence is a separate identity. Attendance\njoins need both the event id and occurrence. Preserve the vendor's date and\ntime-zone meaning instead of treating every occurrence as UTC midnight.\n\n## Pastoral and financial records\n\nNotes, background checks, pledges and giving metrics can be sensitive. Read\nonly what the task needs and reduce those records inside execute_code before\nreturning an answer. Gift metrics are counts by period, with a range of at\nmost twelve months. Per-gift transactions and amounts belong to the legacy\nv1 XML API, which this v2 connector does not reach.\n\n## Vendor call rates and recovery\n\nCCB meters endpoints separately per API client. Individual profile reads have\nno burst; individual advanced searches allow one call per five seconds, and\ncategory schedule lists allow one per two seconds. Prefer list pages over a\nloop of individual gets. Other deployments sharing the client spend from the\nsame vendor allowance.\n\nFor a profile edit, resolve the individual first and preserve fields outside\nthe requested change. Use `ccb_api_write` for the edit; the host may ask the\nuser to confirm. After an ambiguous update, read that profile to determine\nwhat actually changed before deciding whether another write is needed.\n\n## Conventions\n\n- **
|
|
3
|
+
"content": "## People, households and groups\n\nCCB v2 addresses records by integer ids. Resolve a person from the individual\nlist and a household from families; never guess ids. Family membership and\nchurch group membership join by individual id. A membership role belongs to\nthat group, rather than being a property of the person everywhere.\n\nIdentity Auth acts with the signed-in person's permissions. A permission\nrefusal can mean that person lacks access even when the connection is healthy.\n\n## Events and attendance\n\nAn event is a series; its dated occurrence is a separate identity. Attendance\njoins need both the event id and occurrence. Preserve the vendor's date and\ntime-zone meaning instead of treating every occurrence as UTC midnight.\n\n## Pastoral and financial records\n\nNotes, background checks, pledges and giving metrics can be sensitive. Read\nonly what the task needs and reduce those records inside execute_code before\nreturning an answer. Gift metrics are counts by period, with a range of at\nmost twelve months. Per-gift transactions and amounts belong to the legacy\nv1 XML API, which this v2 connector does not reach.\n\n## Vendor call rates and recovery\n\nCCB meters endpoints separately per API client. Individual profile reads have\nno burst; individual advanced searches allow one call per five seconds, and\ncategory schedule lists allow one per two seconds. Prefer list pages over a\nloop of individual gets. Other deployments sharing the client spend from the\nsame vendor allowance.\n\nFor a profile edit, resolve the individual first and preserve fields outside\nthe requested change. Use `ccb_api_write` for the edit; the host may ask the\nuser to confirm. After an ambiguous update, read that profile to determine\nwhat actually changed before deciding whether another write is needed.\n\n## Conventions\n\n- **Hatch:** `ccb_api_search` finds operations, `ccb_api_details` shows one contract, `ccb_api_read` calls a reviewed read and `ccb_api_write` a reviewed write. 14 operations are refused by design; use `ccb_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Errors:** `not_found` means the record does not exist; this API tells absence from a permission gap.",
|
|
4
4
|
"instructionsHeading": "Church instructions"
|
|
5
5
|
};
|
|
@@ -4,7 +4,7 @@ import { responseMedia } from "../_shared/rest/response-media.js";
|
|
|
4
4
|
import { EXCLUSIVE_PATH_IDS } from "./lib/path-bounds.js";
|
|
5
5
|
import { RESPONSE_MEDIA } from "./lib/response-media.js";
|
|
6
6
|
import { Effect, Option, Schema } from "effect";
|
|
7
|
-
import { ConnectorCallError } from "../../errors.js";
|
|
7
|
+
import { ConnectorCallError, tokenPrefix } from "../../errors.js";
|
|
8
8
|
import { Refusal, StatusFailure, toConnectorCallError, } from "../../http/errors.js";
|
|
9
9
|
import { Budget, Credentials, Classification, Dispatches, Signal } from "../../http/services.js";
|
|
10
10
|
import { restSafety } from "../../http/safety.js";
|
|
@@ -236,7 +236,7 @@ export function describeErrors(errors) {
|
|
|
236
236
|
continue;
|
|
237
237
|
}
|
|
238
238
|
const code = typeof entry.code === "number" ? entry.code : undefined;
|
|
239
|
-
const message = typeof entry.message === "string"
|
|
239
|
+
const message = (typeof entry.message === "string" && tokenPrefix(entry.message, 500)) || "Unknown error";
|
|
240
240
|
parts.push(code === undefined ? message : `${code}: ${message}`);
|
|
241
241
|
if (Array.isArray(entry.error_chain))
|
|
242
242
|
walk(entry.error_chain);
|
|
@@ -75,7 +75,7 @@ function usageGuide(options) {
|
|
|
75
75
|
const { auth, accountId, zoneId, pin } = settings(options);
|
|
76
76
|
return {
|
|
77
77
|
heading: "Cloudflare usage",
|
|
78
|
-
summary: "
|
|
78
|
+
summary: "Load for Cloudflare DNS, Workers, R2, KV and analytics; resolve account and zone ids before calls.",
|
|
79
79
|
context: [
|
|
80
80
|
`Connector purpose: ${options.purpose}`,
|
|
81
81
|
`This connection already has an operator-configured ${auth === "apiToken" ? "scoped API token" : "Global API Key"}; there is no sign-in step.`,
|
|
@@ -116,6 +116,8 @@ export const cloudflare = defineApiProvider({
|
|
|
116
116
|
defaultCallAdmission: (options) => admissionPolicy(options.maxConcurrency ?? 6),
|
|
117
117
|
credential: (options) => (options.auth.type === "globalApiKey" ? GLOBAL_API_KEY_CREDENTIAL : API_TOKEN_CREDENTIAL),
|
|
118
118
|
authenticate,
|
|
119
|
+
// The Global API Key scheme's account email names the account; X-Auth-Key proves it.
|
|
120
|
+
publicIdentifierHeaders: ["x-auth-email"],
|
|
119
121
|
errors: cloudflareErrors,
|
|
120
122
|
inBandRefusals: true,
|
|
121
123
|
idempotencyKey: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Accounts, domains and records\n\nResolve account and zone ids before acting. A zone is a domain, found by its name;\nits account id joins it to the account inventory. Cloudflare ids are normally\n32-character hex strings. A default id routes a request; a pin limits its scope.\n\nDNS TTL is in seconds; TTL 1 means automatic. Read the record before changing it.\nPATCH changes supplied fields; PUT replaces the record, so preserve its type,\nname and content. Read state before replacing, rolling back or purging. Prefer\na targeted cache purge by files, tags, hosts or prefixes over purging everything.\n\
|
|
3
|
+
"content": "## Accounts, domains and records\n\nResolve account and zone ids before acting. A zone is a domain, found by its name;\nits account id joins it to the account inventory. Cloudflare ids are normally\n32-character hex strings. A default id routes a request; a pin limits its scope.\n\nDNS TTL is in seconds; TTL 1 means automatic. Read the record before changing it.\nPATCH changes supplied fields; PUT replaces the record, so preserve its type,\nname and content. Read state before replacing, rolling back or purging. Prefer\na targeted cache purge by files, tags, hosts or prefixes over purging everything.\n\nA hatch list stays under `data.result`. Named account and zone lists use camelCase\naccount and name-server fields.\n\n## Workers, storage and analytics\n\nWorker script source is code; secret binding values are separate and unavailable.\nKV values are application data. Encode a slash inside a KV key or R2 object key\nas `%2F`. R2 requires API-token authentication, rather than a Global API Key.\n\nGraphQL Analytics uses accountTag and zoneTag for account/zone identities.\nFilter every accounts/zones field by equality or an explicit set of those ids.\nA pinned query uses one operation per document and every declared fragment.\nAnalytics Engine SQL is read-only; D1 SQL and Durable Object queries can write.\nAI inference, previews and outbound checks can cost money or activate work.\n\n## Disqualifiers and recovery\n\nCredential minting, rotation, stored secrets, private session captures and signed\nupload or deployment links are unavailable. Use the Cloudflare dashboard for\ncredential administration. A Global API Key cannot change user settings or\nmemberships through this connection.\n\nAfter an authentication failure, use `verify_credential` once to distinguish a\nrejected credential from a permission gap. An operator replaces the credential;\nrepeating the failed request does not add permissions. Cloudflare's global limit\ncounts dashboard traffic and all tokens together, so reduce requests as well as\nhonoring the returned wait.\n\n## Conventions\n\n- **Envelope fields:** hatch data keeps the vendor envelope: select `result.id`, or `result[].id` across a list. Named reads keep their projected fields.\n- **Hatch:** `cloudflare_api_search` finds operations, `cloudflare_api_details` shows one contract, `cloudflare_api_read` calls a reviewed read and `cloudflare_api_write` a reviewed write. 70 operations are refused by design; use `cloudflare_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Errors:** `not_found` means the record does not exist; this API tells absence from a permission gap.",
|
|
4
4
|
"instructionsHeading": "Account instructions"
|
|
5
5
|
};
|
|
@@ -101,7 +101,7 @@ export const docs = defineApiProvider({
|
|
|
101
101
|
"Workspace delegation is configured by the deployment. Each call acts as the mapped Workspace user; document access follows that user's Drive sharing.",
|
|
102
102
|
...(options.readOnly ? ["This connection is read-only."] : []),
|
|
103
103
|
],
|
|
104
|
-
summary: "
|
|
104
|
+
summary: "Load for Docs tabs, UTF-16 edit indexes, revision checks and partial-create recovery.",
|
|
105
105
|
required: true,
|
|
106
106
|
}),
|
|
107
107
|
hatch: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Find the document first\n\nDocument ids join Docs to Drive: take the part after `/d/` in a document URL,\nor find the file by name through a Google Drive connection. An id the person\nprovides works as is. Docs has no\naccount-data search or listing; its operation search discovers API methods.\nA missing document may be absent or inaccessible to the mapped Workspace user.\n\n## Read before editing\n\nThe rendering follows document tabs, including nested tabs. Headers, footers,\nfloating images and comments are not rendered; `notRendered` names omitted\nkinds. Suggested insertions and deletions can appear together without marks.\nAn accepted or rejected suggestions preview cannot supply edit indexes.\nIndexed rows page with `page.nextCursor`; keep the same arguments, starting\nwith `maxChars: 0` when only indexes are needed.\n\nUse the inline view's UTF-16 start/end indexes and revision to plan edits.\nIndexes shift after each edit, including earlier requests in the same atomic\nbatch. Apply independent deletions from the end backwards. A revision is a\nprecondition, never an idempotency key. Re-read and recompute after a revision\nconflict.\n\nStyles, named ranges, headers and footers live in the complete document\nresource. Read it with\n`docs_api_read({path:\"/documents
|
|
3
|
+
"content": "## Find the document first\n\nDocument ids join Docs to Drive: take the part after `/d/` in a document URL,\nor find the file by name through a Google Drive connection. An id the person\nprovides works as is. Docs has no\naccount-data search or listing; its operation search discovers API methods.\nA missing document may be absent or inaccessible to the mapped Workspace user.\n\n## Read before editing\n\nThe rendering follows document tabs, including nested tabs. Headers, footers,\nfloating images and comments are not rendered; `notRendered` names omitted\nkinds. Suggested insertions and deletions can appear together without marks.\nAn accepted or rejected suggestions preview cannot supply edit indexes.\nIndexed rows page with `page.nextCursor`; keep the same arguments, starting\nwith `maxChars: 0` when only indexes are needed.\n\nUse the inline view's UTF-16 start/end indexes and revision to plan edits.\nIndexes shift after each edit, including earlier requests in the same atomic\nbatch. Apply independent deletions from the end backwards. A revision is a\nprecondition, never an idempotency key. Re-read and recompute after a revision\nconflict.\n\nStyles, named ranges, headers and footers live in the complete document\nresource. Read it with\n`docs_api_read({path:\"/documents/<id>\",query:{includeTabsContent:true}})`\nwhen a rendering cannot answer the task. Paths omit `/v1`; the hatch reply is\n`{status,data}`, with the vendor document in `data` (a program call's\n`delivery.data.data`). A Docs\ncreate payload sets a title only; initial text is a separate edit. Appending\njoins the final paragraph, so begin with a newline when a new paragraph is\nintended. A broad text replacement can rewrite every tab.\nBatch kinds include `updateTextStyle` for formatting, `insertTable`,\n`createParagraphBullets`, and `deleteContentRange`.\n\n## Recover a partial create\n\nIf creation succeeded but initial text failed, keep the created document id.\nAppend the missing text directly only when the failure proves Google refused\nit. Otherwise read that document and add only what is missing. Creating again\nwould make another document. If the create reply lost its id, search Drive for\nthe title before attempting a fresh create.\n\nPreview comment and suggestion mutations are unavailable. Comments belong to\nDrive. Copying a template or choosing a destination folder also belongs to\nDrive; a Docs create starts in My Drive's root.\n\n## Conventions\n\n- **Hatch:** `docs_api_search` finds operations, `docs_api_details` shows one contract, `docs_api_read` calls a reviewed read and `docs_api_write` a reviewed write.\n- **Errors:** An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. `conflict` means: A revision-guarded batch update returned ABORTED or FAILED_PRECONDITION with HTTP 400 or 409; The document content or revision changed during indexed content paging.",
|
|
4
4
|
"instructionsHeading": "Connection instructions"
|
|
5
5
|
};
|
|
@@ -105,7 +105,7 @@ export const drive = defineApiProvider({
|
|
|
105
105
|
`Connection purpose: ${options.purpose}`,
|
|
106
106
|
"Acts as the Workspace user deployment config maps from the admitted caller or pool owner. No caller argument selects an account. An unmapped or ownerless caller has no access unless the deployment fixes a shared account.",
|
|
107
107
|
],
|
|
108
|
-
summary: "
|
|
108
|
+
summary: "Load for Drive file discovery, bounded content, recoverable trash and sharing.",
|
|
109
109
|
required: true,
|
|
110
110
|
}),
|
|
111
111
|
tools: driveTools,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Finding files and joining Workspace products\n\nDrive searches file data; operation discovery searches API capabilities.\nUse file ids from reads, Drive URLs or the person: an id they give
|
|
3
|
+
"content": "## Finding files and joining Workspace products\n\nDrive searches file data; operation discovery searches API capabilities.\nUse file ids from reads, Drive URLs or the person: an id they give works as is.\nThe same id identifies a Google Doc, Sheet, Slides deck or Form in its product\nconnector. Shared-drive ids identify\nmembership and the root folder; folder parents determine inherited access.\nA missing file and a file the delegated user cannot see fail alike.\n\nDrive query values use single quotes: `name contains 'budget'`,\n`mimeType = 'application/pdf'`, `'folder_123' in parents`, or\n`modifiedTime > '2026-01-01T00:00:00'`. Trashed files are excluded by default.\n`incompleteSearch: true` means Google did not search every corpus; narrow the\nquery or choose a single shared drive. It is never an exact count.\n\n`maxBytes` is the byte budget and `limit` is rows per page; keep a high limit\nfor large folders and continue with the same listing arguments. Named `fields`\nmay narrow and `maxBytes` may change between pages. Hatch paths omit `/drive/v3`.\n\n## Content and file decisions\n\nThe content workflow exports Docs as Markdown, Sheets as CSV of the first\nsheet only, and Slides as text. Other text files retain their declared charset.\nSmall binary files can be returned as base64. A drawing, unsupported native\nfile, folder, shortcut or oversized binary returns an unavailable explanation.\nFor a shortcut, read its target id instead. Drive exports at most 10 MB.\n\nContent containing sent credentials is withheld as a whole binary value; there\nis no partial-content recovery around that boundary. Withheld MIME, shortcut,\nfolder or parent metadata cannot decide a subsequent read or move. Re-read\nthrough a safe source before trying the workflow again.\n\n## Access and recovery\n\nA file or copy inherits its destination folder's access. Read permission holders\nbefore moving, copying or creating in a shared folder, and read them again after\nchanging access. Inherited permissions name the parent folder or shared drive\nthat must be changed instead. An owner cannot be removed or replaced here.\n\nAll new grants use `share_file`, which sends no notification email by default.\nConfirm intent before granting `anyone` or domain access. Permission creation\nthrough the hatch is refused, including public-link grants; the named workflow\nowns grantee validation and notification behavior.\n\nContent updates replace the complete content. A create with an uncertain outcome\nneeds a search for the intended file before another create. Recoverable trash\ncan be restored for 30 days. Permanent deletion, ownership transfer, watch\nregistration, credential minting and generic media transport are outside the\nhatch. Use the named content and upload workflows for bounded bytes.\n\n## Conventions\n\n- **Hatch:** `drive_api_search` finds operations, `drive_api_details` shows one contract, `drive_api_read` calls a reviewed read and `drive_api_write` a reviewed write. 51 operations are refused by design; use `drive_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Errors:** An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. `conflict` means: A Drive source page changed before its local cursor resumed.",
|
|
4
4
|
"instructionsHeading": "Connection instructions"
|
|
5
5
|
};
|
|
@@ -94,6 +94,7 @@ export const forms = defineApiProvider({
|
|
|
94
94
|
errors,
|
|
95
95
|
safety: () => safety,
|
|
96
96
|
probe,
|
|
97
|
+
blocks: { keys: ["writes"], select: (options) => (options.readOnly ? [] : ["writes"]) },
|
|
97
98
|
guide: (options) => ({
|
|
98
99
|
context: [
|
|
99
100
|
`Purpose: ${options.purpose}`,
|
|
@@ -101,7 +102,7 @@ export const forms = defineApiProvider({
|
|
|
101
102
|
...(options.readOnly ? ["This connection is read-only."] : []),
|
|
102
103
|
],
|
|
103
104
|
instructions: options.instructions,
|
|
104
|
-
summary: "
|
|
105
|
+
summary: "Load for form questions, labeled responses and revision-checked batch edits.",
|
|
105
106
|
// Required: whose forms they are, that Drive lists them, and the
|
|
106
107
|
// revision-checked edit sequence are conventions no schema can carry.
|
|
107
108
|
required: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Finding forms and joining answers\n\nThe Forms API cannot list or search forms. Find the file through Drive, or use\nthe id after `/forms/d/` in its edit URL. The `/forms/d/e/…` responder link is\nnot a form id. A form id is also its Drive file id.\n\nA form contains items; an item may ask one question, several grid questions,\nor no question. Responses join by `questionId`, never by `itemId`. Named\nresponse reads label answers with the current question titles in form order;\nan answer to a deleted question has no title. A response may contain several\nvalues or uploaded-file references for one question. File ids join to Drive.\n\n## Reading complete responses\n\nResponses and respondent emails can contain personal information. Filter and\naggregate in `execute_code`, returning named answers only when the task needs\nthem. `submittedAfter` is exclusive UTC: retain the newest `lastSubmittedTime`\nfor the next incremental read. Google does not promise a stable response\nsnapshot. A continuation inside a changed vendor page fails `conflict`; start\nagain or use that newest timestamp.\n\nLarge grids continue inside the same item: `questionsFrom` places the returned\nrows. `omittedQuestionIds` identifies answers left out for size; read those ids\nwith `get_response`. The structural hatch exposes quiz grading and correct\nanswers, images, and option navigation that named projections omit. Temporary\nimage access destinations remain withheld.\n\n## Editing without losing structure\n\nRead the form's current revision before editing. A stale revision refuses the\nbatch as `conflict`; re-read and rebuild the requested edit. A revision guards\nconcurrent changes and does not make a repeated write safe.\n\nCreation sets only title and Drive file name. Add descriptions or questions\nafterward. `updateItem` can replace grading, images and section navigation;\nread the full item through the hatch first and use a precise `updateMask`.\nItem locations are indexes, while response answers use question ids.\n\nPublishing, sharing, deleting forms, and watch administration are outside this\nconnection. Responses are read-only; no tool submits or deletes a response.\n\n## Conventions\n\n- **
|
|
3
|
+
"content": "## Finding forms and joining answers\n\nThe Forms API cannot list or search forms. Find the file through Drive, or use\nthe id after `/forms/d/` in its edit URL. The `/forms/d/e/…` responder link is\nnot a form id. A form id is also its Drive file id.\n\nA form contains items; an item may ask one question, several grid questions,\nor no question. Responses join by `questionId`, never by `itemId`. Named\nresponse reads label answers with the current question titles in form order;\nan answer to a deleted question has no title. A response may contain several\nvalues or uploaded-file references for one question. File ids join to Drive.\n\n## Reading complete responses\n\nResponses and respondent emails can contain personal information. Filter and\naggregate in `execute_code`, returning named answers only when the task needs\nthem. `submittedAfter` is exclusive UTC: retain the newest `lastSubmittedTime`\nfor the next incremental read. Google does not promise a stable response\nsnapshot. A continuation inside a changed vendor page fails `conflict`; start\nagain or use that newest timestamp.\n\nLarge grids continue inside the same item: `questionsFrom` places the returned\nrows. `omittedQuestionIds` identifies answers left out for size; read those ids\nwith `get_response`. The structural hatch exposes quiz grading and correct\nanswers, images, and option navigation that named projections omit. Temporary\nimage access destinations remain withheld.\n\n<!-- when: writes -->\n## Editing without losing structure\n\nRead the form's current revision before editing. A stale revision refuses the\nbatch as `conflict`; re-read and rebuild the requested edit. A revision guards\nconcurrent changes and does not make a repeated write safe.\n\nCreation sets only title and Drive file name. Add descriptions or questions\nafterward. `updateItem` can replace grading, images and section navigation;\nread the full item through the hatch first and use a precise `updateMask`.\nItem locations are indexes, while response answers use question ids.\n\nPublishing, sharing, deleting forms, and watch administration are outside this\nconnection. Responses are read-only; no tool submits or deletes a response.\n<!-- end -->\n\n## Conventions\n\n- **Hatch:** `forms_api_search` finds operations, `forms_api_details` shows one contract, `forms_api_read` calls a reviewed read and `forms_api_write` a reviewed write. 5 operations are refused by design; use `forms_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Errors:** An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. `conflict` means: A revision-guarded batch update returned ABORTED or FAILED_PRECONDITION with HTTP 400 or 409.",
|
|
4
4
|
"instructionsHeading": "Connection instructions"
|
|
5
5
|
};
|