@zackbart/connecta 0.31.0 → 0.33.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 +97 -0
- package/README.md +30 -20
- package/bin/connecta.mjs +25 -6
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +167 -23
- package/dist/access-tokens.js +676 -140
- package/dist/agent-identity.js +47 -0
- package/dist/auth/cloudflare-access.js +13 -5
- package/dist/auth-elicitation.js +1 -1
- package/dist/call-admission-context.js +15 -0
- package/dist/call-admission.js +30 -43
- package/dist/call-recovery.js +6 -2
- package/dist/catalog-service.js +7 -2
- package/dist/config-value-sources.js +14 -9
- package/dist/config.d.ts +58 -78
- package/dist/config.js +143 -47
- package/dist/connector-access.d.ts +4 -33
- package/dist/connector-access.js +15 -99
- package/dist/connector-caller.d.ts +7 -1
- package/dist/connector-caller.js +3 -2
- package/dist/connectors/api-connector.d.ts +27 -4
- package/dist/connectors/api-connector.js +2 -0
- package/dist/connectors/guarded-fetch.d.ts +9 -0
- package/dist/connectors/guarded-fetch.js +7 -0
- package/dist/connectors/remote-mcp.js +2 -0
- package/dist/describe-config.d.ts +32 -7
- package/dist/describe-config.js +9 -18
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -0
- package/dist/execute.js +16 -14
- package/dist/index.d.ts +4 -3
- package/dist/index.js +30 -10
- package/dist/invocation.js +12 -7
- package/dist/membership.d.ts +51 -0
- package/dist/membership.js +121 -0
- package/dist/meta-tools.js +17 -19
- package/dist/module-contracts.d.ts +23 -6
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +2 -2
- package/dist/operator-ui/agents.d.ts +74 -0
- package/dist/operator-ui/agents.js +104 -0
- package/dist/operator-ui/contract.d.ts +151 -29
- package/dist/operator-ui/endpoints.js +192 -0
- package/dist/operator-ui/fix-prompts.js +4 -4
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/member-page.js +112 -0
- package/dist/operator-ui/model.d.ts +15 -7
- package/dist/operator-ui/view.d.ts +84 -19
- package/dist/operator-ui/view.js +83 -21
- package/dist/page-styles.js +1 -1
- package/dist/program-writes.js +2 -2
- package/dist/provider.d.ts +63 -11
- package/dist/provider.js +46 -7
- package/dist/providers/_shared/google/workspace.d.ts +53 -16
- package/dist/providers/_shared/google/workspace.js +31 -9
- package/dist/providers/_shared/rest/tools.js +2 -2
- package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
- package/dist/providers/_shared/rest/value-safety.js +699 -7
- package/dist/providers/breeze/skill.generated.js +2 -2
- package/dist/providers/ccb/index.js +1 -1
- package/dist/providers/cloudflare/index.js +3 -3
- package/dist/providers/cloudflare/rest.js +25 -3
- package/dist/providers/cloudflare/skill.generated.js +2 -2
- package/dist/providers/cloudflare/value-safety.js +32 -0
- package/dist/providers/docs/index.d.ts +1 -1
- package/dist/providers/docs/index.js +5 -5
- package/dist/providers/docs/skill.generated.js +2 -2
- package/dist/providers/drive/index.d.ts +1 -1
- package/dist/providers/drive/index.js +4 -4
- package/dist/providers/drive/skill.generated.js +2 -2
- package/dist/providers/forms/index.d.ts +1 -1
- package/dist/providers/forms/index.js +5 -5
- package/dist/providers/forms/skill.generated.js +2 -2
- package/dist/providers/github/skill.generated.js +2 -2
- package/dist/providers/gmail/index.d.ts +1 -1
- package/dist/providers/gmail/index.js +279 -26
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/linear/index.js +1 -1
- package/dist/providers/mixpanel/index.d.ts +27 -12
- package/dist/providers/mixpanel/index.js +58 -6
- package/dist/providers/mixpanel/operations.js +20 -0
- package/dist/providers/mixpanel/rest.js +603 -0
- package/dist/providers/mixpanel/skill.generated.js +3 -2
- package/dist/providers/mixpanel/value-safety.js +16 -0
- package/dist/providers/notion/index.d.ts +7 -0
- package/dist/providers/notion/index.js +116 -11
- package/dist/providers/notion/rest.js +183 -24
- package/dist/providers/notion/skill.generated.js +4 -4
- package/dist/providers/notion/value-safety.js +30 -5
- package/dist/providers/overflow/index.js +1 -1
- package/dist/providers/planning-center/index.d.ts +27 -0
- package/dist/providers/planning-center/index.js +287 -25
- package/dist/providers/planning-center/skill.generated.js +24 -4
- package/dist/providers/sheets/index.d.ts +4 -1
- package/dist/providers/sheets/index.js +9 -6
- package/dist/providers/sheets/skill.generated.js +2 -2
- package/dist/providers/slides/index.d.ts +1 -1
- package/dist/providers/slides/index.js +6 -6
- package/dist/providers/slides/skill.generated.js +2 -2
- package/dist/providers/stripe/rest.js +9 -2
- package/dist/providers/stripe/skill.generated.js +2 -2
- package/dist/providers/stripe/value-safety.js +22 -0
- package/dist/providers/vercel/index.js +2 -2
- package/dist/providers/vercel/rest.js +20 -2
- package/dist/providers/vercel/skill.generated.js +2 -2
- package/dist/providers/vercel/value-safety.js +22 -0
- package/dist/providers/wispr-flow/index.d.ts +14 -0
- package/dist/providers/wispr-flow/index.js +117 -0
- package/dist/providers/wispr-flow/skill.generated.js +8 -0
- package/dist/registry.d.ts +2 -3
- package/dist/registry.js +8 -9
- package/dist/routes/access-tokens.js +82 -75
- package/dist/routes/activity-disclosure.js +17 -18
- package/dist/routes/activity.js +34 -54
- package/dist/routes/actor-labels.js +33 -4
- package/dist/routes/agent-path.js +43 -0
- package/dist/routes/connect.js +16 -12
- package/dist/routes/credentials.js +16 -40
- package/dist/routes/mcp.js +155 -85
- package/dist/routes/oauth-management.js +11 -5
- package/dist/routes/oauth.js +3 -9
- package/dist/routes/operator.js +25 -17
- package/dist/routes/shared.d.ts +60 -20
- package/dist/routes/shared.js +187 -96
- package/dist/routes/ui-config.js +437 -97
- package/dist/routes/ui-data.js +2 -2
- package/dist/routes/ui.js +9 -16
- package/dist/runtime/call-admission.js +9 -0
- package/dist/server.js +8 -0
- package/dist/skills.js +5 -4
- package/dist/storage/keys.js +9 -1
- package/dist/tool-safety.js +13 -5
- package/dist/types.d.ts +12 -1
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +17 -15
- package/dist/usage-guide.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace-address.js +9 -0
- package/dist/workspace-subject.js +23 -0
- package/documentation/README.md +46 -22
- package/documentation/architecture.md +378 -58
- package/documentation/auth.md +806 -215
- package/documentation/code-mode.md +93 -81
- package/documentation/deploying.md +128 -26
- package/documentation/integrating.md +145 -22
- package/documentation/meta-tools.md +58 -46
- package/documentation/migration-0.33.md +344 -0
- package/documentation/operating.md +14 -9
- package/documentation/operator-tests.md +117 -31
- package/documentation/operator-ui.md +326 -55
- package/documentation/provider-auth-migration.md +42 -27
- package/documentation/provider-migration-0.29.md +3 -3
- package/examples/worker/AGENTS.md +35 -8
- package/examples/worker/README.md +92 -53
- package/examples/worker/src/connecta.config.ts +41 -20
- package/examples/worker/wrangler.jsonc +3 -0
- package/package.json +7 -2
- package/templates/node/.env.example +3 -0
- package/templates/node/AGENTS.md +33 -10
- package/templates/node/Dockerfile +2 -2
- package/templates/node/README.md +71 -44
- package/templates/node/docker-compose.yml +3 -2
- package/templates/node/package.json +4 -3
- package/templates/node/src/adopt-token.ts +23 -0
- package/templates/node/src/connecta.config.ts +33 -12
- package/templates/node/src/rotate-token.ts +17 -0
- package/dist/tool-safety.d.ts +0 -9
- package/templates/node/src/provision-token.ts +0 -17
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": ".breezechms.com (Breeze ChMS, now Tithely Church Management): people, tags, events, attendance, forms, volunteers, and giving.\n\nChurch purpose: \n\n## Sensitive data: reduce in code\n\nPeople records carry home addresses, phones, birthdates, and family ties, and\ngiving records reveal who gives and how much — religious affiliation by proxy.\nRead them inside `execute_code` and return only the counts, totals, or the\nfew fields the task needs. Never echo whole profiles or donor lists.\n\n## Ids and profile fields\n\n- Ids are numeric strings. Read them from a list tool; never guess one.\n- Events have two ids: an instance id (`list_events` `id`) for one\n occurrence, and a series `eventId`. Attendance, volunteers, and\n check-in take the instance id.\n- Person `details` are keyed by profile `fieldId`, not by name. Call\n `list_profile_fields` once and map ids to names in code. Multiple-choice\n values and `list_people` filters use option ids from the same call;\n join several with `-`.\n- `list_people` has no name search. List without details and match names\n in code, then `get_person` the hits.\n- Projections rename Breeze's snake_case fields to camelCase and drop photo\n paths and internal columns; `raw: true` returns Breeze's rows.\n\n## Pagination\n\n`list_people` pages by `cursor`: continue while `page.hasMore`. Breeze\nreports no total, so a full page sets `hasMore` and the next may be empty.\n`list_events` and `list_account_log` do not page: `truncated: true`\nmeans narrow the date range. Other lists return everything.\n\n## Giving\n\n`list_contributions` and `list_funds` call endpoints Breeze removed from\nits public reference in 2023 but still serves. They may change or vanish\nwithout notice; a failure there is not your argument's fault. Amounts are\ndecimal strings; `totalAmount` is an exact sum. Online gifts processed by\nTithe.ly arrive through its sync, batched per deposit, so the latest days\nmay be missing. Read the currency from `get_account_summary`.\n\n## Writes and the hatches\n\n- Every write, `add_person`, `assign_tag`, and `record_check_in`\n included, follows the routing in the `usage` skill. Fetch it with\n `skills({ name: \"usage\" })` for guest API and
|
|
3
|
+
"content": ".breezechms.com (Breeze ChMS, now Tithely Church Management): people, tags, events, attendance, forms, volunteers, and giving.\n\nChurch purpose: \n\n## Sensitive data: reduce in code\n\nPeople records carry home addresses, phones, birthdates, and family ties, and\ngiving records reveal who gives and how much — religious affiliation by proxy.\nRead them inside `execute_code` and return only the counts, totals, or the\nfew fields the task needs. Never echo whole profiles or donor lists.\n\n## Ids and profile fields\n\n- Ids are numeric strings. Read them from a list tool; never guess one.\n- Events have two ids: an instance id (`list_events` `id`) for one\n occurrence, and a series `eventId`. Attendance, volunteers, and\n check-in take the instance id.\n- Person `details` are keyed by profile `fieldId`, not by name. Call\n `list_profile_fields` once and map ids to names in code. Multiple-choice\n values and `list_people` filters use option ids from the same call;\n join several with `-`.\n- `list_people` has no name search. List without details and match names\n in code, then `get_person` the hits.\n- Projections rename Breeze's snake_case fields to camelCase and drop photo\n paths and internal columns; `raw: true` returns Breeze's rows.\n\n## Pagination\n\n`list_people` pages by `cursor`: continue while `page.hasMore`. Breeze\nreports no total, so a full page sets `hasMore` and the next may be empty.\n`list_events` and `list_account_log` do not page: `truncated: true`\nmeans narrow the date range. Other lists return everything.\n\n## Giving\n\n`list_contributions` and `list_funds` call endpoints Breeze removed from\nits public reference in 2023 but still serves. They may change or vanish\nwithout notice; a failure there is not your argument's fault. Amounts are\ndecimal strings; `totalAmount` is an exact sum. Online gifts processed by\nTithe.ly arrive through its sync, batched per deposit, so the latest days\nmay be missing. Read the currency from `get_account_summary`.\n\n## Writes and the hatches\n\n- Every write, `add_person`, `assign_tag`, and `record_check_in`\n included, follows the routing in the `usage` skill. Fetch it with\n `skills({ name: \"usage\" })` for guest API and write rules.\n Those three only add, and `add_person` never deduplicates.\n- `update_person` field values: text, date (M/D/YYYY), or option id in\n `response`; email, phone, address, and family_role values in `details`.\n- Every Breeze call is a GET, writes included, so the hatches split by\n endpoint. `breeze_api_get` admits only reviewed read endpoints and their\n documented parameters, such as `/tags/list_folders`,\n `/events/list_event`, `/events/attendance/eligible`, `/giving/view`,\n and `/pledges/list_campaigns`; it refuses anything else. Everything else\n — event, family, volunteer, and form writes, giving writes, check-out,\n undocumented parameters — is `breeze_api_mutate`. Paths are below\n `/api`; `*_json` values may be passed as objects.\n- `list_account_log` actions include person_created, person_updated,\n person_deleted, tag_assign, tag_unassign, event_created,\n attendance_deleted, form_entry_updated, contribution_added,\n contribution_updated, contribution_deleted, and batch_updated.\n\n## Rate limits\n\nBreeze publishes no limit and sends no rate headers; a third party reports\nabout 20 requests per minute. Keep loops sequential and prefer one list call\nover many single reads.\n",
|
|
4
4
|
"instructionsHeading": "Church instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": ".breezechms.com (Breeze ChMS, now Tithely Church Management): people, tags, events, attendance, forms, volunteers, and giving.\n\nChurch purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Sensitive data: reduce in code\n\nPeople records carry home addresses, phones, birthdates, and family ties, and\ngiving records reveal who gives and how much — religious affiliation by proxy.\nRead them inside `execute_code` and return only the counts, totals, or the\nfew fields the task needs. Never echo whole profiles or donor lists.\n\n## Ids and profile fields\n\n- Ids are numeric strings. Read them from a list tool; never guess one.\n- Events have two ids: an instance id (`list_events` `id`) for one\n occurrence, and a series `eventId`. Attendance, volunteers, and\n check-in take the instance id.\n- Person `details` are keyed by profile `fieldId`, not by name. Call\n `list_profile_fields` once and map ids to names in code. Multiple-choice\n values and `list_people` filters use option ids from the same call;\n join several with `-`.\n- `list_people` has no name search. List without details and match names\n in code, then `get_person` the hits.\n- Projections rename Breeze's snake_case fields to camelCase and drop photo\n paths and internal columns; `raw: true` returns Breeze's rows.\n\n## Pagination\n\n`list_people` pages by `cursor`: continue while `page.hasMore`. Breeze\nreports no total, so a full page sets `hasMore` and the next may be empty.\n`list_events` and `list_account_log` do not page: `truncated: true`\nmeans narrow the date range. Other lists return everything.\n\n## Giving\n\n`list_contributions` and `list_funds` call endpoints Breeze removed from\nits public reference in 2023 but still serves. They may change or vanish\nwithout notice; a failure there is not your argument's fault. Amounts are\ndecimal strings; `totalAmount` is an exact sum. Online gifts processed by\nTithe.ly arrive through its sync, batched per deposit, so the latest days\nmay be missing. Read the currency from `get_account_summary`.\n\n## Writes and the hatches\n\n- Every write, `add_person`, `assign_tag`, and `record_check_in`\n included, follows the routing in the `usage` skill. Fetch it with\n `skills({ name: \"usage\" })` for guest API and
|
|
7
|
+
"guide_1": "\n\n## Sensitive data: reduce in code\n\nPeople records carry home addresses, phones, birthdates, and family ties, and\ngiving records reveal who gives and how much — religious affiliation by proxy.\nRead them inside `execute_code` and return only the counts, totals, or the\nfew fields the task needs. Never echo whole profiles or donor lists.\n\n## Ids and profile fields\n\n- Ids are numeric strings. Read them from a list tool; never guess one.\n- Events have two ids: an instance id (`list_events` `id`) for one\n occurrence, and a series `eventId`. Attendance, volunteers, and\n check-in take the instance id.\n- Person `details` are keyed by profile `fieldId`, not by name. Call\n `list_profile_fields` once and map ids to names in code. Multiple-choice\n values and `list_people` filters use option ids from the same call;\n join several with `-`.\n- `list_people` has no name search. List without details and match names\n in code, then `get_person` the hits.\n- Projections rename Breeze's snake_case fields to camelCase and drop photo\n paths and internal columns; `raw: true` returns Breeze's rows.\n\n## Pagination\n\n`list_people` pages by `cursor`: continue while `page.hasMore`. Breeze\nreports no total, so a full page sets `hasMore` and the next may be empty.\n`list_events` and `list_account_log` do not page: `truncated: true`\nmeans narrow the date range. Other lists return everything.\n\n## Giving\n\n`list_contributions` and `list_funds` call endpoints Breeze removed from\nits public reference in 2023 but still serves. They may change or vanish\nwithout notice; a failure there is not your argument's fault. Amounts are\ndecimal strings; `totalAmount` is an exact sum. Online gifts processed by\nTithe.ly arrive through its sync, batched per deposit, so the latest days\nmay be missing. Read the currency from `get_account_summary`.\n\n## Writes and the hatches\n\n- Every write, `add_person`, `assign_tag`, and `record_check_in`\n included, follows the routing in the `usage` skill. Fetch it with\n `skills({ name: \"usage\" })` for guest API and write rules.\n Those three only add, and `add_person` never deduplicates.\n- `update_person` field values: text, date (M/D/YYYY), or option id in\n `response`; email, phone, address, and family_role values in `details`.\n- Every Breeze call is a GET, writes included, so the hatches split by\n endpoint. `breeze_api_get` admits only reviewed read endpoints and their\n documented parameters, such as `/tags/list_folders`,\n `/events/list_event`, `/events/attendance/eligible`, `/giving/view`,\n and `/pledges/list_campaigns`; it refuses anything else. Everything else\n — event, family, volunteer, and form writes, giving writes, check-out,\n undocumented parameters — is `breeze_api_mutate`. Paths are below\n `/api`; `*_json` values may be passed as objects.\n- `list_account_log` actions include person_created, person_updated,\n person_deleted, tag_assign, tag_unassign, event_created,\n attendance_deleted, form_entry_updated, contribution_added,\n contribution_updated, contribution_deleted, and batch_updated.\n\n## Rate limits\n\nBreeze publishes no limit and sends no rate headers; a third party reports\nabout 20 requests per minute. Keep loops sequential and prefer one list call\nover many single reads.\n"
|
|
8
8
|
}
|
|
9
9
|
};
|
|
@@ -1718,7 +1718,7 @@ function ccbConnector(id, options) {
|
|
|
1718
1718
|
summary: `${environment === "sandbox" ? "Sandbox" : "Production"} CCB, ${mode === "identity" ? "per person" : "church-wide"}: id resolution, sensitive data, paging, rate limits, hatches.`,
|
|
1719
1719
|
required: true,
|
|
1720
1720
|
},
|
|
1721
|
-
callAdmission: options.callAdmission
|
|
1721
|
+
callAdmission: options.callAdmission === undefined ? CCB_ADMISSION : options.callAdmission,
|
|
1722
1722
|
...(options.maxResultBytes !== undefined ? { maxResultBytes: options.maxResultBytes } : {}),
|
|
1723
1723
|
tools: tools(ccbTransport(endpoints.apiOrigin), defaultPerPage, writable),
|
|
1724
1724
|
});
|
|
@@ -10,7 +10,7 @@ import { skill } from "./skill.generated.js";
|
|
|
10
10
|
import { apiConnector } from "../../connectors/api-connector.js";
|
|
11
11
|
import { reviewedCatalog } from "../../catalog-drift.js";
|
|
12
12
|
import { keys, optionsOf, variants } from "../../config-schema.js";
|
|
13
|
-
import { defineProvider } from "../../provider.js";
|
|
13
|
+
import { CALL_ADMISSION, defineProvider } from "../../provider.js";
|
|
14
14
|
import { byAuth, hostedOAuth } from "../_shared/rest/dispatch.js";
|
|
15
15
|
import { restTools } from "../_shared/rest/tools.js";
|
|
16
16
|
import { CLOUDFLARE_API_BASE, cloudflareRest } from "./rest.js";
|
|
@@ -123,7 +123,7 @@ const PIN = optionsOf()(keys("accountIds", "zoneIds"));
|
|
|
123
123
|
const CLOUDFLARE_OPTIONS = variants(["auth", "type"], {
|
|
124
124
|
oauth: optionsOf()({
|
|
125
125
|
...COMMON,
|
|
126
|
-
|
|
126
|
+
callAdmission: CALL_ADMISSION,
|
|
127
127
|
auth: optionsOf()(keys("type")),
|
|
128
128
|
}).shape,
|
|
129
129
|
apiToken: optionsOf()({
|
|
@@ -145,7 +145,7 @@ function cloudflareOAuth(id, options, provider) {
|
|
|
145
145
|
url: CLOUDFLARE_MCP_ENDPOINT,
|
|
146
146
|
title: options.title ?? "Cloudflare (MCP)",
|
|
147
147
|
description: `Cloudflare's official whole-API MCP interface: ${options.purpose.trim()}`,
|
|
148
|
-
...(options.callAdmission ? { callAdmission: options.callAdmission } : {}),
|
|
148
|
+
...(options.callAdmission !== undefined ? { callAdmission: options.callAdmission } : {}),
|
|
149
149
|
usageGuide: {
|
|
150
150
|
content: oauthUsageGuide(options.purpose.trim(), options.instructions),
|
|
151
151
|
summary: "Hosted MCP over OAuth. Search the OpenAPI document; execute programs always classify as writes.",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { chargeConnectorCall } from "../../call-admission-context.js";
|
|
1
2
|
import { retryAfterMs } from "../../connectors/guarded-fetch.js";
|
|
2
3
|
import { ConnectorCallError } from "../../errors.js";
|
|
3
4
|
import { OperationIndex } from "../_shared/rest/operation-index.js";
|
|
4
5
|
import { callRest, restCall, restTransport, } from "../_shared/rest/tools.js";
|
|
5
6
|
import { inspectGraphqlQuery } from "./graphql.js";
|
|
6
|
-
import { valueSafety } from "../_shared/rest/value-safety.js";
|
|
7
|
+
import { valueSafety, withholdEmbeddedUrlsIn, refuseWithheldLinks, refuseWithheldUpload, refuseWithheldBase64, } from "../_shared/rest/value-safety.js";
|
|
7
8
|
import { CLOUDFLARE_VALUE_SAFETY } from "./value-safety.js";
|
|
8
9
|
import { openapi } from "./openapi.generated.js";
|
|
9
10
|
/** Cloudflare's v4 REST base. Override only for a proxy or a test double. */
|
|
@@ -253,7 +254,7 @@ const SAFETY = valueSafety(CLOUDFLARE_VALUE_SAFETY, () => cloudflareIndex());
|
|
|
253
254
|
* (it may echo a submitted secret); the status and numeric codes still route.
|
|
254
255
|
*/
|
|
255
256
|
function failureFor(status, headers, body, call) {
|
|
256
|
-
const errors = errorsOf(body);
|
|
257
|
+
const errors = errorsOf(withholdEmbeddedUrlsIn(body));
|
|
257
258
|
const ray = headers.get("cf-ray");
|
|
258
259
|
const codes = errorCodes(errors);
|
|
259
260
|
const withheld = call !== undefined && SAFETY.withholdsErrors(call.op);
|
|
@@ -472,6 +473,7 @@ export function cloudflareRest(options) {
|
|
|
472
473
|
let owner = owners.get(zone);
|
|
473
474
|
if (owner === undefined) {
|
|
474
475
|
chargeLookup(ctx);
|
|
476
|
+
await chargeConnectorCall(ctx);
|
|
475
477
|
owner = await send({ method: "GET", path: `/zones/${encodeURIComponent(zone)}` }, ctx, async (response) => {
|
|
476
478
|
const body = await response.jsonResult().catch(() => ({ value: undefined }));
|
|
477
479
|
const value = "value" in body ? body.value : undefined;
|
|
@@ -491,6 +493,24 @@ export function cloudflareRest(options) {
|
|
|
491
493
|
* neither is admitted only from the reviewed `PIN_SAFE_UNSCOPED` set.
|
|
492
494
|
*/
|
|
493
495
|
const admit = async (call, ctx) => {
|
|
496
|
+
if (call.method !== "GET" &&
|
|
497
|
+
call.method !== "HEAD" &&
|
|
498
|
+
!READ_POSTS.some(([method, path]) => method === call.method && path === call.op.path)) {
|
|
499
|
+
refuseWithheldLinks([call.query, call.path], "Cloudflare");
|
|
500
|
+
if (call.body instanceof RawUpload)
|
|
501
|
+
await refuseWithheldUpload(call.body.body, "Cloudflare");
|
|
502
|
+
else
|
|
503
|
+
refuseWithheldLinks(call.body, "Cloudflare");
|
|
504
|
+
if (call.method === "PUT" &&
|
|
505
|
+
call.op.path === "/accounts/{account_id}/storage/kv/namespaces/{namespace_id}/bulk" &&
|
|
506
|
+
Array.isArray(call.body)) {
|
|
507
|
+
for (const entry of call.body) {
|
|
508
|
+
// KV values support 25 MiB decoded; MAX_RESPONSE_BYTES only caps reads.
|
|
509
|
+
if (record(entry)["base64"] === true)
|
|
510
|
+
await refuseWithheldBase64(record(entry)["value"], "Cloudflare", 25 * 1024 * 1024);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
494
514
|
if (!pin)
|
|
495
515
|
return;
|
|
496
516
|
const { accounts, zones, template } = scopeIds(call);
|
|
@@ -632,7 +652,9 @@ export function cloudflareRest(options) {
|
|
|
632
652
|
const info = record(body["result_info"]);
|
|
633
653
|
if (Object.keys(info).length === 0)
|
|
634
654
|
return undefined;
|
|
635
|
-
const
|
|
655
|
+
const rawCursor = text(info["cursor"]) ?? text(record(info["cursors"])["after"]);
|
|
656
|
+
// Never echo or offer a modified cursor: it would not address the next page.
|
|
657
|
+
const cursor = rawCursor === undefined || withholdEmbeddedUrlsIn(rawCursor) === rawCursor ? rawCursor : undefined;
|
|
636
658
|
if (cursor !== undefined || "cursor" in info || "cursors" in info) {
|
|
637
659
|
const accepted = operations.contract(call.op).parameters.map((parameter) => parameter.name);
|
|
638
660
|
const param = accepted.includes("cursor") ? "cursor" : accepted.includes("after") ? "after" : "cursor";
|
|
@@ -3,10 +3,10 @@ export const skill = {
|
|
|
3
3
|
"content": "- Resolve account and zone ids before acting; never guess one. Ids are 32-character hex strings, and a zone is found by its domain name. Defaults and pins route a connector; Cloudflare's permissions still decide what the credential may do.\n- Read state before replacing, rolling back, or purging. For DNS, PATCH changes the fields you send and PUT replaces the whole record, so read the record first and send its type, name, and content with the change. Prefer a targeted cache purge (files, tags, hosts, or prefixes) over purging everything.\n- Cloudflare reports failures in `errors` with numeric codes and returns `success: false`; read the message instead of retrying the same call.\n- Honor rate-limit waits. Cloudflare documents 1,200 requests per five minutes per user, counted across the dashboard and every token; Connecta's per-runtime window approximates it, and a limited user is blocked for the rest of the window.\n- Treat every create, update, delete, purge, deploy, and upload as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n",
|
|
4
4
|
"instructionsHeading": "Account instructions",
|
|
5
5
|
"fragments": {
|
|
6
|
-
"oauth": "\n\n- The catalog is Cloudflare's own `search` and `execute`. `search` runs code against Cloudflare's OpenAPI document; `execute` runs code that may call any Cloudflare API endpoint the OAuth grant allows.\n- Connecta classifies `execute` as a write, even for a program that only reads, because one program can mix GET, POST, PUT, PATCH, and DELETE and no schema proves otherwise. A read-only
|
|
6
|
+
"oauth": "\n\n- The catalog is Cloudflare's own `search` and `execute`. `search` runs code against Cloudflare's OpenAPI document; `execute` runs code that may call any Cloudflare API endpoint the OAuth grant allows.\n- Connecta classifies `execute` as a write, even for a program that only reads, because one program can mix GET, POST, PUT, PATCH, and DELETE and no schema proves otherwise. A read-only endpoint therefore gets only `search` here. Deployments that need API reads from a read-only endpoint configure an API-token connector, whose `cloudflare_api_read` and named reads are reads.\n- Use `search` to find the exact method, path, and fields before writing an execute program. Do not guess an endpoint from product naming.\n- Keep returned values small. Filter and project inside the Cloudflare program, then reduce again inside Connecta's `execute_code` when several calls must be joined.\n- This connection has none of the REST connector's tools: no `cloudflare_api_*` validation against a pinned index, no `graphql_query`, no `cloudflare_api_upload`, and no account or zone pin. OAuth permissions are the boundary.\n",
|
|
7
7
|
"oauth_tail": "- An `auth_required` failure means this connector's Cloudflare authorization is missing or expired: run `authorize_connector` for this connector id, then retry the same call unchanged.\n",
|
|
8
8
|
"global_warning": "\n\nThis connector holds a Global API Key. It acts as its Cloudflare user with every permission that user has, in every account and zone the user belongs to, and nothing on Cloudflare's side narrows it. A mistaken write can reach production domains, billing, and members across all of them. Prefer reads, state the account and zone of every write, and stop when a request is ambiguous. Writes under `/user` and `/memberships` are refused. R2 accepts API tokens only, so R2 operations fail with this key.\n",
|
|
9
|
-
"key": "\n\n- This is Connecta's REST connector to the Cloudflare v4 API, holding one operator-managed credential. `cloudflare_api_search` finds API operations in a pinned index (not records) and names the tool that calls each; `cloudflare_api_details` returns one operation's parameters and body with types, enums, and required names. Neither sends a request.\n- `cloudflare_api_read` calls GET operations plus reviewed read-only POSTs: analytics queries, Analytics Engine and SQL API queries, Log Explorer SQL, Vectorize queries, KV bulk get, telemetry queries, and validation checks. `cloudflare_api_write` calls every other POST, PUT, PATCH, and DELETE with `method` and `path` stated. D1 `…/query` and `…/raw`, Durable Object queries, Workers AI `…/ai/run`, AI Search and AutoRAG search (query rewriting runs billed models), monetization checks, and flag evaluation are writes: they can change data, cost money, or activate something.\n- Paths are concrete: substitute ids into the template search returned (`/zones/<zone id>/dns_records/<record id>`). Pass `query` and `body` as JSON named as details lists them; text operations (Analytics Engine SQL, Log Explorer SQL, Worker scripts) take a string body, and Vectorize inserts a list of objects sent as NDJSON. R2 calls accept `headers` with `cf-r2-jurisdiction` or `cf-r2-storage-class`. Encode a `/` inside an R2 object key or KV key name as `%2F`.\n- Every call is checked against the pinned index before it is sent. An unknown path returns the nearest operations; an unknown or missing parameter returns `validation.issues` with the accepted names. Nothing reached Cloudflare, so fix the call from that answer.\n- Results arrive as `{ status, data, page? }` with Cloudflare's envelope unwrapped: `data` is the `result`. On a list, pass `page.next` as the `page.param` query argument (`page` for numbered lists, `cursor` for KV, R2, and other cursor lists). Lists belong inside `execute_code`; pass `select` (dot paths such as `id` and `name`) to keep only the fields the question needs.\n- `list_zones` and `list_accounts` resolve names to ids; `verify_credential` separates a bad credential from a missing permission. `graphql_query` runs read-only GraphQL Analytics queries (mutations are refused before sending); filter every `zones` or `accounts` field by `zoneTag` or `accountTag`. A pinned connector sends one query per document, with every fragment it defines used.\n- Multipart, binary, and file bodies go through `cloudflare_api_upload`: Worker scripts and versions, KV values, R2 objects, Pages deployments, Images, Stream, DNS zone-file import, and Snippets. It takes `textBody`, `base64Body`, or multipart `parts`, never a local file.\n- Refused by design: every operation that mints, rotates, or hands back a credential (API tokens, Access service tokens, R2 temporary credentials, tunnel and Mesh node tokens, signing keys, registry credentials, direct-upload URLs, deploy hooks, preview and participant tokens), payment client secrets, URL Scanner captures (results, HAR, DOM, raw responses: they hold the scanned session's cookies and headers; use the scan search for verdicts), and GETs that act (an email unsubscribe, a PagerDuty connect). Stored and embedded secrets (Turnstile, TSIG, webhook and identity-provider secrets, Stream keys, Logpush destination credentials, probe and custom-provider headers, BGP keys, Zaraz variables, Hyperdrive database passwords, RealtimeKit storage credentials, transform-rule header values) and every environment or plain-text binding value (Pages, Containers, Workers, Builds; names and types stay) come back as `\"[redacted]\"` on every method. Notification, RealtimeKit, and data security webhook destinations keep only their scheme and host. Workers KV values are application data and come back as stored. Reviewed operations, and operations that take credentials, report Cloudflare's error codes without its error text. Worker script content is readable: it is code, and secret bindings are not part of it.\n- An `auth_required` failure means the credential is missing, revoked, or malformed: an operator must replace it in this connection in the operator UI, and `authorize_connector` returns that handoff. `provider_permission_denied` means the credential works but lacks this permission or resource, or the call is outside the pin; ask rather than retry.\n",
|
|
9
|
+
"key": "\n\n- This is Connecta's REST connector to the Cloudflare v4 API, holding one operator-managed credential. `cloudflare_api_search` finds API operations in a pinned index (not records) and names the tool that calls each; `cloudflare_api_details` returns one operation's parameters and body with types, enums, and required names. Neither sends a request.\n- `cloudflare_api_read` calls GET operations plus reviewed read-only POSTs: analytics queries, Analytics Engine and SQL API queries, Log Explorer SQL, Vectorize queries, KV bulk get, telemetry queries, and validation checks. `cloudflare_api_write` calls every other POST, PUT, PATCH, and DELETE with `method` and `path` stated. D1 `…/query` and `…/raw`, Durable Object queries, Workers AI `…/ai/run`, AI Search and AutoRAG search (query rewriting runs billed models), monetization checks, and flag evaluation are writes: they can change data, cost money, or activate something.\n- Paths are concrete: substitute ids into the template search returned (`/zones/<zone id>/dns_records/<record id>`). Pass `query` and `body` as JSON named as details lists them; text operations (Analytics Engine SQL, Log Explorer SQL, Worker scripts) take a string body, and Vectorize inserts a list of objects sent as NDJSON. R2 calls accept `headers` with `cf-r2-jurisdiction` or `cf-r2-storage-class`. Encode a `/` inside an R2 object key or KV key name as `%2F`.\n- Every call is checked against the pinned index before it is sent. An unknown path returns the nearest operations; an unknown or missing parameter returns `validation.issues` with the accepted names. Nothing reached Cloudflare, so fix the call from that answer.\n- Under an account pin, each uncached zone-ownership check also spends the connector's call budget. Checks stay capped at three per call and 60 per five minutes per connector; an exhausted budget stops the check and the requested operation.\n- Results arrive as `{ status, data, page? }` with Cloudflare's envelope unwrapped: `data` is the `result`. On a list, pass `page.next` as the `page.param` query argument (`page` for numbered lists, `cursor` for KV, R2, and other cursor lists). Lists belong inside `execute_code`; pass `select` (dot paths such as `id` and `name`) to keep only the fields the question needs.\n- `list_zones` and `list_accounts` resolve names to ids; `verify_credential` separates a bad credential from a missing permission. `graphql_query` runs read-only GraphQL Analytics queries (mutations are refused before sending); filter every `zones` or `accounts` field by `zoneTag` or `accountTag`. A pinned connector sends one query per document, with every fragment it defines used.\n- Multipart, binary, and file bodies go through `cloudflare_api_upload`: Worker scripts and versions, KV values, R2 objects, Pages deployments, Images, Stream, DNS zone-file import, and Snippets. It takes `textBody`, `base64Body`, or multipart `parts`, never a local file.\n- Refused by design: every operation that mints, rotates, or hands back a credential (API tokens, Access service tokens, R2 temporary credentials, tunnel and Mesh node tokens, signing keys, registry credentials, direct-upload URLs, deploy hooks, preview and participant tokens), payment client secrets, URL Scanner captures (results, HAR, DOM, raw responses: they hold the scanned session's cookies and headers; use the scan search for verdicts), and GETs that act (an email unsubscribe, a PagerDuty connect). Stored and embedded secrets (Turnstile, TSIG, webhook and identity-provider secrets, Stream keys, Logpush destination credentials, probe and custom-provider headers, BGP keys, Zaraz variables, Hyperdrive database passwords, RealtimeKit storage credentials, transform-rule header values) and every environment or plain-text binding value (Pages, Containers, Workers, Builds; names and types stay) come back as `\"[redacted]\"` on every method. Notification, RealtimeKit, and data security webhook destinations keep only their scheme and host. Workers KV values are application data and come back as stored. Reviewed operations, and operations that take credentials, report Cloudflare's error codes without its error text. Worker script content is readable: it is code, and secret bindings are not part of it.\n- An `auth_required` failure means the credential is missing, revoked, or malformed: an operator must replace it in this connection in the operator UI, and `authorize_connector` returns that handoff. `provider_permission_denied` means the credential works but lacks this permission or resource, or the call is outside the pin; ask rather than retry.\n- Embedded URL safety uses reviewed paths plus global signing parameters. Generic credential-named query parameters (`code`, `key`, `token`, `sig`, etc.) and credential-looking fragments withhold the destination's entire query/fragment only in Workers build `lines`, Pages log `data.line`, and telemetry `$metadata.message`/`error` and raw string/object `source` payloads in both `events.events` and `invocations.*` on the reviewed log operations and vendor error text. Ordinary `code`/`key` links can therefore be false positives in these diagnostics. DNS TXT content, KV values, Access/Waiting Room custom HTML and other operator content keep those ordinary URL parameters byte for byte. Literal AWS SigV4/Google V4 `X-Amz-*`/`X-Goog-*` Signature, Credential and Security-Token values are withheld in every decoded string before projection. A withheld value is not the original: do not write it back. KV bulk writes also inspect decoded `value` fields with `base64: true` (up to Cloudflare's 25 MiB decoded value limit; malformed or oversized base64 is refused, and the host's inbound body cap still applies). All writes, including named writes and supported raw/base64/multipart uploads, reject URL destinations whose own query/fragment contains the exact `[redacted]` placeholder, plain or encoded, with `invalid_args` before dispatch. Admission iteratively checks every JSON key and value within the host's accepted request size, without separate depth or node-count ceilings; cyclic direct arguments are refused. A marker outside the destination remains ordinary text. Split strings, encoded host/signing names, bare hosts without `//`, and opaque bearer paths remain outside this scan. Binary responses represented as base64, including Worker JavaScript served as `application/javascript`, are returned unchanged and are not decoded/scanned. An envelope cursor that would change under URL sanitizing is omitted rather than echoed or reused.\n",
|
|
10
10
|
"shared": "- Resolve account and zone ids before acting; never guess one. Ids are 32-character hex strings, and a zone is found by its domain name. Defaults and pins route a connector; Cloudflare's permissions still decide what the credential may do.\n- Read state before replacing, rolling back, or purging. For DNS, PATCH changes the fields you send and PUT replaces the whole record, so read the record first and send its type, name, and content with the change. Prefer a targeted cache purge (files, tags, hosts, or prefixes) over purging everything.\n- Cloudflare reports failures in `errors` with numeric codes and returns `success: false`; read the message instead of retrying the same call.\n- Honor rate-limit waits. Cloudflare documents 1,200 requests per five minutes per user, counted across the dashboard and every token; Connecta's per-runtime window approximates it, and a limited user is blocked for the rest of the window.\n- Treat every create, update, delete, purge, deploy, and upload as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n",
|
|
11
11
|
"key_tail": "- Cloudflare answers a rate limit with HTTP 429; the failure carries `retryAfterMs`. Failed authentication is limited separately (code 10502), so diagnose a credential once with `verify_credential` rather than retrying calls.\n"
|
|
12
12
|
}
|
|
@@ -15,6 +15,22 @@
|
|
|
15
15
|
// Paths are dot paths into the data a tool returns (Cloudflare's `result`,
|
|
16
16
|
// or the whole body when it is not an envelope); lists are traversed
|
|
17
17
|
// implicitly and `*` matches every key of an object.
|
|
18
|
+
// Embedded URL verdict (#812): reviewed paths + global signing parameters.
|
|
19
|
+
// Evidence: digest-verified schema revision 3fad73eb27c381031c09ccdfa28ef9352029cd5a:
|
|
20
|
+
// builds_BuildLogsResponse.lines (timestamp/log-message tuples),
|
|
21
|
+
// pages_deployment_log.data.line and workers-observability_query_results
|
|
22
|
+
// events.events and invocations.* -> telemetry_event.$metadata.message/error
|
|
23
|
+
// and source (raw string/object log payload). Exact operations
|
|
24
|
+
// and paths are below, after envelope unwrapping. All vendor error text is
|
|
25
|
+
// scanned too; ordinary code/key links can be false positives in diagnostics.
|
|
26
|
+
// DNS TXT content, KV values, Access custom_html, Waiting Room custom_page_html
|
|
27
|
+
// and other operator/application content retain generic query parameters.
|
|
28
|
+
// Literal X-Amz-/X-Goog- Signature/Credential/Security-Token values are withheld
|
|
29
|
+
// in every decoded string. Binary/base64 response bodies (including Worker
|
|
30
|
+
// JavaScript served as application/javascript) are not decoded or scanned.
|
|
31
|
+
// Recording/download/upload capabilities retain their structured-field rules.
|
|
32
|
+
// The review found no documented Cloudflare-minted links embedded in other
|
|
33
|
+
// free text and justifies no Cloudflare/R2/S3-wide file-host policy.
|
|
18
34
|
import { redact as reviewed, refuse, safe as reviewedSafe, } from "../_shared/rest/value-safety.js";
|
|
19
35
|
const MINT = "Connecta does not create, rotate, or return credentials; an operator manages this credential in the Cloudflare dashboard.";
|
|
20
36
|
const API_TOKENS = "Connecta does not create, roll, edit, or delete API tokens; an operator manages tokens in the Cloudflare dashboard.";
|
|
@@ -1152,6 +1168,22 @@ const OPERATIONS = {
|
|
|
1152
1168
|
* are reviewed metadata wherever they appear.
|
|
1153
1169
|
*/
|
|
1154
1170
|
export const CLOUDFLARE_VALUE_SAFETY = {
|
|
1171
|
+
embeddedUrls: {
|
|
1172
|
+
operations: {
|
|
1173
|
+
"GET /accounts/{account_id}/builds/builds/{build_uuid}/logs": ["lines"],
|
|
1174
|
+
"GET /accounts/{account_id}/pages/projects/{project_name}/deployments/{deployment_id}/history/logs": [
|
|
1175
|
+
"data.line",
|
|
1176
|
+
],
|
|
1177
|
+
"POST /accounts/{account_id}/workers/observability/telemetry/query": [
|
|
1178
|
+
"events.events.$metadata.message",
|
|
1179
|
+
"events.events.$metadata.error",
|
|
1180
|
+
"events.events.source",
|
|
1181
|
+
"invocations.*.$metadata.message",
|
|
1182
|
+
"invocations.*.$metadata.error",
|
|
1183
|
+
"invocations.*.source",
|
|
1184
|
+
],
|
|
1185
|
+
},
|
|
1186
|
+
},
|
|
1155
1187
|
title: "Cloudflare",
|
|
1156
1188
|
operations: OPERATIONS,
|
|
1157
1189
|
fields: {
|
|
@@ -48,7 +48,7 @@ export type DocsOptions = GoogleWorkspaceOptions;
|
|
|
48
48
|
*
|
|
49
49
|
* Reads run in programs. `create_document`, `append_text`, and `insert_text`
|
|
50
50
|
* are additive writes the host approves through call_destructive_tool or a
|
|
51
|
-
*
|
|
51
|
+
* program on a writable endpoint; `replace_all_text` and `batch_update_document` are
|
|
52
52
|
* destructive.
|
|
53
53
|
*/
|
|
54
54
|
export declare const docs: import("../../provider.js").ProviderFactory<GoogleWorkspaceOptions>;
|
|
@@ -26,7 +26,7 @@ import { skill } from "./skill.generated.js";
|
|
|
26
26
|
*/
|
|
27
27
|
import { apiConnector as api, defined } from "../../connectors/api-connector.js";
|
|
28
28
|
import { ConnectorCallError } from "../../errors.js";
|
|
29
|
-
import { googleOutcomeOf, GOOGLE_WORKSPACE_OPTIONS, googleWorkspaceClient, workspaceConnection, } from "../_shared/google/workspace.js";
|
|
29
|
+
import { googleOutcomeOf, GOOGLE_WORKSPACE_OPTIONS, googleWorkspaceClient, workspaceConnection, workspaceConnector, } from "../_shared/google/workspace.js";
|
|
30
30
|
import { RESULT_BUDGET_BYTES, clampText, jsonBytes } from "../_shared/google/result-size.js";
|
|
31
31
|
import { asProviderFactory } from "../../provider.js";
|
|
32
32
|
/**
|
|
@@ -935,7 +935,7 @@ function toolDefinitions(client) {
|
|
|
935
935
|
name: "create_document",
|
|
936
936
|
description: "Create a new Google Doc in the user's My Drive root with a title and optional plain-text body. Cannot choose a folder, apply a template, or copy a document.",
|
|
937
937
|
// Additive: a new document changes nothing that existed. Not read-only,
|
|
938
|
-
// so program calls require a
|
|
938
|
+
// so program calls require a writable endpoint; other writes use
|
|
939
939
|
// call_destructive_tool. The host controls approval.
|
|
940
940
|
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
941
941
|
inputSchema: input({
|
|
@@ -1188,7 +1188,7 @@ function usageGuide(purpose, instructions) {
|
|
|
1188
1188
|
*
|
|
1189
1189
|
* Reads run in programs. `create_document`, `append_text`, and `insert_text`
|
|
1190
1190
|
* are additive writes the host approves through call_destructive_tool or a
|
|
1191
|
-
*
|
|
1191
|
+
* program on a writable endpoint; `replace_all_text` and `batch_update_document` are
|
|
1192
1192
|
* destructive.
|
|
1193
1193
|
*/
|
|
1194
1194
|
export const docs = asProviderFactory({
|
|
@@ -1218,7 +1218,7 @@ function docsConnector(id, options) {
|
|
|
1218
1218
|
notFound: "ambiguous",
|
|
1219
1219
|
connection,
|
|
1220
1220
|
});
|
|
1221
|
-
return api(id, {
|
|
1221
|
+
return workspaceConnector(connection, api(id, {
|
|
1222
1222
|
...defined({
|
|
1223
1223
|
callAdmission: options.callAdmission,
|
|
1224
1224
|
maxResultBytes: options.maxResultBytes,
|
|
@@ -1233,5 +1233,5 @@ function docsConnector(id, options) {
|
|
|
1233
1233
|
required: true,
|
|
1234
1234
|
},
|
|
1235
1235
|
tools: tools(client),
|
|
1236
|
-
});
|
|
1236
|
+
}));
|
|
1237
1237
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Google Docs usage\n\nReads and edits Google Docs as the signed-in person through Workspace delegation; it cannot search or list documents.\n\nConnection purpose: \n\n## Whose documents\n\nEvery call acts as the Workspace account deployment config maps the caller\nto, and reaches exactly the documents Drive shares with that person. No
|
|
3
|
+
"content": "# Google Docs usage\n\nReads and edits Google Docs as the signed-in person through Workspace delegation; it cannot search or list documents.\n\nConnection purpose: \n\n## Whose documents\n\nEvery call acts as the Workspace account deployment config maps the caller\nto, and reaches exactly the documents Drive shares with that person; an\nagent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names an\naccount. A call with none mapped, or from an agent whose pool has no owner,\nfails `auth_required`; only an operator can change either.\n\n## Finding a document\n\nThere is no search or list tool. Take the id from a\ndocs.google.com URL (the part after `/d/`), from the person, or from a\nGoogle Drive connection's file search. A failure on an id may mean it does\nnot exist or is not shared with this person; Google does not say which.\n\n## Reading\n\n- `get_document` renders every tab as markdown-ish text: headings, lists,\n links, tables as pipe rows, footnotes after the body, images and other\n embeds as bracketed markers. Headers, footers, floating images, and\n comments are not rendered; a tab's `notRendered` names any it holds, and\n `suggestion_marks` there means the text mixes unmarked suggested\n insertions and deletions in (read with `suggestions` to preview). Text past `maxChars` (shared across tabs) ends with a marker;\n pass `tabId` to read one tab.\n- Before an index-based edit, read with `withIndexes: true` and target an\n element's `startIndex`/`endIndex`. A long document's rows page with\n `page.nextCursor`; send `maxChars: 0` on later pages, and start over if\n `revisionId` changed between them. Indexes are UTF-16 code units and\n shift after every edit. Keep `revisionId` and pass it as\n `requiredRevisionId`, so an edit against a document someone else has\n changed fails `conflict` with nothing applied; re-read and recompute. Indexes\n from a suggestions preview (`accepted`/`rejected`) are not valid for\n edits.\n- `raw: true` returns Google's document resource for the tabs instead of\n the rendering — styles, headers, footers, named ranges, list and segment ids\n that `batch_update_document` requests may need. It is large: pass\n `tabId`, and call it directly rather than inside `execute_code`,\n which carries at most 256 KiB per result.\n- The same holds for a `maxChars` above the default: a long rendering\n can pass here and still be refused inside `execute_code`, even when the\n program would only measure it. Read it with a direct `call_tool`, paged\n with `connecta.result`, or read one tab at a time.\n\n## Editing\n\n- `create_document` makes a new document in My Drive's root. Never create\n again after a failure that may have left one behind; the message says\n which. When the document exists but its starting text failed, the message\n names its id: append the text with `append_text` straight away only when\n Google refused it; otherwise read the document first and append only what\n is missing.\n- An edit sent with no answer back, answered with a server error, or\n redirected says its outcome is unknown: re-read before repeating it, or\n repeat it with the same `requiredRevisionId`, which Google refuses if the\n first attempt landed. One Google answered 2xx whose reply was unreadable\n says it was applied: do not repeat it.\n- `append_text` joins the last paragraph; begin with `\\n` for a new one.\n- `replace_all_text` changes every match at once; read first and make\n `find` specific.\n- `batch_update_document` takes raw Docs API requests (`deleteContentRange`,\n `updateTextStyle`, `insertTable`, `createParagraphBullets`, …) for what\n the named tools cannot do. It is always destructive, and requires the\n `requiredRevisionId` from the read it was planned against: up to 100\n requests, each exactly one generally available kind (the Developer Preview\n comment and suggestion kinds are refused). Several requests in one\n call apply atomically, and each sees the index shifts of those before it;\n order deletions from the end of the document backwards.\n",
|
|
4
4
|
"instructionsHeading": "Connection instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": "# Google Docs usage\n\nReads and edits Google Docs as the signed-in person through Workspace delegation; it cannot search or list documents.\n\nConnection purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Whose documents\n\nEvery call acts as the Workspace account deployment config maps the caller\nto, and reaches exactly the documents Drive shares with that person. No
|
|
7
|
+
"guide_1": "\n\n## Whose documents\n\nEvery call acts as the Workspace account deployment config maps the caller\nto, and reaches exactly the documents Drive shares with that person; an\nagent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names an\naccount. A call with none mapped, or from an agent whose pool has no owner,\nfails `auth_required`; only an operator can change either.\n\n## Finding a document\n\nThere is no search or list tool. Take the id from a\ndocs.google.com URL (the part after `/d/`), from the person, or from a\nGoogle Drive connection's file search. A failure on an id may mean it does\nnot exist or is not shared with this person; Google does not say which.\n\n## Reading\n\n- `get_document` renders every tab as markdown-ish text: headings, lists,\n links, tables as pipe rows, footnotes after the body, images and other\n embeds as bracketed markers. Headers, footers, floating images, and\n comments are not rendered; a tab's `notRendered` names any it holds, and\n `suggestion_marks` there means the text mixes unmarked suggested\n insertions and deletions in (read with `suggestions` to preview). Text past `maxChars` (shared across tabs) ends with a marker;\n pass `tabId` to read one tab.\n- Before an index-based edit, read with `withIndexes: true` and target an\n element's `startIndex`/`endIndex`. A long document's rows page with\n `page.nextCursor`; send `maxChars: 0` on later pages, and start over if\n `revisionId` changed between them. Indexes are UTF-16 code units and\n shift after every edit. Keep `revisionId` and pass it as\n `requiredRevisionId`, so an edit against a document someone else has\n changed fails `conflict` with nothing applied; re-read and recompute. Indexes\n from a suggestions preview (`accepted`/`rejected`) are not valid for\n edits.\n- `raw: true` returns Google's document resource for the tabs instead of\n the rendering — styles, headers, footers, named ranges, list and segment ids\n that `batch_update_document` requests may need. It is large: pass\n `tabId`, and call it directly rather than inside `execute_code`,\n which carries at most 256 KiB per result.\n- The same holds for a `maxChars` above the default: a long rendering\n can pass here and still be refused inside `execute_code`, even when the\n program would only measure it. Read it with a direct `call_tool`, paged\n with `connecta.result`, or read one tab at a time.\n\n## Editing\n\n- `create_document` makes a new document in My Drive's root. Never create\n again after a failure that may have left one behind; the message says\n which. When the document exists but its starting text failed, the message\n names its id: append the text with `append_text` straight away only when\n Google refused it; otherwise read the document first and append only what\n is missing.\n- An edit sent with no answer back, answered with a server error, or\n redirected says its outcome is unknown: re-read before repeating it, or\n repeat it with the same `requiredRevisionId`, which Google refuses if the\n first attempt landed. One Google answered 2xx whose reply was unreadable\n says it was applied: do not repeat it.\n- `append_text` joins the last paragraph; begin with `\\n` for a new one.\n- `replace_all_text` changes every match at once; read first and make\n `find` specific.\n- `batch_update_document` takes raw Docs API requests (`deleteContentRange`,\n `updateTextStyle`, `insertTable`, `createParagraphBullets`, …) for what\n the named tools cannot do. It is always destructive, and requires the\n `requiredRevisionId` from the read it was planned against: up to 100\n requests, each exactly one generally available kind (the Developer Preview\n comment and suggestion kinds are refused). Several requests in one\n call apply atomically, and each sees the index shifts of those before it;\n order deletions from the end of the document backwards.\n"
|
|
8
8
|
}
|
|
9
9
|
};
|
|
@@ -48,6 +48,6 @@ export type DriveOptions = GoogleWorkspaceOptions;
|
|
|
48
48
|
* The `drive` scope reaches everything the user can; the tool surface is what
|
|
49
49
|
* keeps this connection from deleting a file permanently or transferring
|
|
50
50
|
* ownership. Every write crosses host approval through call_destructive_tool
|
|
51
|
-
* or a
|
|
51
|
+
* or a program on a writable endpoint.
|
|
52
52
|
*/
|
|
53
53
|
export declare const drive: import("../../provider.js").ProviderFactory<GoogleWorkspaceOptions>;
|
|
@@ -33,7 +33,7 @@ import { skill } from "./skill.generated.js";
|
|
|
33
33
|
import { apiConnector as api, defined } from "../../connectors/api-connector.js";
|
|
34
34
|
import { ConnectorCallError } from "../../errors.js";
|
|
35
35
|
import { RESULT_BUDGET_BYTES, clampText, jsonBytes } from "../_shared/google/result-size.js";
|
|
36
|
-
import { googleOutcomeOf, GOOGLE_WORKSPACE_OPTIONS, googleWorkspaceClient, workspaceConnection, } from "../_shared/google/workspace.js";
|
|
36
|
+
import { googleOutcomeOf, GOOGLE_WORKSPACE_OPTIONS, googleWorkspaceClient, workspaceConnection, workspaceConnector, } from "../_shared/google/workspace.js";
|
|
37
37
|
import { asProviderFactory } from "../../provider.js";
|
|
38
38
|
/**
|
|
39
39
|
* Drive's API root. Uploads use the same path beneath `/upload` on the same
|
|
@@ -1473,7 +1473,7 @@ function usageGuide(purpose, instructions) {
|
|
|
1473
1473
|
* The `drive` scope reaches everything the user can; the tool surface is what
|
|
1474
1474
|
* keeps this connection from deleting a file permanently or transferring
|
|
1475
1475
|
* ownership. Every write crosses host approval through call_destructive_tool
|
|
1476
|
-
* or a
|
|
1476
|
+
* or a program on a writable endpoint.
|
|
1477
1477
|
*/
|
|
1478
1478
|
export const drive = asProviderFactory({
|
|
1479
1479
|
name: "drive",
|
|
@@ -1504,7 +1504,7 @@ function driveConnector(id, options) {
|
|
|
1504
1504
|
};
|
|
1505
1505
|
const client = googleWorkspaceClient({ ...shared, baseUrl });
|
|
1506
1506
|
const upload = googleWorkspaceClient({ ...shared, baseUrl: uploadBaseUrl(baseUrl) });
|
|
1507
|
-
return api(id, {
|
|
1507
|
+
return workspaceConnector(connection, api(id, {
|
|
1508
1508
|
...defined({
|
|
1509
1509
|
callAdmission: options.callAdmission,
|
|
1510
1510
|
maxResultBytes: options.maxResultBytes,
|
|
@@ -1519,5 +1519,5 @@ function driveConnector(id, options) {
|
|
|
1519
1519
|
required: true,
|
|
1520
1520
|
},
|
|
1521
1521
|
tools: guarded(tools(client, upload)),
|
|
1522
|
-
});
|
|
1522
|
+
}));
|
|
1523
1523
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Google Drive usage\n\nActs as the signed-in person's own Google Drive through Workspace delegation: what they can open, and nothing they cannot.\n\nConnection purpose: \n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded. No
|
|
3
|
+
"content": "# Google Drive usage\n\nActs as the signed-in person's own Google Drive through Workspace delegation: what they can open, and nothing they cannot.\n\nConnection purpose: \n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No\nargument names an account. A call with none mapped, or from an agent whose\npool has no owner, fails `auth_required`; only an operator can change either. A missing file\nand one this person cannot see fail alike — Google does not distinguish them.\n\n## Finding files\n\n- `search_files` takes Drive query syntax: `name contains 'x'`,\n `fullText contains 'x'`, `mimeType = '…'`, `'<folderId>' in parents`,\n `modifiedTime > '2026-01-01T00:00:00'`. Quote values with single quotes.\n Trashed files are left out unless `includeTrashed`. `incompleteSearch:\n true` means Drive gave up before searching everything; narrow the query or\n the corpus. Shared drives: `list_shared_drives`, then `corpora: \"drive\"`\n with its `driveId`.\n- Page with `page.nextCursor` and the same arguments; a cursor from another\n tool or other arguments is refused. A page can stop short of `limit` to\n stay under `maxBytes`; the cursor resumes at the first row left out, and\n fails `conflict` if that page changed meanwhile — start over then.\n- Ids come from these reads or a Drive URL (`/d/<id>/`, `/folders/<id>`);\n never guess one.\n\n## Reading content\n\n- `get_file_content` exports Docs as Markdown, Sheets as CSV of the\n **first sheet only**, and Slides as plain text; reads text files as text;\n and returns other files as base64 when the encoded file fits the result. Anything else — a\n larger binary, a drawing, a form, a folder, a shortcut — comes back as\n `format: \"unavailable\"` with a `note`, never as an empty success.\n- Text is capped by `maxChars`; a cut ends with a truncation marker and\n `contentTruncated: true`. Drive exports at most 10 MB.\n- Every read stays under `maxBytes` of JSON. The default is what one result\n can carry into `execute_code`, so a program always receives it. A larger\n `maxBytes` (up to 4 MiB — a binary of up to 1 MiB) reaches only a direct\n `call_tool`, which pages it with `connecta.result`.\n\n## Writing\n\n- `create_folder` and `restore_file` add without losing or exposing\n anything.\n- A new file takes its folder's sharing, not its source's: `create_file`\n and `copy_file` into a shared folder disclose that content to everyone\n the folder is shared with, so both are approved like any destructive write.\n Check the destination with `list_permissions` first. `convertTo`\n imports content as a Google Doc, Sheet, or Slides file.\n- `update_file_content` replaces the whole content; send all of it.\n `update_file` renames; `move_file` changes the folder and with it who\n inherits access.\n- `trash_file` is recoverable for 30 days with `restore_file`. There is no\n permanent delete and no ownership transfer.\n- `share_file` emails no one unless `sendNotificationEmail: true`. Check\n `list_permissions` before and after changing access, and confirm with the\n person before sharing with `anyone` or a whole domain.\n",
|
|
4
4
|
"instructionsHeading": "Connection instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": "# Google Drive usage\n\nActs as the signed-in person's own Google Drive through Workspace delegation: what they can open, and nothing they cannot.\n\nConnection purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded. No
|
|
7
|
+
"guide_1": "\n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No\nargument names an account. A call with none mapped, or from an agent whose\npool has no owner, fails `auth_required`; only an operator can change either. A missing file\nand one this person cannot see fail alike — Google does not distinguish them.\n\n## Finding files\n\n- `search_files` takes Drive query syntax: `name contains 'x'`,\n `fullText contains 'x'`, `mimeType = '…'`, `'<folderId>' in parents`,\n `modifiedTime > '2026-01-01T00:00:00'`. Quote values with single quotes.\n Trashed files are left out unless `includeTrashed`. `incompleteSearch:\n true` means Drive gave up before searching everything; narrow the query or\n the corpus. Shared drives: `list_shared_drives`, then `corpora: \"drive\"`\n with its `driveId`.\n- Page with `page.nextCursor` and the same arguments; a cursor from another\n tool or other arguments is refused. A page can stop short of `limit` to\n stay under `maxBytes`; the cursor resumes at the first row left out, and\n fails `conflict` if that page changed meanwhile — start over then.\n- Ids come from these reads or a Drive URL (`/d/<id>/`, `/folders/<id>`);\n never guess one.\n\n## Reading content\n\n- `get_file_content` exports Docs as Markdown, Sheets as CSV of the\n **first sheet only**, and Slides as plain text; reads text files as text;\n and returns other files as base64 when the encoded file fits the result. Anything else — a\n larger binary, a drawing, a form, a folder, a shortcut — comes back as\n `format: \"unavailable\"` with a `note`, never as an empty success.\n- Text is capped by `maxChars`; a cut ends with a truncation marker and\n `contentTruncated: true`. Drive exports at most 10 MB.\n- Every read stays under `maxBytes` of JSON. The default is what one result\n can carry into `execute_code`, so a program always receives it. A larger\n `maxBytes` (up to 4 MiB — a binary of up to 1 MiB) reaches only a direct\n `call_tool`, which pages it with `connecta.result`.\n\n## Writing\n\n- `create_folder` and `restore_file` add without losing or exposing\n anything.\n- A new file takes its folder's sharing, not its source's: `create_file`\n and `copy_file` into a shared folder disclose that content to everyone\n the folder is shared with, so both are approved like any destructive write.\n Check the destination with `list_permissions` first. `convertTo`\n imports content as a Google Doc, Sheet, or Slides file.\n- `update_file_content` replaces the whole content; send all of it.\n `update_file` renames; `move_file` changes the folder and with it who\n inherits access.\n- `trash_file` is recoverable for 30 days with `restore_file`. There is no\n permanent delete and no ownership transfer.\n- `share_file` emails no one unless `sendNotificationEmail: true`. Check\n `list_permissions` before and after changing access, and confirm with the\n person before sharing with `anyone` or a whole domain.\n"
|
|
8
8
|
}
|
|
9
9
|
};
|
|
@@ -48,7 +48,7 @@ export type FormsOptions = GoogleWorkspaceOptions;
|
|
|
48
48
|
*
|
|
49
49
|
* Listing forms needs Drive, which this connection does not request; pair it
|
|
50
50
|
* with a Drive connection for that. `create_form` is an additive write the
|
|
51
|
-
* host approves through call_destructive_tool or a
|
|
51
|
+
* host approves through call_destructive_tool or a program on a writable endpoint;
|
|
52
52
|
* `update_form_info` and `batch_update_form` are destructive.
|
|
53
53
|
*/
|
|
54
54
|
export declare const forms: import("../../provider.js").ProviderFactory<GoogleWorkspaceOptions>;
|
|
@@ -28,7 +28,7 @@ import { skill } from "./skill.generated.js";
|
|
|
28
28
|
import { apiConnector as api, defined } from "../../connectors/api-connector.js";
|
|
29
29
|
import { ConnectorCallError } from "../../errors.js";
|
|
30
30
|
import { clampText, jsonBytes, RESULT_BUDGET_BYTES } from "../_shared/google/result-size.js";
|
|
31
|
-
import { googleOutcomeOf, GOOGLE_WORKSPACE_OPTIONS, googleWorkspaceClient, workspaceConnection, } from "../_shared/google/workspace.js";
|
|
31
|
+
import { googleOutcomeOf, GOOGLE_WORKSPACE_OPTIONS, googleWorkspaceClient, workspaceConnection, workspaceConnector, } from "../_shared/google/workspace.js";
|
|
32
32
|
import { asProviderFactory } from "../../provider.js";
|
|
33
33
|
/**
|
|
34
34
|
* The Forms API root. Every path the tools send sits beneath `/forms`; there
|
|
@@ -806,7 +806,7 @@ function tools(client) {
|
|
|
806
806
|
name: "create_form",
|
|
807
807
|
description: "Create an empty Google Form with a title in the user's Drive. Google sets only the title here: add questions and a description with batch_update_form.",
|
|
808
808
|
// Additive: a new, empty form changes nothing that existed. Not
|
|
809
|
-
// read-only: program calls require a
|
|
809
|
+
// read-only: program calls require a writable endpoint; other writes use
|
|
810
810
|
// call_destructive_tool. The host controls approval.
|
|
811
811
|
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
812
812
|
inputSchema: input({
|
|
@@ -1168,7 +1168,7 @@ function usageGuide(purpose, instructions) {
|
|
|
1168
1168
|
*
|
|
1169
1169
|
* Listing forms needs Drive, which this connection does not request; pair it
|
|
1170
1170
|
* with a Drive connection for that. `create_form` is an additive write the
|
|
1171
|
-
* host approves through call_destructive_tool or a
|
|
1171
|
+
* host approves through call_destructive_tool or a program on a writable endpoint;
|
|
1172
1172
|
* `update_form_info` and `batch_update_form` are destructive.
|
|
1173
1173
|
*/
|
|
1174
1174
|
export const forms = asProviderFactory({
|
|
@@ -1198,7 +1198,7 @@ function formsConnector(id, options) {
|
|
|
1198
1198
|
notFound: "ambiguous",
|
|
1199
1199
|
connection,
|
|
1200
1200
|
});
|
|
1201
|
-
return api(id, {
|
|
1201
|
+
return workspaceConnector(connection, api(id, {
|
|
1202
1202
|
...defined({
|
|
1203
1203
|
callAdmission: options.callAdmission,
|
|
1204
1204
|
maxResultBytes: options.maxResultBytes,
|
|
@@ -1213,5 +1213,5 @@ function formsConnector(id, options) {
|
|
|
1213
1213
|
required: true,
|
|
1214
1214
|
},
|
|
1215
1215
|
tools: tools(client),
|
|
1216
|
-
});
|
|
1216
|
+
}));
|
|
1217
1217
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Google Forms usage\n\nActs as the signed-in person in Google Forms through Workspace delegation: read and edit their forms, read responses, never submit or delete.\n\nConnection purpose: \n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open. No argument names a user. A call with none mapped\
|
|
3
|
+
"content": "# Google Forms usage\n\nActs as the signed-in person in Google Forms through Workspace delegation: read and edit their forms, read responses, never submit or delete.\n\nConnection purpose: \n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names a user. A call with none mapped, or from an agent\nwhose pool has no owner, fails `auth_required`; only an operator can change\neither. A form id\nthat does not exist and one not shared with that user fail alike, as\n`connector_call_failed`: Google does not say which.\n\n## Finding forms\n\nThere is no list or search here: the Forms API has none. Find a form in\nDrive, or take its id from an edit URL (`/forms/d/<formId>/edit`). The\n`/forms/d/e/…` responder link is not the form id.\n\n## Reading\n\n- Every result stays inside KiB, so it crosses into\n `execute_code` whole. A page that would outgrow that ends early with\n `page.hasMore`; follow `page.nextCursor` rather than raising `limit`.\n- `get_form` lists items with their `questions`: one for a question,\n one per row for a grid, none for sections and media. Answers are keyed by\n those `questionId`s. A large form pages its items (`itemCount` is the\n total), and a grid too large for one page continues on the next: the item\n repeats with `questionsFrom`, the index of its first row there, and\n `moreQuestions` counts rows still to come. A cursor from a form that has\n since changed fails `conflict`.\n Long text is cut with a marker; `raw: true` returns Google's whole Form\n resource, or refuses one too large for a result.\n- `list_responses` labels each answer with its question's title, in form\n order; an answer to a since-deleted question has no title. Answers longer\n than characters end with a truncation marker; `get_response`\n reads one whole. Page with `page.nextCursor` and the same\n `submittedAfter`. To read only new responses, pass the newest\n `lastSubmittedTime` already seen as `submittedAfter`. A cursor\n continues after the last response it returned; if Google's page has\n changed since (new, deleted, or reordered responses), it fails\n `conflict` — start again, or use `submittedAfter`.\n- A response too large for one result comes back cut, with the answers left\n out named in `omittedQuestionIds`; pass them to `get_response` as\n `questionIds`.\n\n## Editing\n\n- `create_form` makes an empty form: Google takes only its title and Drive\n file name at creation. Add a description and questions afterwards.\n- `update_form_info` replaces the title or description.\n- `batch_update_form` is the escape hatch for everything else, and always\n needs approval. It takes Google's own Request objects, applied all or none,\n with the form's current `revisionId` as `requiredRevisionId`: a form\n someone changed since you read it is refused as `conflict`, so re-read it\n and rebuild the edit. Its reply carries the new `revisionId` and each created item's id.\n A reply too large to return whole comes back with `truncated: true`: the\n batch still applied; read the rest with `get_form`. An `updateItem` needs an `updateMask`; read `raw: true` first so the\n item it replaces keeps its grading, images, and navigation.\n- Publishing, sharing, deleting, and watches are not in this connection; the\n owner does those in Forms or Drive. Responses are read-only.\n",
|
|
4
4
|
"instructionsHeading": "Connection instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": "# Google Forms usage\n\nActs as the signed-in person in Google Forms through Workspace delegation: read and edit their forms, read responses, never submit or delete.\n\nConnection purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open. No argument names a user. A call with none mapped\
|
|
7
|
+
"guide_1": "\n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names a user. A call with none mapped, or from an agent\nwhose pool has no owner, fails `auth_required`; only an operator can change\neither. A form id\nthat does not exist and one not shared with that user fail alike, as\n`connector_call_failed`: Google does not say which.\n\n## Finding forms\n\nThere is no list or search here: the Forms API has none. Find a form in\nDrive, or take its id from an edit URL (`/forms/d/<formId>/edit`). The\n`/forms/d/e/…` responder link is not the form id.\n\n## Reading\n\n- Every result stays inside ",
|
|
8
8
|
"guide_2": " KiB, so it crosses into\n `execute_code` whole. A page that would outgrow that ends early with\n `page.hasMore`; follow `page.nextCursor` rather than raising `limit`.\n- `get_form` lists items with their `questions`: one for a question,\n one per row for a grid, none for sections and media. Answers are keyed by\n those `questionId`s. A large form pages its items (`itemCount` is the\n total), and a grid too large for one page continues on the next: the item\n repeats with `questionsFrom`, the index of its first row there, and\n `moreQuestions` counts rows still to come. A cursor from a form that has\n since changed fails `conflict`.\n Long text is cut with a marker; `raw: true` returns Google's whole Form\n resource, or refuses one too large for a result.\n- `list_responses` labels each answer with its question's title, in form\n order; an answer to a since-deleted question has no title. Answers longer\n than ",
|
|
9
9
|
"guide_3": " characters end with a truncation marker; `get_response`\n reads one whole. Page with `page.nextCursor` and the same\n `submittedAfter`. To read only new responses, pass the newest\n `lastSubmittedTime` already seen as `submittedAfter`. A cursor\n continues after the last response it returned; if Google's page has\n changed since (new, deleted, or reordered responses), it fails\n `conflict` — start again, or use `submittedAfter`.\n- A response too large for one result comes back cut, with the answers left\n out named in `omittedQuestionIds`; pass them to `get_response` as\n `questionIds`.\n\n## Editing\n\n- `create_form` makes an empty form: Google takes only its title and Drive\n file name at creation. Add a description and questions afterwards.\n- `update_form_info` replaces the title or description.\n- `batch_update_form` is the escape hatch for everything else, and always\n needs approval. It takes Google's own Request objects, applied all or none,\n with the form's current `revisionId` as `requiredRevisionId`: a form\n someone changed since you read it is refused as `conflict`, so re-read it\n and rebuild the edit. Its reply carries the new `revisionId` and each created item's id.\n A reply too large to return whole comes back with `truncated: true`: the\n batch still applied; read the rest with `get_form`. An `updateItem` needs an `updateMask`; read `raw: true` first so the\n item it replaces keeps its grading, images, and navigation.\n- Publishing, sharing, deleting, and watches are not in this connection; the\n owner does those in Forms or Drive. Responses are read-only.\n"
|
|
10
10
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Setup\n\nCreate a GitHub App, generate its RSA private key, and install it on every\nconfigured owner. Org grants require **All repositories**, including future\nrepositories. Exact repo grants work with selected-repository installations.\nRead-only grants request read permissions; read-write grants request only the\npermissions needed by each operation. The App needs repository Metadata,\nContents, Issues, Pull requests, Actions, Checks and Commit statuses for the\nreviewed read tools. Grant Contents, Issues, Pull requests and Actions write\nonly for the operations the deployment needs. Workflow-file edits also need\nthe App's Workflows write permission and `workflows: \"write\"` on the effective\nscope. Org grants authorize repository operations, not organization administration.\n\nPass `app: { appId, privateKey }` with the key from an environment secret, or\nomit privateKey and populate the connector's encrypted `privateKey` vault field.\nNever put private keys or installation tokens in agent instructions. Tokens\nstay in bounded runtime memory, refresh a minute before expiry, and are never\npersisted. Key rotation partitions the caches by the key's fingerprint.\nConfigured repository names bind to repository IDs on first use. Bindings last\nfor the provider instance; name lookups expire after five minutes. A rename\nkeeps its grant, while a replacement repository at the old name does not.\nReconfigure to authorize a replacement. Transfers leave the original\ninstallation and cannot use its tokens. Org grants bind to installation IDs.\n\n## Choose the target first\n\n`list_scopes({})` returns configured org/repo grants without network access.\n`list_scopes({ owner })` resolves that owner's installation and pages reachable\nrepositories. Carry its `next_page`. The connection context lists configured\nscopes and access. A repo entry overrides and may narrow its parent org entry.\nAn org grant covers current and future repositories, as long as the installation\nkeeps All repositories selected.\n\nEvery hosted call requires explicit `owner` and `repo`. Connecta checks scope\nand write access before reading the key or minting a token, and narrows each\nrepo call's token with `repository_ids` and the reviewed permissions.\nAn internal metadata-only installation token resolves names through GitHub's\ninstallation repository list; it never authenticates hosted calls or search.\nExpiry and misses refresh the name lookup without rebinding configured grants.\nUse discovery or the configured name to refresh a rename before using its new\nname; names that cannot map to a configured grant fail before operation tokens\nare minted. Cross-owner\nPR heads, cross-repository issue parents, opaque comment/review-thread IDs and\nsymlink writes are refused. The first release acts as the App; personal OAuth\nattribution is planned separately. Classification describes behavior; scope\nchecks authorize targets
|
|
3
|
+
"content": "## Setup\n\nCreate a GitHub App, generate its RSA private key, and install it on every\nconfigured owner. Org grants require **All repositories**, including future\nrepositories. Exact repo grants work with selected-repository installations.\nRead-only grants request read permissions; read-write grants request only the\npermissions needed by each operation. The App needs repository Metadata,\nContents, Issues, Pull requests, Actions, Checks and Commit statuses for the\nreviewed read tools. Grant Contents, Issues, Pull requests and Actions write\nonly for the operations the deployment needs. Workflow-file edits also need\nthe App's Workflows write permission and `workflows: \"write\"` on the effective\nscope. Org grants authorize repository operations, not organization administration.\n\nPass `app: { appId, privateKey }` with the key from an environment secret, or\nomit privateKey and populate the connector's encrypted `privateKey` vault field.\nNever put private keys or installation tokens in agent instructions. Tokens\nstay in bounded runtime memory, refresh a minute before expiry, and are never\npersisted. Key rotation partitions the caches by the key's fingerprint.\nConfigured repository names bind to repository IDs on first use. Bindings last\nfor the provider instance; name lookups expire after five minutes. A rename\nkeeps its grant, while a replacement repository at the old name does not.\nReconfigure to authorize a replacement. Transfers leave the original\ninstallation and cannot use its tokens. Org grants bind to installation IDs.\n\n## Choose the target first\n\n`list_scopes({})` returns configured org/repo grants without network access.\n`list_scopes({ owner })` resolves that owner's installation and pages reachable\nrepositories. Carry its `next_page`. The connection context lists configured\nscopes and access. A repo entry overrides and may narrow its parent org entry.\nAn org grant covers current and future repositories, as long as the installation\nkeeps All repositories selected.\n\nEvery hosted call requires explicit `owner` and `repo`. Connecta checks scope\nand write access before reading the key or minting a token, and narrows each\nrepo call's token with `repository_ids` and the reviewed permissions.\nAn internal metadata-only installation token resolves names through GitHub's\ninstallation repository list; it never authenticates hosted calls or search.\nExpiry and misses refresh the name lookup without rebinding configured grants.\nUse discovery or the configured name to refresh a rename before using its new\nname; names that cannot map to a configured grant fail before operation tokens\nare minted. Cross-owner\nPR heads, cross-repository issue parents, opaque comment/review-thread IDs and\nsymlink writes are refused. The first release acts as the App; personal OAuth\nattribution is planned separately. Classification describes behavior; scope\nchecks authorize targets whether or not the endpoint is read-only.\n\n## Tools and paging\n\nHosted schemas and results come from GitHub. Reviewed reads cover files,\ncommits, branches, tags, releases, issues, labels, PR details/diffs/reviews,\nand Actions workflows/runs/jobs. Reviewed writes cover issues, comments, PR\ncreation/updates/reviews, branches, file edits and Actions dispatch/rerun/cancel.\nCompound `issue_write` is always a write, including update/close. Review methods\nare limited to create/submit_pending/delete_pending; resolve/unresolve by opaque thread ID are\nnot exposed. Scope-changing tools, repo creation/forks, deletion/transfer,\nadministration, secrets and Copilot delegation are absent. Unknown upstream\ntools, arguments and methods require review before use.\n\nUse `search_scoped` for repositories, issues, PRs or code across installations.\nSupply literal words and an optional subset of configured org/repo selectors.\nScope qualifiers and Boolean operators are refused. Results have a partition\nID, next page, and an incomplete-results flag. Pass pages keyed by those exact\npartition IDs. Each call makes at most 20 partition requests; select fewer\nscopes when needed. Search pages stop at 10 and retain incompleteness. Returned\nrepositories are checked by ID against both deployment scopes and selected\npartitions. Issue and PR results resolve their repository URL to an ID before\nany result is returned.\nInstallation tokens do not override GitHub's own code-search access restrictions.\n\nREST complements own `create_release`, `update_release`, `delete_release` and\n`merge_pull_request`. Publishing a draft is `update_release({ draft: false })`.\nMerge requires an expected head SHA, a same-repository head/base and explicit\nworkflow-write access on the effective scope. GitHub atomically guards only the\nhead SHA, so a concurrent base retarget can introduce workflow changes despite\nan earlier file comparison. Merge is therefore disabled by default.\nRead current state before updates. A dispatched write is attempted once;\nGitHub errors never trigger an automatic write retry. Honor retry timing on\nrate limits; an ambiguous 404 may mean absence or an installation permission\ngap. Fix the App installation/key/permissions for auth errors; the App has no\ninteractive OAuth reconnect flow.\n",
|
|
4
4
|
"instructionsHeading": "Repository instructions",
|
|
5
5
|
"fragments": {
|
|
6
|
-
"guide": "## Setup\n\nCreate a GitHub App, generate its RSA private key, and install it on every\nconfigured owner. Org grants require **All repositories**, including future\nrepositories. Exact repo grants work with selected-repository installations.\nRead-only grants request read permissions; read-write grants request only the\npermissions needed by each operation. The App needs repository Metadata,\nContents, Issues, Pull requests, Actions, Checks and Commit statuses for the\nreviewed read tools. Grant Contents, Issues, Pull requests and Actions write\nonly for the operations the deployment needs. Workflow-file edits also need\nthe App's Workflows write permission and `workflows: \"write\"` on the effective\nscope. Org grants authorize repository operations, not organization administration.\n\nPass `app: { appId, privateKey }` with the key from an environment secret, or\nomit privateKey and populate the connector's encrypted `privateKey` vault field.\nNever put private keys or installation tokens in agent instructions. Tokens\nstay in bounded runtime memory, refresh a minute before expiry, and are never\npersisted. Key rotation partitions the caches by the key's fingerprint.\nConfigured repository names bind to repository IDs on first use. Bindings last\nfor the provider instance; name lookups expire after five minutes. A rename\nkeeps its grant, while a replacement repository at the old name does not.\nReconfigure to authorize a replacement. Transfers leave the original\ninstallation and cannot use its tokens. Org grants bind to installation IDs.\n\n## Choose the target first\n\n`list_scopes({})` returns configured org/repo grants without network access.\n`list_scopes({ owner })` resolves that owner's installation and pages reachable\nrepositories. Carry its `next_page`. The connection context lists configured\nscopes and access. A repo entry overrides and may narrow its parent org entry.\nAn org grant covers current and future repositories, as long as the installation\nkeeps All repositories selected.\n\nEvery hosted call requires explicit `owner` and `repo`. Connecta checks scope\nand write access before reading the key or minting a token, and narrows each\nrepo call's token with `repository_ids` and the reviewed permissions.\nAn internal metadata-only installation token resolves names through GitHub's\ninstallation repository list; it never authenticates hosted calls or search.\nExpiry and misses refresh the name lookup without rebinding configured grants.\nUse discovery or the configured name to refresh a rename before using its new\nname; names that cannot map to a configured grant fail before operation tokens\nare minted. Cross-owner\nPR heads, cross-repository issue parents, opaque comment/review-thread IDs and\nsymlink writes are refused. The first release acts as the App; personal OAuth\nattribution is planned separately. Classification describes behavior; scope\nchecks authorize targets
|
|
6
|
+
"guide": "## Setup\n\nCreate a GitHub App, generate its RSA private key, and install it on every\nconfigured owner. Org grants require **All repositories**, including future\nrepositories. Exact repo grants work with selected-repository installations.\nRead-only grants request read permissions; read-write grants request only the\npermissions needed by each operation. The App needs repository Metadata,\nContents, Issues, Pull requests, Actions, Checks and Commit statuses for the\nreviewed read tools. Grant Contents, Issues, Pull requests and Actions write\nonly for the operations the deployment needs. Workflow-file edits also need\nthe App's Workflows write permission and `workflows: \"write\"` on the effective\nscope. Org grants authorize repository operations, not organization administration.\n\nPass `app: { appId, privateKey }` with the key from an environment secret, or\nomit privateKey and populate the connector's encrypted `privateKey` vault field.\nNever put private keys or installation tokens in agent instructions. Tokens\nstay in bounded runtime memory, refresh a minute before expiry, and are never\npersisted. Key rotation partitions the caches by the key's fingerprint.\nConfigured repository names bind to repository IDs on first use. Bindings last\nfor the provider instance; name lookups expire after five minutes. A rename\nkeeps its grant, while a replacement repository at the old name does not.\nReconfigure to authorize a replacement. Transfers leave the original\ninstallation and cannot use its tokens. Org grants bind to installation IDs.\n\n## Choose the target first\n\n`list_scopes({})` returns configured org/repo grants without network access.\n`list_scopes({ owner })` resolves that owner's installation and pages reachable\nrepositories. Carry its `next_page`. The connection context lists configured\nscopes and access. A repo entry overrides and may narrow its parent org entry.\nAn org grant covers current and future repositories, as long as the installation\nkeeps All repositories selected.\n\nEvery hosted call requires explicit `owner` and `repo`. Connecta checks scope\nand write access before reading the key or minting a token, and narrows each\nrepo call's token with `repository_ids` and the reviewed permissions.\nAn internal metadata-only installation token resolves names through GitHub's\ninstallation repository list; it never authenticates hosted calls or search.\nExpiry and misses refresh the name lookup without rebinding configured grants.\nUse discovery or the configured name to refresh a rename before using its new\nname; names that cannot map to a configured grant fail before operation tokens\nare minted. Cross-owner\nPR heads, cross-repository issue parents, opaque comment/review-thread IDs and\nsymlink writes are refused. The first release acts as the App; personal OAuth\nattribution is planned separately. Classification describes behavior; scope\nchecks authorize targets whether or not the endpoint is read-only.\n\n## Tools and paging\n\nHosted schemas and results come from GitHub. Reviewed reads cover files,\ncommits, branches, tags, releases, issues, labels, PR details/diffs/reviews,\nand Actions workflows/runs/jobs. Reviewed writes cover issues, comments, PR\ncreation/updates/reviews, branches, file edits and Actions dispatch/rerun/cancel.\nCompound `issue_write` is always a write, including update/close. Review methods\nare limited to create/submit_pending/delete_pending; resolve/unresolve by opaque thread ID are\nnot exposed. Scope-changing tools, repo creation/forks, deletion/transfer,\nadministration, secrets and Copilot delegation are absent. Unknown upstream\ntools, arguments and methods require review before use.\n\nUse `search_scoped` for repositories, issues, PRs or code across installations.\nSupply literal words and an optional subset of configured org/repo selectors.\nScope qualifiers and Boolean operators are refused. Results have a partition\nID, next page, and an incomplete-results flag. Pass pages keyed by those exact\npartition IDs. Each call makes at most 20 partition requests; select fewer\nscopes when needed. Search pages stop at 10 and retain incompleteness. Returned\nrepositories are checked by ID against both deployment scopes and selected\npartitions. Issue and PR results resolve their repository URL to an ID before\nany result is returned.\nInstallation tokens do not override GitHub's own code-search access restrictions.\n\nREST complements own `create_release`, `update_release`, `delete_release` and\n`merge_pull_request`. Publishing a draft is `update_release({ draft: false })`.\nMerge requires an expected head SHA, a same-repository head/base and explicit\nworkflow-write access on the effective scope. GitHub atomically guards only the\nhead SHA, so a concurrent base retarget can introduce workflow changes despite\nan earlier file comparison. Merge is therefore disabled by default.\nRead current state before updates. A dispatched write is attempted once;\nGitHub errors never trigger an automatic write retry. Honor retry timing on\nrate limits; an ambiguous 404 may mean absence or an installation permission\ngap. Fix the App installation/key/permissions for auth errors; the App has no\ninteractive OAuth reconnect flow.\n"
|
|
7
7
|
}
|
|
8
8
|
};
|
|
@@ -46,6 +46,6 @@ export type GmailOptions = GoogleWorkspaceOptions;
|
|
|
46
46
|
*
|
|
47
47
|
* `gmail.compose` technically allows sending; this connection never does,
|
|
48
48
|
* because no tool reaches a send method. Drafts are additive writes the host
|
|
49
|
-
* approves through call_destructive_tool or a
|
|
49
|
+
* approves through call_destructive_tool or a program on a writable endpoint.
|
|
50
50
|
*/
|
|
51
51
|
export declare const gmail: import("../../provider.js").ProviderFactory<GoogleWorkspaceOptions>;
|