@zackbart/connecta 0.37.1 → 0.39.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 +94 -0
- package/README.md +5 -4
- package/bin/connecta.mjs +1 -39
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.js +2 -4
- package/dist/agent-identity.js +1 -1
- package/dist/agent-routes.js +231 -0
- package/dist/auth/clerk-sdk/generated.js +2 -1
- package/dist/auth/downstream-oauth/consent.js +519 -0
- package/dist/auth/downstream-oauth/grant-store.js +439 -0
- package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
- package/dist/auth/downstream-oauth/refresh-records.js +447 -0
- package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
- package/dist/auth/downstream-oauth.js +66 -2516
- package/dist/auth/downstream-revocation.js +1 -1
- package/dist/auth/native.js +27 -0
- package/dist/auth/oauth-grant.js +24 -0
- package/dist/auth/oauth-token-response.js +1 -1
- package/dist/auth/static-oauth.js +153 -131
- package/dist/auth/vercel-agent.js +2 -1
- package/dist/auth-elicitation.js +9 -3
- package/dist/authorization-handoff.js +11 -5
- package/dist/branding.d.ts +1 -15
- package/dist/branding.js +2 -2
- package/dist/bytes.js +42 -0
- package/dist/call-admission-context.js +2 -5
- package/dist/call-recovery.js +0 -3
- package/dist/catalog-cache.js +130 -43
- package/dist/catalog-fingerprint.js +2 -18
- package/dist/catalog-policy.js +18 -0
- package/dist/catalog-service.js +285 -48
- package/dist/catalog.js +18 -2
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +1 -1
- package/dist/config-schema.js +6 -2
- package/dist/config.d.ts +57 -25
- package/dist/config.js +30 -77
- package/dist/connector-caller.d.ts +1 -1
- package/dist/connector-health.js +59 -0
- package/dist/connector-status.js +117 -0
- package/dist/connectors/api-connector.d.ts +7 -2
- package/dist/connectors/api-connector.js +11 -7
- package/dist/connectors/guarded-fetch.d.ts +0 -6
- package/dist/connectors/guarded-fetch.js +1 -11
- package/dist/connectors/negotiation-cache.js +22 -4
- package/dist/connectors/remote-mcp/call.js +164 -0
- package/dist/connectors/remote-mcp/catalog.js +278 -0
- package/dist/connectors/remote-mcp/config.js +263 -0
- package/dist/connectors/remote-mcp/connection.js +671 -0
- package/dist/connectors/remote-mcp/errors.js +488 -0
- package/dist/connectors/remote-mcp/oauth.js +469 -0
- package/dist/connectors/remote-mcp/resources.js +473 -0
- package/dist/connectors/remote-mcp/runtime.js +1 -0
- package/dist/connectors/remote-mcp/transport.js +544 -0
- package/dist/connectors/remote-mcp.js +61 -2898
- package/dist/connectors/resource-uri.js +0 -4
- package/dist/credential-contract.d.ts +23 -0
- package/dist/credential-reads.js +17 -0
- package/dist/credential-verification.js +53 -0
- package/dist/credentials.d.ts +7 -0
- package/dist/credentials.js +60 -25
- package/dist/d1.d.ts +0 -89
- package/dist/d1.js +0 -34
- package/dist/describe-config.d.ts +12 -2
- package/dist/describe-config.js +2 -0
- package/dist/discovery-health.js +199 -0
- package/dist/downstream-input.js +4 -2
- package/dist/errors.d.ts +55 -17
- package/dist/errors.js +95 -41
- package/dist/execute.js +172 -125
- package/dist/executor-result.js +5 -2
- package/dist/executors/quickjs-runtime.js +2 -25
- package/dist/executors/quickjs.js +1 -1
- package/dist/guest-functions.js +83 -0
- package/dist/guest-runtime.js +63 -20
- package/dist/guest-types.d.ts +24 -10
- package/dist/http/argument-problems.js +35 -0
- package/dist/http/client.js +1 -1
- package/dist/http/errors.js +7 -6
- package/dist/http/fields.js +192 -13
- package/dist/http/hatch-response.js +73 -0
- package/dist/http/hatch.js +88 -23
- package/dist/http/json-schema.js +1 -1
- package/dist/http/node-transport.js +0 -1
- package/dist/http/oauth.js +4 -3
- package/dist/http/paging.js +6 -9
- package/dist/http/provider.js +33 -5
- package/dist/http/safety.js +9 -1
- package/dist/http/services.js +1 -8
- package/dist/http/tools.js +45 -30
- package/dist/http/vendor.js +7 -7
- package/dist/identity.js +2 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -2
- package/dist/invocation-auth.js +2 -1
- package/dist/invocation.js +21 -6
- package/dist/membership.d.ts +13 -5
- package/dist/membership.js +34 -2
- package/dist/meta-tools.js +50 -35
- package/dist/native-recovery.js +4 -16
- package/dist/native-tools.js +6 -2
- package/dist/oauth-handoff.js +2 -2
- package/dist/oauth-sealing.js +10 -0
- package/dist/operation-vocabulary.js +136 -14
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +2 -49
- package/dist/operator-ui/contract.d.ts +12 -2
- package/dist/operator-ui/generated.js +3 -3
- package/dist/operator-ui/model.d.ts +22 -1
- package/dist/program-writes.js +3 -8
- package/dist/provider-call.js +0 -7
- package/dist/provider.d.ts +1 -1
- package/dist/providers/_shared/google/kit.js +1 -6
- package/dist/providers/_shared/google/service-account.js +1 -3
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/_shared/guide.js +3 -0
- package/dist/providers/_shared/json.js +12 -0
- package/dist/providers/_shared/rest/compact-index.js +1 -1
- package/dist/providers/_shared/rest/kit.js +1 -3
- package/dist/providers/_shared/rest/operation-index.js +49 -53
- package/dist/providers/_shared/rest/value-safety.js +106 -28
- package/dist/providers/breeze/api.js +9 -8
- package/dist/providers/breeze/index.js +4 -2
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/breeze/tools.js +7 -7
- package/dist/providers/ccb/api.js +3 -7
- package/dist/providers/ccb/index.js +7 -1
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/ccb/tools.js +1 -6
- package/dist/providers/cloudflare/api.js +10 -3
- package/dist/providers/cloudflare/index.js +2 -1
- package/dist/providers/cloudflare/safety.js +3 -3
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/lib/schemas.js +24 -22
- package/dist/providers/docs/safety.js +2 -0
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/docs/tools.js +4 -14
- package/dist/providers/drive/api.js +1 -0
- package/dist/providers/drive/lib/contracts.js +8 -5
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/drive/tools.js +23 -41
- package/dist/providers/forms/api.js +1 -0
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/forms/tools.js +11 -16
- package/dist/providers/github/api.js +1 -0
- package/dist/providers/github/lib/discovery.js +1 -1
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +61 -17
- package/dist/providers/gmail/lib/schemas.js +60 -50
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/gmail/tools.js +12 -26
- package/dist/providers/granola/index.js +2 -0
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/api.js +8 -4
- package/dist/providers/overflow/index.js +2 -1
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/overflow/tools.js +3 -7
- package/dist/providers/planning-center/api.js +5 -2
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/lib/json-api.js +1 -9
- package/dist/providers/planning-center/skill.generated.js +1 -1
- package/dist/providers/planning-center/tools.js +2 -1
- package/dist/providers/quickbooks/safety.js +80 -39
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/api.js +1 -0
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/sheets/tools.js +14 -23
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/slides/tools.js +19 -26
- package/dist/providers/stripe/index.js +2 -1
- package/dist/providers/stripe/safety.js +2 -2
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/api.js +5 -8
- package/dist/providers/tithely/index.js +3 -2
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/tithely/tools.js +7 -5
- package/dist/providers/vercel/api.js +2 -7
- package/dist/providers/vercel/lib/pin.js +1 -1
- package/dist/providers/vercel/safety.js +1 -1
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/providers/vercel/tools.js +6 -4
- package/dist/redaction-marker.js +2 -2
- package/dist/registry.d.ts +11 -5
- package/dist/registry.js +46 -20
- package/dist/request-principal.js +1 -1
- package/dist/request-state.js +2 -2
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/activity-disclosure.js +1 -1
- package/dist/routes/connect.js +2 -2
- package/dist/routes/credentials.js +130 -61
- package/dist/routes/dispatch.js +5 -1
- package/dist/routes/mcp.js +34 -17
- package/dist/routes/oauth-management.js +1 -1
- package/dist/routes/operator.js +6 -5
- package/dist/routes/shared.d.ts +13 -8
- package/dist/routes/shared.js +15 -9
- package/dist/routes/ui-config.js +3 -3
- package/dist/routes/ui-data.js +126 -20
- package/dist/routes/ui.js +17 -1
- package/dist/runtime/call-admission.js +1 -1
- package/dist/runtime/call.js +8 -3
- package/dist/runtime/native-storage.js +9 -0
- package/dist/runtime/request-reads.js +182 -0
- package/dist/runtime/request-scope.js +16 -0
- package/dist/runtime/settlement.js +1 -1
- package/dist/runtime/shared-read.js +2 -15
- package/dist/runtime/storage.js +12 -0
- package/dist/sent-secrets.d.ts +1 -2
- package/dist/sent-secrets.js +3 -8
- package/dist/server.js +4 -4
- package/dist/skills.js +44 -54
- package/dist/sqlite.d.ts +0 -19
- package/dist/sqlite.js +2 -83
- package/dist/storage/keys.js +69 -128
- package/dist/storage/memory.js +8 -2
- package/dist/storage/sql-native.js +27 -101
- package/dist/storage/sql.d.ts +1 -24
- package/dist/storage/sql.js +13 -14
- package/dist/storage-digest.js +32 -0
- package/dist/timeout.js +8 -0
- package/dist/tool-error.js +4 -0
- package/dist/types.d.ts +33 -0
- package/dist/url-safety.js +0 -4
- package/dist/usage-guide.js +18 -37
- package/dist/validate.d.ts +3 -0
- package/dist/validate.js +220 -158
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +1 -1
- package/dist/write-outcome.js +0 -3
- package/documentation/README.md +19 -55
- package/documentation/architecture.md +436 -515
- package/documentation/auth.md +229 -88
- package/documentation/code-mode.md +201 -94
- package/documentation/deploying.md +25 -43
- package/documentation/integrating.md +18 -19
- package/documentation/meta-tools.md +225 -51
- package/documentation/migration-0.33.md +10 -6
- package/documentation/migration-0.34.md +5 -5
- package/documentation/migration-retired-layouts.md +74 -0
- package/documentation/operator-tests.md +20 -120
- package/documentation/operator-ui.md +108 -16
- package/documentation/pool-membership-upgrade.md +44 -0
- package/documentation/provider-auth-migration.md +4 -10
- package/documentation/provider-migration-0.29.md +4 -15
- package/documentation/providers.md +105 -0
- package/examples/worker/AGENTS.md +14 -16
- package/examples/worker/README.md +24 -118
- package/examples/worker/src/connecta.config.ts +1 -0
- package/guides/connecting-agents.md +9 -31
- package/guides/how-it-works.md +6 -4
- package/guides/safety-and-trust.md +4 -1
- package/package.json +19 -19
- package/templates/node/AGENTS.md +13 -12
- package/templates/node/README.md +9 -26
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +1 -0
- package/dist/auth/oauth-v2-migration.js +0 -135
- package/dist/provider-call-facts.d.ts +0 -115
- package/dist/provider-call.d.ts +0 -112
- package/dist/storage/d1-copy.js +0 -255
- package/dist/storage/sql-schema.d.ts +0 -6
- package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
- package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Identity first\n\nA People person id joins records across products. Resolve people by identity,\nthen join on `person_id`; names can repeat. A membership, scheduled person,\nworkflow card and person each have their own record id.\n\n`get_me` identifies the token's user and the organization's time zone. Bare\ndates mean the organization's local day; use a timestamp with an offset for\nan exact boundary. Name filters are exact unless their value ends in `%`.\n\nAn old person id may have been merged. Read `/people/v2/person_mergers` with\n`where[person_to_remove_id]`, follow `person_to_keep_id`, then read that profile.\nAn empty history does not establish that a person was merged.\n\nReduce pastoral information inside `execute_code` and return aggregates unless\nthe task needs named people. A named projection omits some safe vendor fields;\nread the indexed record when the task needs those fields.\n\n<!-- when: people -->\n## People and follow-up\n\nResolve a person before changing their profile. An omitted attribute leaves\nthe current value alone; `null` clears it. Archiving a person is reversible;\ndeleting a profile is permanent.\n\nWorkflow cards join people to a workflow and its ordered steps. Resolve both\nthe card id and its person id before acting on a card. Snoozing needs a duration;\npromoting a card completes its step, while skipping moves on without completing\nit. A workflow card note and a profile note are different records. Profile note\ncategories can notify their subscribers.\n\nCustom field values need their field-definition records to supply names and\ndata types. Form answers similarly join submissions, values and fields; the\nAPI cannot submit a form on someone's behalf.\n<!-- end -->\n\n<!-- when: lists -->\nSaved People lists are live cross-product queries. Their results are only as\nfresh as their last refresh. Refreshing a list can fire its automations, such\nas adding a person to a workflow, and is a write even when the task is a count.\n<!-- end -->\n\n<!-- when: services -->\n## Services plans and schedules\n\nResolve the service type before finding a dated plan. Plan times define the\nservice dates. `get_plan` combines the plan, order of service and scheduled\nteam; a cut section can be read through its indexed items or team-members path.\nItem durations are seconds, so divide by 60 when reporting minutes.\n\nScheduling needs a team and may need a position. `C` means confirmed, `U`\nunconfirmed and `D` declined. Preparing a notification and sending it are\ndifferent actions. A song item's identity comes from its song and arrangement;\nmusical keys are not credentials. Services cannot upload a file through this\nconnection.\n<!-- end -->\n\n<!-- when: groups -->\n## Groups membership joins\n\nUse a group's membership records to distinguish leaders from members. On this\nAPI version memberships carry no person attributes; `include=person` supplies\nnames, and the related person id supplies contact details and other product\nrecords. The API can change memberships and group settings, but cannot create\na group, create a meeting or record group attendance.\n<!-- end -->\n\n<!-- when: check-ins -->\n## Check-Ins occurrences\n\nAn event is recurring; its event period is a dated occurrence. Period counts\nare the efficient attendance answer. Individual check-ins join a person to a\nperiod, and date filtering uses the period's start, not the check-in's creation\ntime. The API cannot check anyone in or out.\n<!-- end -->\n\n<!-- when: calendar -->\n## Calendar occurrences\n\nEvent instances expand recurrence into occurrences. A booking can return a\n`BookedEvent` stub when the user lacks access to its event. Events, instances\nand times are read-only; tags, resources, rooms and folders have write APIs.\n<!-- end -->\n\n<!-- when: registrations -->\n## Registrations\n\nA signup describes a registration event. Its attendee records join back to\nPeople and distinguish active, waitlisted and canceled attendees. This API\ncannot register someone.\n<!-- end -->\n\n<!-- when: giving -->\n## Giving amounts and donor joins\n\nAmounts are integer cents with an explicit currency. A donation's designations\nallocate it across funds; do not count a donation once per designation. Join\ndonors by person id and funds by fund id. Recurring schedules and the donations\nthey produce are different records. Creating/editing a donation, refunding it\nor committing a batch changes financial records; named giving tools only read.\n<!-- end -->\n\n<!-- when: publishing -->\n## Publishing\n\nEpisodes, channels and resources are available through the indexed API. A public\nChurch Center link and a media resource are different things; use the record\nthe task asks for. Signed download URL generation is refused because the\nprivate link would remain withheld.\n<!-- end -->\n\n<!-- when: webhooks -->\n## Webhooks\n\nA subscription names an event and callback destination. Signing keys, private\ndownload destinations and opaque event/delivery payloads or headers remain\nwithheld. Status and event metadata are available for audits.\nWebhook signing-key rotation is refused: the new credential cannot be returned\nor installed on the receiver through this connection.\n<!-- end -->\n\n<!-- when: api -->\n## Organization API\n\nThe organization API includes application metadata and personal-access-token\ninventory. Inventory records describe tokens; they do not return their values.\nRevoking a token is a write and may interrupt another integration, including\nthis connection if its own token is revoked.\n<!-- end -->\n\n<!-- when: limited -->\n## Product boundary\n\nSome products are unavailable on this connection. People lists and their rules\nare unavailable because they can query another product. Webhook payloads and\nchanges to existing subscriptions are unavailable because their ids do not name\ntheir product. A new subscription must name a reachable product's event.\n\nStored workflow cards, notes, custom fields and form answers remain readable\nwhen reachable: staff or automations may have copied information into them.\nThe connection does not infer the provenance of those stored records.\n<!-- end -->\n\n## Not available\n\nOnly reviewed product versions and indexed operations are callable. File\nuploads use a separate host this connection does not reach. Credentials and\nprivate calendar access codes cannot be recovered through a tool.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `list_person_field_data`, `list_person_notes`, `list_workflow_cards`, `list_workflows`, `pco_api_details`, `pco_api_search`, `search_people`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. For `pco_api_read`, put it in `query[page.param]` or `body[page.param]`, as `page.in` specifies. Keep the original method, path, filters, page size, fields and headers. Paged `list_*` and `search_*` reads take `limit` (default 25, at most 100). The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n<!-- when: services -->\n- **Paging:** For `list_person_schedules`, `list_plans`, `list_service_types`, `list_teams`, pass `page.nextCursor` as the top-level `cursor`.\n<!-- end -->\n<!-- when: groups -->\n- **Paging:** For `list_group_memberships`, `list_groups`, pass `page.nextCursor` as the top-level `cursor`.\n<!-- end -->\n<!-- when: giving -->\n- **Paging:** For `list_donations`, `list_funds`, pass `page.nextCursor` as the top-level `cursor`.\n<!-- end -->\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `pco_api_search` finds this API's operations and reads no account data, `pco_api_details` shows one operation's contract, and `pco_api_read` calls a reviewed read. `pco_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused. 2 operations are refused by design; use `pco_api_details` with the method and path to
|
|
3
|
+
"content": "## Identity first\n\nA People person id joins records across products. Resolve people by identity,\nthen join on `person_id`; names can repeat. A membership, scheduled person,\nworkflow card and person each have their own record id.\n\n`get_me` identifies the token's user and the organization's time zone. Bare\ndates mean the organization's local day; use a timestamp with an offset for\nan exact boundary. Name filters are exact unless their value ends in `%`.\n\nAn old person id may have been merged. Read `/people/v2/person_mergers` with\n`where[person_to_remove_id]`, follow `person_to_keep_id`, then read that profile.\nAn empty history does not establish that a person was merged.\n\nReduce pastoral information inside `execute_code` and return aggregates unless\nthe task needs named people. A named projection omits some safe vendor fields;\nread the indexed record when the task needs those fields.\n\n<!-- when: people -->\n## People and follow-up\n\nResolve a person before changing their profile. An omitted attribute leaves\nthe current value alone; `null` clears it. Archiving a person is reversible;\ndeleting a profile is permanent.\n\nWorkflow cards join people to a workflow and its ordered steps. Resolve both\nthe card id and its person id before acting on a card. Snoozing needs a duration;\npromoting a card completes its step, while skipping moves on without completing\nit. A workflow card note and a profile note are different records. Profile note\ncategories can notify their subscribers.\n\nCustom field values need their field-definition records to supply names and\ndata types. Form answers similarly join submissions, values and fields; the\nAPI cannot submit a form on someone's behalf.\n<!-- end -->\n\n<!-- when: lists -->\nSaved People lists are live cross-product queries. Their results are only as\nfresh as their last refresh. Refreshing a list can fire its automations, such\nas adding a person to a workflow, and is a write even when the task is a count.\n<!-- end -->\n\n<!-- when: services -->\n## Services plans and schedules\n\nResolve the service type before finding a dated plan. Plan times define the\nservice dates. `get_plan` combines the plan, order of service and scheduled\nteam; a cut section can be read through its indexed items or team-members path.\nItem durations are seconds, so divide by 60 when reporting minutes.\n\nScheduling needs a team and may need a position. `C` means confirmed, `U`\nunconfirmed and `D` declined. Preparing a notification and sending it are\ndifferent actions. A song item's identity comes from its song and arrangement;\nmusical keys are not credentials. Services cannot upload a file through this\nconnection.\n<!-- end -->\n\n<!-- when: groups -->\n## Groups membership joins\n\nUse a group's membership records to distinguish leaders from members. On this\nAPI version memberships carry no person attributes; `include=person` supplies\nnames, and the related person id supplies contact details and other product\nrecords. The API can change memberships and group settings, but cannot create\na group, create a meeting or record group attendance.\n<!-- end -->\n\n<!-- when: check-ins -->\n## Check-Ins occurrences\n\nAn event is recurring; its event period is a dated occurrence. Period counts\nare the efficient attendance answer. Individual check-ins join a person to a\nperiod, and date filtering uses the period's start, not the check-in's creation\ntime. The API cannot check anyone in or out.\n<!-- end -->\n\n<!-- when: calendar -->\n## Calendar occurrences\n\nEvent instances expand recurrence into occurrences. A booking can return a\n`BookedEvent` stub when the user lacks access to its event. Events, instances\nand times are read-only; tags, resources, rooms and folders have write APIs.\n<!-- end -->\n\n<!-- when: registrations -->\n## Registrations\n\nA signup describes a registration event. Its attendee records join back to\nPeople and distinguish active, waitlisted and canceled attendees. This API\ncannot register someone.\n<!-- end -->\n\n<!-- when: giving -->\n## Giving amounts and donor joins\n\nAmounts are integer cents with an explicit currency. A donation's designations\nallocate it across funds; do not count a donation once per designation. Join\ndonors by person id and funds by fund id. Recurring schedules and the donations\nthey produce are different records. Creating/editing a donation, refunding it\nor committing a batch changes financial records; named giving tools only read.\n<!-- end -->\n\n<!-- when: publishing -->\n## Publishing\n\nEpisodes, channels and resources are available through the indexed API. A public\nChurch Center link and a media resource are different things; use the record\nthe task asks for. Signed download URL generation is refused because the\nprivate link would remain withheld.\n<!-- end -->\n\n<!-- when: webhooks -->\n## Webhooks\n\nA subscription names an event and callback destination. Signing keys, private\ndownload destinations and opaque event/delivery payloads or headers remain\nwithheld. Status and event metadata are available for audits.\nWebhook signing-key rotation is refused: the new credential cannot be returned\nor installed on the receiver through this connection.\n<!-- end -->\n\n<!-- when: api -->\n## Organization API\n\nThe organization API includes application metadata and personal-access-token\ninventory. Inventory records describe tokens; they do not return their values.\nRevoking a token is a write and may interrupt another integration, including\nthis connection if its own token is revoked.\n<!-- end -->\n\n<!-- when: limited -->\n## Product boundary\n\nSome products are unavailable on this connection. People lists and their rules\nare unavailable because they can query another product. Webhook payloads and\nchanges to existing subscriptions are unavailable because their ids do not name\ntheir product. A new subscription must name a reachable product's event.\n\nStored workflow cards, notes, custom fields and form answers remain readable\nwhen reachable: staff or automations may have copied information into them.\nThe connection does not infer the provenance of those stored records.\n<!-- end -->\n\n## Not available\n\nOnly reviewed product versions and indexed operations are callable. File\nuploads use a separate host this connection does not reach. Credentials and\nprivate calendar access codes cannot be recovered through a tool.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `list_person_field_data`, `list_person_notes`, `list_workflow_cards`, `list_workflows`, `pco_api_details`, `pco_api_search`, `search_people`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. For `pco_api_read`, put it in `query[page.param]` or `body[page.param]`, as `page.in` specifies. Keep the original method, path, filters, page size, fields and headers. Paged `list_*` and `search_*` reads take `limit` (default 25, at most 100). The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n<!-- when: services -->\n- **Paging:** For `list_person_schedules`, `list_plans`, `list_service_types`, `list_teams`, pass `page.nextCursor` as the top-level `cursor`.\n<!-- end -->\n<!-- when: groups -->\n- **Paging:** For `list_group_memberships`, `list_groups`, pass `page.nextCursor` as the top-level `cursor`.\n<!-- end -->\n<!-- when: giving -->\n- **Paging:** For `list_donations`, `list_funds`, pass `page.nextCursor` as the top-level `cursor`.\n<!-- end -->\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `pco_api_search` finds this API's operations and reads no account data, `pco_api_details` shows one operation's contract, and `pco_api_read` calls a reviewed read. Details' `response.fields` promises selection only; `withheld` hints are incomplete, and absence never means safe or readable. `pco_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused. 2 operations are refused by design; use `pco_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. Writes take no idempotency key.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. `not_found` means the record does not exist; this API tells absence from a permission gap. `conflict` means: HTTP 409 for duplicate records in People.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
4
4
|
"instructionsHeading": "Organization instructions"
|
|
5
5
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { asRecord, compact } from "../_shared/json.js";
|
|
1
2
|
// Curated Planning Center workflows over the standard provider kit.
|
|
2
3
|
import { Effect, Schema } from "effect";
|
|
3
4
|
import { ConnectorCallError } from "../../errors.js";
|
|
@@ -6,7 +7,7 @@ import { Integer, isMinChars, emptyRequired } from "../../http/json-schema.js";
|
|
|
6
7
|
import { read, write, list as standardList } from "../../http/tools.js";
|
|
7
8
|
import { PLANNING_CENTER_APPS, MAX_PAGE_SIZE, planningCenterCall } from "./api.js";
|
|
8
9
|
import { excludedApps } from "./lib/guide.js";
|
|
9
|
-
import {
|
|
10
|
+
import { resources, indexIncluded, relId, related, relatedMany, pick, attribute, writeDocument, pageOf, } from "./lib/json-api.js";
|
|
10
11
|
const fail = (code, message, options) => Effect.fail(new Refusal({ error: new ConnectorCallError(code, message, options) }));
|
|
11
12
|
const pattern = (source) => Schema.makeFilter((value) => typeof value !== "string" || new RegExp(source).test(value), {
|
|
12
13
|
toJsonSchema: () => ({ pattern: source }),
|
|
@@ -139,43 +139,91 @@ function safeRecord(value) {
|
|
|
139
139
|
out["LinkedTxn"] = source["LinkedTxn"].map((row) => pick(row, ["TxnId", "TxnType", "TxnLineId"]));
|
|
140
140
|
return out;
|
|
141
141
|
}
|
|
142
|
+
const REPORT_KEYS = new Set([
|
|
143
|
+
"Header",
|
|
144
|
+
"Columns",
|
|
145
|
+
"Column",
|
|
146
|
+
"Rows",
|
|
147
|
+
"Row",
|
|
148
|
+
"Summary",
|
|
149
|
+
"ColData",
|
|
150
|
+
"ColTitle",
|
|
151
|
+
"ColType",
|
|
152
|
+
"MetaData",
|
|
153
|
+
"Name",
|
|
154
|
+
"Value",
|
|
155
|
+
"Option",
|
|
156
|
+
"Time",
|
|
157
|
+
"ReportName",
|
|
158
|
+
"DateMacro",
|
|
159
|
+
"ReportBasis",
|
|
160
|
+
"StartPeriod",
|
|
161
|
+
"EndPeriod",
|
|
162
|
+
"SummarizeColumnsBy",
|
|
163
|
+
"Currency",
|
|
164
|
+
"value",
|
|
165
|
+
"id",
|
|
166
|
+
"type",
|
|
167
|
+
"group",
|
|
168
|
+
]);
|
|
169
|
+
const ENTITIES = ["Customer", "Vendor", "Account", "Item", "Invoice", "Bill", "Payment", "BillPayment"];
|
|
170
|
+
const COMPANY_KEYS = [
|
|
171
|
+
"Id",
|
|
172
|
+
"SyncToken",
|
|
173
|
+
"CompanyName",
|
|
174
|
+
"LegalName",
|
|
175
|
+
"Country",
|
|
176
|
+
"FiscalYearStartMonth",
|
|
177
|
+
"SupportedLanguages",
|
|
178
|
+
];
|
|
142
179
|
// Report trees retain vendor hierarchy, column metadata and empty strings. No index has a fixed metric meaning.
|
|
143
180
|
function reportTree(value) {
|
|
144
181
|
if (Array.isArray(value))
|
|
145
182
|
return value.map(reportTree);
|
|
146
183
|
if (typeof value !== "object" || value === null)
|
|
147
184
|
return scalar(value);
|
|
148
|
-
const keys = new Set([
|
|
149
|
-
"Header",
|
|
150
|
-
"Columns",
|
|
151
|
-
"Column",
|
|
152
|
-
"Rows",
|
|
153
|
-
"Row",
|
|
154
|
-
"Summary",
|
|
155
|
-
"ColData",
|
|
156
|
-
"ColTitle",
|
|
157
|
-
"ColType",
|
|
158
|
-
"MetaData",
|
|
159
|
-
"Name",
|
|
160
|
-
"Value",
|
|
161
|
-
"Option",
|
|
162
|
-
"Time",
|
|
163
|
-
"ReportName",
|
|
164
|
-
"DateMacro",
|
|
165
|
-
"ReportBasis",
|
|
166
|
-
"StartPeriod",
|
|
167
|
-
"EndPeriod",
|
|
168
|
-
"SummarizeColumnsBy",
|
|
169
|
-
"Currency",
|
|
170
|
-
"value",
|
|
171
|
-
"id",
|
|
172
|
-
"type",
|
|
173
|
-
"group",
|
|
174
|
-
]);
|
|
175
185
|
return Object.fromEntries(Object.entries(value)
|
|
176
|
-
.filter(([key]) =>
|
|
186
|
+
.filter(([key]) => REPORT_KEYS.has(key))
|
|
177
187
|
.map(([key, entry]) => [key, reportTree(entry)]));
|
|
178
188
|
}
|
|
189
|
+
/** What `safeRecord` does to a path within a record: returns it whole, trims it to picked fields, or drops it. */
|
|
190
|
+
function recordPath(path) {
|
|
191
|
+
const [head, ...rest] = path;
|
|
192
|
+
if (head === undefined)
|
|
193
|
+
return "trimmed";
|
|
194
|
+
if (scalars.includes(head))
|
|
195
|
+
return rest.length ? "dropped" : "whole";
|
|
196
|
+
if (details.includes(head) || head === "Line")
|
|
197
|
+
return recordPath(rest);
|
|
198
|
+
const picked = head === "MetaData"
|
|
199
|
+
? ["CreateTime", "LastUpdatedTime"]
|
|
200
|
+
: head === "LinkedTxn"
|
|
201
|
+
? ["TxnId", "TxnType", "TxnLineId"]
|
|
202
|
+
: references.includes(head)
|
|
203
|
+
? ["value", "name", "type"]
|
|
204
|
+
: undefined;
|
|
205
|
+
if (!picked)
|
|
206
|
+
return "dropped";
|
|
207
|
+
if (rest.length === 0)
|
|
208
|
+
return "trimmed";
|
|
209
|
+
return rest.length === 1 && picked.includes(rest[0]) ? "whole" : "dropped";
|
|
210
|
+
}
|
|
211
|
+
/** The closed projection as a discovery hint: a path it always drops is withheld. */
|
|
212
|
+
function quickbooksWithholds(template) {
|
|
213
|
+
return (path) => {
|
|
214
|
+
const names = path.map((segment) => segment.replace(/\[\]$/, ""));
|
|
215
|
+
if (template.startsWith("/reports/"))
|
|
216
|
+
return !names.every((name) => REPORT_KEYS.has(name));
|
|
217
|
+
if (template.startsWith("/companyinfo/"))
|
|
218
|
+
return (names[0] !== "CompanyInfo" || names.length > 2 || (names.length === 2 && !COMPANY_KEYS.includes(names[1])));
|
|
219
|
+
const [root, ...rest] = template === "/query" && names[0] === "QueryResponse" ? names.slice(1) : names;
|
|
220
|
+
if (template === "/query" && names[0] !== "QueryResponse")
|
|
221
|
+
return true;
|
|
222
|
+
if (root === undefined)
|
|
223
|
+
return false;
|
|
224
|
+
return !ENTITIES.includes(root) || recordPath(rest) === "dropped";
|
|
225
|
+
};
|
|
226
|
+
}
|
|
179
227
|
export function quickbooksSafety(options) {
|
|
180
228
|
return {
|
|
181
229
|
reviewed: {
|
|
@@ -188,32 +236,25 @@ export function quickbooksSafety(options) {
|
|
|
188
236
|
return { method: op.method, template: op.path };
|
|
189
237
|
},
|
|
190
238
|
withholdsErrors: () => true,
|
|
239
|
+
withholds: (operation) => quickbooksWithholds(operation.template),
|
|
191
240
|
redact: (body, operation) => {
|
|
192
241
|
const root = object(body);
|
|
193
242
|
if (operation.template.startsWith("/reports/"))
|
|
194
243
|
return reportTree(root);
|
|
195
244
|
if (operation.template.startsWith("/companyinfo/"))
|
|
196
245
|
return {
|
|
197
|
-
CompanyInfo: pick(root["CompanyInfo"],
|
|
198
|
-
"Id",
|
|
199
|
-
"SyncToken",
|
|
200
|
-
"CompanyName",
|
|
201
|
-
"LegalName",
|
|
202
|
-
"Country",
|
|
203
|
-
"FiscalYearStartMonth",
|
|
204
|
-
"SupportedLanguages",
|
|
205
|
-
]),
|
|
246
|
+
CompanyInfo: pick(root["CompanyInfo"], COMPANY_KEYS),
|
|
206
247
|
};
|
|
207
248
|
if (operation.template === "/query") {
|
|
208
249
|
const query = object(root["QueryResponse"]);
|
|
209
250
|
return {
|
|
210
251
|
QueryResponse: Object.fromEntries(Object.entries(query)
|
|
211
|
-
.filter(([key]) =>
|
|
252
|
+
.filter(([key]) => ENTITIES.includes(key) && Array.isArray(query[key]))
|
|
212
253
|
.map(([key, rows]) => [key, rows.map(safeRecord)])),
|
|
213
254
|
};
|
|
214
255
|
}
|
|
215
256
|
return Object.fromEntries(Object.entries(root)
|
|
216
|
-
.filter(([key]) =>
|
|
257
|
+
.filter(([key]) => ENTITIES.includes(key))
|
|
217
258
|
.map(([key, record]) => [key, safeRecord(record)]));
|
|
218
259
|
},
|
|
219
260
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Accounting identity and joins\n\nAn Id identifies a record inside this company and record family. DocNumber is\na human label, may repeat, and is not an Id. SyncToken is the public record\nversion used for concurrency control, not an OAuth credential. References carry\nthe target Id in value; invoice CustomerRef joins Customer, bill VendorRef\njoins Vendor, and payment Line.LinkedTxn joins invoices or bills.\n\nAccounting Payment and BillPayment records describe received and paid money;\nreading them does not execute a payment. Desktop, Payments, Payroll, attachments,\nand unreviewed accounting entities are outside this connection.\n\n## Money, time and reports\n\nAmounts use decimal currency units, never integer cents. Preserve CurrencyRef\nand ExchangeRate when combining transactions; never sum unlike currencies.\nTxnDate is the company's accounting date, not a UTC instant. MetaData timestamps\ncarry their own offset. Customer and vendor searches omit inactive records\nunless the Active filter explicitly selects them.\n\nReports are trees with Header, Columns, nested Rows and section summaries.\nRead column types and metadata together with row values; column order and\nsection labels can change. Empty amount strings mean no value, not zero.\nDo not flatten away section identity or add totals to their component rows.\nCheck the returned period, basis, currency and NoReportData option before\ncomparing reports. Aging is an as-of snapshot, rather than a transaction range.\n\nReturn aggregates and the few identifying records the task needs; avoid\ncopying full customer or vendor directories into the conversation.\n\n\n## Named writes and reconciliation\n\nThe connector defaults to read-only. Deployment code must set `readOnly: false`\nto expose named writes; endpoint and pool read-only policies still apply. The\nAPI hatch remains read-only. `create_customer` accepts DisplayName and optional\nCompanyName. `update_customer` requires caller-observed Id and SyncToken and\nchanges only those naming fields with sparse:true. Omitted CompanyName stays\nunchanged. A stale-version conflict never fetches a newer version or overwrites\nit. Read and review the current record before proposing a new update.\nDisplayName must be unique across customers, vendors and employees.\n\n`create_invoice` creates a real accounting transaction, never a draft, for a\nverified US company and a customer using USD. This bounds the no-email claim\nto the reviewed US autosend contract and money to two-decimal USD units. First\nobserve an active customer with its CurrencyRef and active Service item Ids.\nSupply currency USD, a positive explicit exchange rate, accounting and\ndue dates, a reconciliation DocNumber and 1–20 lines. Each line is one service\nunit with an explicit positive amount in decimal currency units, supplied as a\nstring with exactly two fractional digits. No prices, dates or exchange rates\nare defaulted or fetched. Preflight reads verify the fixed company Country is US\nand recheck customer currency and item activity/type; they are not an atomic snapshot. Vendor validation can still\nreject changed references. Taxes and totals are computed by QuickBooks: inspect\nthe returned currency, exchange rate and totals; line sums are not a promise of\nan untaxed total. Locale/company tax requirements may reject this narrow body.\nNo customer/item creation happens implicitly.\nEach additional request charges the call-admission budget before dispatch.\nContradictory returned references, dates, rate or service prices produce an\nunknown outcome; reconcile through reads rather than repeating the write.\n\nInvoice email status is fixed to NotSet; online, IPN, credit-card, ACH, PayPal\nand Affirm payment flags are fixed false. No email field or send operation is\nexposed. Company invoice-autosend opt-in does not supply the documented\nNeedToSend request state. This prevents this creation request from asking Intuit\nto email the invoice, including when the customer has an email on file.\nInvoice updates are unavailable: preserving an existing send-ready state has\nnot been established safe, while resetting it would change payment settings.\nDeletes, voids, email delivery, charges, refunds, other accounting writes and\narbitrary mutations are unavailable.\n\nEvery write requires a caller-generated `requestId`: 1–50 ASCII letters,\ndigits, underscores or hyphens, starting with a letter or digit. Keep it stable\nfor the exact intent and unique across requests/tools for this company. It is\nsent in Intuit's requestid query parameter with minorversion 75, rather than an\nidempotency header. Intuit documents recognizing the same content and ID and\nreturning the original response; no retention duration is established. This\nnever permits automatic replay. A dispatched write is sent once even after\n401, timeout, server failure or an invalid success response.\n\nAfter an unknown customer-create outcome, search exact DisplayName, including\ninactive records when needed, and inspect the match. For an invoice, search\nDocNumber plus CustomerRef and inspect currency, dates, lines and computed\namounts. DocNumber can repeat, so it alone proves nothing. Reconcile and report\nambiguity before considering another create; never generate a fresh requestId\nand blindly repeat a potentially committed intent. Unknown update outcomes\nneed a read of the known Id and version. A requestId is not a lookup endpoint or\na permanent duplicate-prevention guarantee.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `quickbooks_api_details`, `quickbooks_api_search`, `search_records`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. Paged `list_*` and `search_*` reads take `limit` (default 25, at most 100). The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `quickbooks_api_search` finds this API's operations and reads no account data, `quickbooks_api_details` shows one operation's contract, and `quickbooks_api_read` calls a reviewed read. An operation without a reviewed verdict is refused. 3 operations are refused by design; use `quickbooks_api_details` with the method and path to
|
|
3
|
+
"content": "## Accounting identity and joins\n\nAn Id identifies a record inside this company and record family. DocNumber is\na human label, may repeat, and is not an Id. SyncToken is the public record\nversion used for concurrency control, not an OAuth credential. References carry\nthe target Id in value; invoice CustomerRef joins Customer, bill VendorRef\njoins Vendor, and payment Line.LinkedTxn joins invoices or bills.\n\nAccounting Payment and BillPayment records describe received and paid money;\nreading them does not execute a payment. Desktop, Payments, Payroll, attachments,\nand unreviewed accounting entities are outside this connection.\n\n## Money, time and reports\n\nAmounts use decimal currency units, never integer cents. Preserve CurrencyRef\nand ExchangeRate when combining transactions; never sum unlike currencies.\nTxnDate is the company's accounting date, not a UTC instant. MetaData timestamps\ncarry their own offset. Customer and vendor searches omit inactive records\nunless the Active filter explicitly selects them.\n\nReports are trees with Header, Columns, nested Rows and section summaries.\nRead column types and metadata together with row values; column order and\nsection labels can change. Empty amount strings mean no value, not zero.\nDo not flatten away section identity or add totals to their component rows.\nCheck the returned period, basis, currency and NoReportData option before\ncomparing reports. Aging is an as-of snapshot, rather than a transaction range.\n\nReturn aggregates and the few identifying records the task needs; avoid\ncopying full customer or vendor directories into the conversation.\n\n\n## Named writes and reconciliation\n\nThe connector defaults to read-only. Deployment code must set `readOnly: false`\nto expose named writes; endpoint and pool read-only policies still apply. The\nAPI hatch remains read-only. `create_customer` accepts DisplayName and optional\nCompanyName. `update_customer` requires caller-observed Id and SyncToken and\nchanges only those naming fields with sparse:true. Omitted CompanyName stays\nunchanged. A stale-version conflict never fetches a newer version or overwrites\nit. Read and review the current record before proposing a new update.\nDisplayName must be unique across customers, vendors and employees.\n\n`create_invoice` creates a real accounting transaction, never a draft, for a\nverified US company and a customer using USD. This bounds the no-email claim\nto the reviewed US autosend contract and money to two-decimal USD units. First\nobserve an active customer with its CurrencyRef and active Service item Ids.\nSupply currency USD, a positive explicit exchange rate, accounting and\ndue dates, a reconciliation DocNumber and 1–20 lines. Each line is one service\nunit with an explicit positive amount in decimal currency units, supplied as a\nstring with exactly two fractional digits. No prices, dates or exchange rates\nare defaulted or fetched. Preflight reads verify the fixed company Country is US\nand recheck customer currency and item activity/type; they are not an atomic snapshot. Vendor validation can still\nreject changed references. Taxes and totals are computed by QuickBooks: inspect\nthe returned currency, exchange rate and totals; line sums are not a promise of\nan untaxed total. Locale/company tax requirements may reject this narrow body.\nNo customer/item creation happens implicitly.\nEach additional request charges the call-admission budget before dispatch.\nContradictory returned references, dates, rate or service prices produce an\nunknown outcome; reconcile through reads rather than repeating the write.\n\nInvoice email status is fixed to NotSet; online, IPN, credit-card, ACH, PayPal\nand Affirm payment flags are fixed false. No email field or send operation is\nexposed. Company invoice-autosend opt-in does not supply the documented\nNeedToSend request state. This prevents this creation request from asking Intuit\nto email the invoice, including when the customer has an email on file.\nInvoice updates are unavailable: preserving an existing send-ready state has\nnot been established safe, while resetting it would change payment settings.\nDeletes, voids, email delivery, charges, refunds, other accounting writes and\narbitrary mutations are unavailable.\n\nEvery write requires a caller-generated `requestId`: 1–50 ASCII letters,\ndigits, underscores or hyphens, starting with a letter or digit. Keep it stable\nfor the exact intent and unique across requests/tools for this company. It is\nsent in Intuit's requestid query parameter with minorversion 75, rather than an\nidempotency header. Intuit documents recognizing the same content and ID and\nreturning the original response; no retention duration is established. This\nnever permits automatic replay. A dispatched write is sent once even after\n401, timeout, server failure or an invalid success response.\n\nAfter an unknown customer-create outcome, search exact DisplayName, including\ninactive records when needed, and inspect the match. For an invoice, search\nDocNumber plus CustomerRef and inspect currency, dates, lines and computed\namounts. DocNumber can repeat, so it alone proves nothing. Reconcile and report\nambiguity before considering another create; never generate a fresh requestId\nand blindly repeat a potentially committed intent. Unknown update outcomes\nneed a read of the known Id and version. A requestId is not a lookup endpoint or\na permanent duplicate-prevention guarantee.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `quickbooks_api_details`, `quickbooks_api_search`, `search_records`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. Paged `list_*` and `search_*` reads take `limit` (default 25, at most 100). The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `quickbooks_api_search` finds this API's operations and reads no account data, `quickbooks_api_details` shows one operation's contract, and `quickbooks_api_read` calls a reviewed read. Details' `response.fields` promises selection only; `withheld` hints are incomplete, and absence never means safe or readable. An operation without a reviewed verdict is refused. 3 operations are refused by design; use `quickbooks_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. Writes take no idempotency key.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. `conflict` means: Intuit fault 5010 for a stale SyncToken; Intuit fault 6240 for a duplicate DisplayName.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
4
4
|
"instructionsHeading": "Company instructions"
|
|
5
5
|
};
|
|
@@ -121,6 +121,7 @@ export const safety = {
|
|
|
121
121
|
return { method: op.method, template: op.path };
|
|
122
122
|
},
|
|
123
123
|
redact: (data, op) => values.redact(data, op.method, op.template),
|
|
124
|
+
withholds: (op) => values.withholds(op.method, op.template),
|
|
124
125
|
withholdsErrors: (op) => values.withholdsErrors(sheetsIndex.ops.find((entry) => entry.method === op.method && entry.path === op.template)),
|
|
125
126
|
},
|
|
126
127
|
sensitiveFields: { names: [], choice: "drop" },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Find the workbook and resolve ranges\n\nSheets starts from a spreadsheet id. A spreadsheet id the person gives works\nas is; search Drive only for a name. The Drive file id is\nthe spreadsheet id. Moving, sharing and trashing the file also belong to Drive.\nRead `get_spreadsheet` first to resolve sheet titles, numeric sheet ids and named\nranges before writing. Sheet id zero is valid; a tab's position is not its id.\n\nA1 ranges use sheet titles; structural GridRanges use numeric sheet ids with\nzero-based, end-exclusive row and column indexes. Quote titles containing\nspaces or punctuation and double apostrophes: `'Alice''s costs'!A1:D7`.\nA bare title may resolve to a named range; quote it to address the whole tab.\n\n## Read cell meaning\n\nDisplayed values reflect spreadsheet locale and number formats. Use\nunformatted numbers for arithmetic and formulas when inspecting expressions.\nDate serials count days from 1899-12-30; interpret dates using the workbook's\ntime zone. Trailing empty rows and cells may be absent. An empty range is a\nvalid result, not proof that the sheet is missing.\n\nWhole-row content reads can resume partway through a range. A named range or\nwhole-sheet reference may be re-read and earlier rows skipped; rowOffset is\nrelative to Google's echoed range. Reduce amounts inside execute_code before\nreturning them. Read an oversized cell separately to recover its full text.\n\n## Edit and reconcile\n\nUse RAW for external data; USER_ENTERED can turn leading equals signs into\nformulas. In cell writes, null leaves a cell unchanged and an empty string\nclears it. Clearing preserves formatting and validation. `append_values` is\npinned to INSERT_ROWS and inserts rows after the detected table. A vendor append using\nOVERWRITE can replace existing cells below that table.\n\nRead just before replacing another person's cells. Sheets has no revision\nprecondition or replay key: concurrent edits can be overwritten. After an\nuncertain append, read the target table and identify the row before adding\nanything missing. After an uncertain workbook create, search Drive by title\nbefore creating another workbook.\n\nStructural batches apply atomically, but can delete rows, sheets or\nprotections. An oversized reply may be summarized after the edit saved;\ninspect the workbook to recover details. Preview comment mutations and\nexternal data-source configuration, refresh and administration are outside\nthis connection. Developer-metadata searches and data-filter reads are\nread-only even though their wire method is POST.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `get_values`, `sheets_api_details`, `sheets_api_search`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `sheets_api_search` finds this API's operations and reads no account data, `sheets_api_details` shows one operation's contract, and `sheets_api_read` calls a reviewed read. `sheets_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. Writes take no idempotency key.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. It documents no conflict.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
3
|
+
"content": "## Find the workbook and resolve ranges\n\nSheets starts from a spreadsheet id. A spreadsheet id the person gives works\nas is; search Drive only for a name. The Drive file id is\nthe spreadsheet id. Moving, sharing and trashing the file also belong to Drive.\nRead `get_spreadsheet` first to resolve sheet titles, numeric sheet ids and named\nranges before writing. Sheet id zero is valid; a tab's position is not its id.\n\nA1 ranges use sheet titles; structural GridRanges use numeric sheet ids with\nzero-based, end-exclusive row and column indexes. Quote titles containing\nspaces or punctuation and double apostrophes: `'Alice''s costs'!A1:D7`.\nA bare title may resolve to a named range; quote it to address the whole tab.\n\n## Read cell meaning\n\nDisplayed values reflect spreadsheet locale and number formats. Use\nunformatted numbers for arithmetic and formulas when inspecting expressions.\nDate serials count days from 1899-12-30; interpret dates using the workbook's\ntime zone. Trailing empty rows and cells may be absent. An empty range is a\nvalid result, not proof that the sheet is missing.\n\nWhole-row content reads can resume partway through a range. A named range or\nwhole-sheet reference may be re-read and earlier rows skipped; rowOffset is\nrelative to Google's echoed range. Reduce amounts inside execute_code before\nreturning them. Read an oversized cell separately to recover its full text.\n\n## Edit and reconcile\n\nUse RAW for external data; USER_ENTERED can turn leading equals signs into\nformulas. In cell writes, null leaves a cell unchanged and an empty string\nclears it. Clearing preserves formatting and validation. `append_values` is\npinned to INSERT_ROWS and inserts rows after the detected table. A vendor append using\nOVERWRITE can replace existing cells below that table.\n\nRead just before replacing another person's cells. Sheets has no revision\nprecondition or replay key: concurrent edits can be overwritten. After an\nuncertain append, read the target table and identify the row before adding\nanything missing. After an uncertain workbook create, search Drive by title\nbefore creating another workbook.\n\nStructural batches apply atomically, but can delete rows, sheets or\nprotections. An oversized reply may be summarized after the edit saved;\ninspect the workbook to recover details. Preview comment mutations and\nexternal data-source configuration, refresh and administration are outside\nthis connection. Developer-metadata searches and data-filter reads are\nread-only even though their wire method is POST.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `get_values`, `sheets_api_details`, `sheets_api_search`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `sheets_api_search` finds this API's operations and reads no account data, `sheets_api_details` shows one operation's contract, and `sheets_api_read` calls a reviewed read. Details' `response.fields` promises selection only; `withheld` hints are incomplete, and absence never means safe or readable. `sheets_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. Writes take no idempotency key.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. It documents no conflict.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
4
4
|
"instructionsHeading": "Connection instructions"
|
|
5
5
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { routePhrases } from "../../agent-routes.js";
|
|
2
|
+
import { asRecord, asArray, compact } from "../_shared/json.js";
|
|
1
3
|
// Google Sheets named projections, bounded cell reads and guarded writes.
|
|
2
4
|
import { Effect, Option, Schema } from "effect";
|
|
3
5
|
import { ConnectorCallError } from "../../errors.js";
|
|
@@ -7,7 +9,7 @@ import { clampText, jsonBytes, RESULT_BUDGET_BYTES } from "../_shared/google/res
|
|
|
7
9
|
import { encodeForAgent, redactBeforeCut } from "../../sent-secrets.js";
|
|
8
10
|
import { read, write } from "../../http/tools.js";
|
|
9
11
|
import { boundCursor } from "../../http/paging.js";
|
|
10
|
-
import {
|
|
12
|
+
import { fieldsContract, selectFields } from "../../http/fields.js";
|
|
11
13
|
import { Operation } from "../../runtime/call.js";
|
|
12
14
|
import { sheetsIndex } from "./lib/hatch.js";
|
|
13
15
|
import { SHEETS_ADMITTED_KINDS, SHEETS_REFUSED_KINDS, sheetsExternalDataProblem } from "./lib/requests.js";
|
|
@@ -38,15 +40,6 @@ const MAX_CELL_CHARS = 50_000;
|
|
|
38
40
|
const MAX_WRITE_CELLS = 50_000;
|
|
39
41
|
/** Named ranges a metadata read lists before reporting a size cut. */
|
|
40
42
|
const MAX_NAMED_RANGES = 200;
|
|
41
|
-
function asRecord(value) {
|
|
42
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
43
|
-
}
|
|
44
|
-
function asArray(value) {
|
|
45
|
-
return Array.isArray(value) ? value : [];
|
|
46
|
-
}
|
|
47
|
-
function compact(value) {
|
|
48
|
-
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
49
|
-
}
|
|
50
43
|
function text(value) {
|
|
51
44
|
return typeof value === "string" && value !== "" ? value : undefined;
|
|
52
45
|
}
|
|
@@ -158,19 +151,19 @@ function cappedCell(ctx, whole, max, cut) {
|
|
|
158
151
|
return `${value.slice(0, max)}[… ${value.length - max} more characters truncated; read this cell alone with maxCellChars up to ${MAX_CELL_CHARS} for the rest]`;
|
|
159
152
|
}
|
|
160
153
|
/** Refuse a row no page of `budget` bytes can hold, rather than overrun the cap. */
|
|
161
|
-
function rowTooLarge(range, row, needed, budget) {
|
|
162
|
-
return new ConnectorCallError("invalid_args", `Row ${row} of ${range} needs a page of ${needed} bytes with the page's own fields, more than this page's maxBytes of ${budget}; narrow the range's columns or lower maxCellChars${raiseBytes(needed)}.`);
|
|
154
|
+
function rowTooLarge(range, row, needed, budget, phrases) {
|
|
155
|
+
return new ConnectorCallError("invalid_args", `Row ${row} of ${range} needs a page of ${needed} bytes with the page's own fields, more than this page's maxBytes of ${budget}; narrow the range's columns or lower maxCellChars${raiseBytes(needed, phrases)}.`);
|
|
163
156
|
}
|
|
164
157
|
/** Refuse a page too small for even one range's own fields. */
|
|
165
|
-
function pageTooSmall(range, needed, budget) {
|
|
166
|
-
return new ConnectorCallError("invalid_args", `A page reporting ${range} needs ${needed} bytes before any of its rows, more than this page's maxBytes of ${budget}; shorten the range${raiseBytes(needed)}.`);
|
|
158
|
+
function pageTooSmall(range, needed, budget, phrases) {
|
|
159
|
+
return new ConnectorCallError("invalid_args", `A page reporting ${range} needs ${needed} bytes before any of its rows, more than this page's maxBytes of ${budget}; shorten the range${raiseBytes(needed, phrases)}.`);
|
|
167
160
|
}
|
|
168
|
-
function raiseBytes(needed) {
|
|
161
|
+
function raiseBytes(needed, phrases) {
|
|
169
162
|
if (needed > MAX_PAGE_BYTES)
|
|
170
163
|
return "";
|
|
171
164
|
return needed <= DEFAULT_PAGE_BYTES
|
|
172
165
|
? `, or raise maxBytes to at least ${needed}`
|
|
173
|
-
: `, or raise maxBytes to at least ${needed} (above ${DEFAULT_PAGE_BYTES},
|
|
166
|
+
: `, or raise maxBytes to at least ${needed} (above ${DEFAULT_PAGE_BYTES}, ${phrases.onlyDirectRead}${phrases.program ? `: ${phrases.program} cannot receive it` : ""})`;
|
|
174
167
|
}
|
|
175
168
|
/** Refuse a row no page of `budget` cells can hold, rather than overrun the cap. */
|
|
176
169
|
function rowTooWide(range, row, width, budget) {
|
|
@@ -457,7 +450,7 @@ export function sheetsTools({ connection }) {
|
|
|
457
450
|
})) + 1;
|
|
458
451
|
if (bytes + fields > byteBudget) {
|
|
459
452
|
if (valueRanges.length === 0)
|
|
460
|
-
throw pageTooSmall(echoed, bytes + fields, byteBudget);
|
|
453
|
+
throw pageTooSmall(echoed, bytes + fields, byteBudget, routePhrases(ctx));
|
|
461
454
|
next = { i: resume.i + index, s: before };
|
|
462
455
|
break;
|
|
463
456
|
}
|
|
@@ -478,7 +471,7 @@ export function sheetsTools({ connection }) {
|
|
|
478
471
|
const size = jsonBytes(capped) + 1;
|
|
479
472
|
if (bytes + size > byteBudget) {
|
|
480
473
|
if (empty)
|
|
481
|
-
throw rowTooLarge(echoed, dropped + 1, bytes + size, byteBudget);
|
|
474
|
+
throw rowTooLarge(echoed, dropped + 1, bytes + size, byteBudget, routePhrases(ctx));
|
|
482
475
|
break;
|
|
483
476
|
}
|
|
484
477
|
cells += width;
|
|
@@ -710,15 +703,13 @@ export function sheetsTools({ connection }) {
|
|
|
710
703
|
};
|
|
711
704
|
if (!declaration.annotations.readOnlyHint)
|
|
712
705
|
return write(connection, { ...spec, destructive: declaration.annotations.destructiveHint });
|
|
706
|
+
const contract = fieldsContract("result", declaration.output, "Truncation and content continuation metadata remain available.");
|
|
713
707
|
return read(connection, {
|
|
714
708
|
...spec,
|
|
715
|
-
input: Schema.Struct({
|
|
716
|
-
...declaration.input.fields,
|
|
717
|
-
fields: Schema.optionalKey(fieldsInput("Dot paths to keep; truncation and content continuation metadata remain available.")),
|
|
718
|
-
}),
|
|
709
|
+
input: Schema.Struct({ ...declaration.input.fields, fields: Schema.optionalKey(contract.input) }),
|
|
719
710
|
run: (args) => Effect.gen(function* () {
|
|
720
711
|
const { fields, ...rest } = args;
|
|
721
|
-
const problem =
|
|
712
|
+
const problem = contract.problem(fields);
|
|
722
713
|
if (problem)
|
|
723
714
|
return yield* Effect.fail(new Refusal({ error: problem }));
|
|
724
715
|
const result = yield* spec.run(rest);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Find and read a deck\n\nSlides cannot search or list decks. Find the file through Drive, or take the\npresentation id from `/presentation/d/<id>/` in its URL. A presentation is a\nDrive file; finding one does not grant permission to open it. A user-given id\nworks as is; no Drive lookup is needed to use it.\n\n`get_presentation` reads text in page order, with speaker notes, alt text and\nplaceholder ids. `get_page` opens groups and reads a slide, layout, master or\nnotes page. A slide's `index` is zero-based. Resolve a layout's placeholders\nwith `list_layouts` and `get_page` before filling a new slide.\nNamed-read `fields` narrows data while preserving `page` and `truncated` cut disclosures.\n\nText and notes have independent continuation. Pass an element's `textCursor`\nor a slide's `elementsCursor` to `get_page` with that slide's id; pass a\n`notesCursor` with its `notesPageId`. A deck's `layoutsCursor` continues in\n`list_layouts`. Content cursors bind to the deck's revision and the text they\ncontinue; a changed deck requires a fresh read. Follow continuation even when\na result contains fewer rows than requested.\n\nFor styles, transforms, HTML comment content, anchors' ranges or other full\nstructural JSON, use `slides_api_read`. Operation details use the legacy\n`presentationsId` spelling for presentations.get; pages and batch updates use\n`presentationId`. These are path parameter names, not query arguments.\nSpell colons and unreserved characters literally in hatch paths; encode other\ncharacters within one id segment. Named tools handle their own id encoding.\n\n`slides_api_read` with `{path: \"/presentations/<id>\"}` returns `{status,data}`;\nthe vendor presentation is `data`. A Connecta call's result delivery wraps that\ntool value in its own `data`; follow its result handle for large JSON.\nContent cursors belong to the named reading tools and are separate from that delivery handle.\n\n`get_slide_thumbnail` returns a link that opens as the delegated person for\nabout 30 minutes. Keep it private. Generic thumbnail/preview operations are\nrefused; use the named thumbnail workflow.\n\n## Edit at the version you read\n\nRead before editing and use its `revisionId` as the write precondition.\nRevision ids are preconditions, not replay keys. `create_slide` adds an empty\nslide; read its placeholder ids before inserting text. `replace_all_text`\nreplaces literal text, with `matchCase` and an optional `requiredRevisionId`.\nEach write returns the new `revisionId` for the next one. A `conflict` means\nthe deck changed since your read: re-read, rebuild the requests, and send them\nwith the new `revisionId`. A failure that says the write applied means Google\nanswered 2xx: do not repeat it; re-read. No Slides tool shares, moves or deletes\nthe deck file.\n\n`batch_update_presentation` takes the existing reviewed Request kinds.\nThe generic batch hatch uses `POST /presentations/deck1:batchUpdate` with\n`body: {requests: [{insertText: {objectId: \"shape1\", text: \"Hello\"}}],\nwriteControl: {requiredRevisionId: \"revision-from-read\"}}`. It retains the\nsame request-kind and revision guards. No additional preview capabilities are\nadmitted by indexing the complete Discovery document.\n\n## Comments can save separately\n\nA comment thread's `commentId` identifies the thread; its head post and\nreplies have their own `postId`. Authors' `user` ids are stable; display names\nare labels. Anchors join to page and object ids; speaker-note comments belong\nto the notes page. Reading comments requires comment access. A view-only\naccount may be refused rather than receiving an apparently empty list.\n\nResolve, reopen or reassign a thread with `update_comment_thread`; edit its\ntext with `update_comment_post`. Reassignment requires content. Only the\npost's author can edit or delete that post. Comments notify people as in the\nSlides editor.\n\n`create_comment` sends one `insertComment` by itself. When one edit changes\nthe deck and adds or changes comments, send it as one `batch_update_presentation`\ncall: `insertComment`, `addCommentReply`, `updateCommentPost`, `deleteComment`\nand `deleteCommentReply` are Request kinds and share a batch with deck kinds\nsuch as `updateTextStyle`, under the same `requiredRevisionId`. Slides saves\nthe deck part and the comment part separately, so a successful mixed batch can\nsave the deck without confirming the comments. When `commentUpdateState` is not\n`ALL_SAVED`, or the result says comment saves are unconfirmed, read the deck and\n`list_comments`, report the deck part and the comment part each as saved or\nunconfirmed, and send neither the batch nor its comment part again. Keep every\ncreated id for reconciliation.\n\n`create_comment_reply` sends one reply by itself. The write hatch keeps\ncomment-save state and advice in envelope-level `warnings`, even with\n`fields: [\"replies\"]`; Google's own state remains in vendor `data` when supplied.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `get_page`, `get_presentation`, `list_comments`, `list_layouts`, `slides_api_details`, `slides_api_search`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `slides_api_search` finds this API's operations and reads no account data, `slides_api_details` shows one operation's contract, and `slides_api_read` calls a reviewed read. `slides_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused. 1 operation is refused by design; use `slides_api_details` with the method and path to
|
|
3
|
+
"content": "## Find and read a deck\n\nSlides cannot search or list decks. Find the file through Drive, or take the\npresentation id from `/presentation/d/<id>/` in its URL. A presentation is a\nDrive file; finding one does not grant permission to open it. A user-given id\nworks as is; no Drive lookup is needed to use it.\n\n`get_presentation` reads text in page order, with speaker notes, alt text and\nplaceholder ids. `get_page` opens groups and reads a slide, layout, master or\nnotes page. A slide's `index` is zero-based. Resolve a layout's placeholders\nwith `list_layouts` and `get_page` before filling a new slide.\nNamed-read `fields` narrows data while preserving `page` and `truncated` cut disclosures.\n\nText and notes have independent continuation. Pass an element's `textCursor`\nor a slide's `elementsCursor` to `get_page` with that slide's id; pass a\n`notesCursor` with its `notesPageId`. A deck's `layoutsCursor` continues in\n`list_layouts`. Content cursors bind to the deck's revision and the text they\ncontinue; a changed deck requires a fresh read. Follow continuation even when\na result contains fewer rows than requested.\n\nFor styles, transforms, HTML comment content, anchors' ranges or other full\nstructural JSON, use `slides_api_read`. Operation details use the legacy\n`presentationsId` spelling for presentations.get; pages and batch updates use\n`presentationId`. These are path parameter names, not query arguments.\nSpell colons and unreserved characters literally in hatch paths; encode other\ncharacters within one id segment. Named tools handle their own id encoding.\n\n`slides_api_read` with `{path: \"/presentations/<id>\"}` returns `{status,data}`;\nthe vendor presentation is `data`. A Connecta call's result delivery wraps that\ntool value in its own `data`; follow its result handle for large JSON.\nContent cursors belong to the named reading tools and are separate from that delivery handle.\n\n`get_slide_thumbnail` returns a link that opens as the delegated person for\nabout 30 minutes. Keep it private. Generic thumbnail/preview operations are\nrefused; use the named thumbnail workflow.\n\n## Edit at the version you read\n\nRead before editing and use its `revisionId` as the write precondition.\nRevision ids are preconditions, not replay keys. `create_slide` adds an empty\nslide; read its placeholder ids before inserting text. `replace_all_text`\nreplaces literal text, with `matchCase` and an optional `requiredRevisionId`.\nEach write returns the new `revisionId` for the next one. A `conflict` means\nthe deck changed since your read: re-read, rebuild the requests, and send them\nwith the new `revisionId`. A failure that says the write applied means Google\nanswered 2xx: do not repeat it; re-read. No Slides tool shares, moves or deletes\nthe deck file.\n\n`batch_update_presentation` takes the existing reviewed Request kinds.\nThe generic batch hatch uses `POST /presentations/deck1:batchUpdate` with\n`body: {requests: [{insertText: {objectId: \"shape1\", text: \"Hello\"}}],\nwriteControl: {requiredRevisionId: \"revision-from-read\"}}`. It retains the\nsame request-kind and revision guards. No additional preview capabilities are\nadmitted by indexing the complete Discovery document.\n\n## Comments can save separately\n\nA comment thread's `commentId` identifies the thread; its head post and\nreplies have their own `postId`. Authors' `user` ids are stable; display names\nare labels. Anchors join to page and object ids; speaker-note comments belong\nto the notes page. Reading comments requires comment access. A view-only\naccount may be refused rather than receiving an apparently empty list.\n\nResolve, reopen or reassign a thread with `update_comment_thread`; edit its\ntext with `update_comment_post`. Reassignment requires content. Only the\npost's author can edit or delete that post. Comments notify people as in the\nSlides editor.\n\n`create_comment` sends one `insertComment` by itself. When one edit changes\nthe deck and adds or changes comments, send it as one `batch_update_presentation`\ncall: `insertComment`, `addCommentReply`, `updateCommentPost`, `deleteComment`\nand `deleteCommentReply` are Request kinds and share a batch with deck kinds\nsuch as `updateTextStyle`, under the same `requiredRevisionId`. Slides saves\nthe deck part and the comment part separately, so a successful mixed batch can\nsave the deck without confirming the comments. When `commentUpdateState` is not\n`ALL_SAVED`, or the result says comment saves are unconfirmed, read the deck and\n`list_comments`, report the deck part and the comment part each as saved or\nunconfirmed, and send neither the batch nor its comment part again. Keep every\ncreated id for reconciliation.\n\n`create_comment_reply` sends one reply by itself. The write hatch keeps\ncomment-save state and advice in envelope-level `warnings`, even with\n`fields: [\"replies\"]`; Google's own state remains in vendor `data` when supplied.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `get_page`, `get_presentation`, `list_comments`, `list_layouts`, `slides_api_details`, `slides_api_search`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `slides_api_search` finds this API's operations and reads no account data, `slides_api_details` shows one operation's contract, and `slides_api_read` calls a reviewed read. Details' `response.fields` promises selection only; `withheld` hints are incomplete, and absence never means safe or readable. `slides_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused. 1 operation is refused by design; use `slides_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. Writes take no idempotency key.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. `conflict` means: A revision-guarded batch update returned ABORTED or FAILED_PRECONDITION with HTTP 400 or 409; The content or revision bound to a named continuation cursor changed; restart the read.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
4
4
|
"instructionsHeading": "Connection instructions"
|
|
5
5
|
};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { routePhrases } from "../../agent-routes.js";
|
|
2
|
+
import { asRecord, asArray, compact } from "../_shared/json.js";
|
|
3
|
+
import { decodeBase64url } from "../../bytes.js";
|
|
1
4
|
// Google Slides named workflows on real kit schemas, with bound content continuation.
|
|
2
5
|
import { Effect, Option, Schema } from "effect";
|
|
3
6
|
import { ConnectorCallError } from "../../errors.js";
|
|
@@ -9,7 +12,7 @@ import { SLIDES_INDEX } from "./api.js";
|
|
|
9
12
|
import * as contracts from "./lib/schemas.js";
|
|
10
13
|
import { read, write } from "../../http/tools.js";
|
|
11
14
|
import { Operation } from "../../runtime/call.js";
|
|
12
|
-
import {
|
|
15
|
+
import { fieldsContract, selectFields } from "../../http/fields.js";
|
|
13
16
|
import { Refusal } from "../../http/errors.js";
|
|
14
17
|
import { boundCursor } from "../../http/paging.js";
|
|
15
18
|
/** Slides per page of get_presentation. Slides itself returns the whole deck. */
|
|
@@ -52,7 +55,7 @@ const MAX_ECHO_CHARS = 100;
|
|
|
52
55
|
/**
|
|
53
56
|
* The comment kinds of Slides' Request. A batch carrying any of them has
|
|
54
57
|
* comment changes Slides saves apart from the deck's, and only `ALL_SAVED`
|
|
55
|
-
* says they all landed
|
|
58
|
+
* says they all landed.
|
|
56
59
|
*/
|
|
57
60
|
/** The comment save states that say every comment change in a batch landed, or none was asked for. */
|
|
58
61
|
const COMMENTS_SETTLED = new Set(["NO_UPDATES_REQUESTED", "ALL_SAVED"]);
|
|
@@ -121,15 +124,6 @@ const COMMENT_DECK_FIELDS = "presentationId,revisionId,commentsViewMode,comments
|
|
|
121
124
|
"slides(objectId,commentAnchors,slideProperties(notesPage(objectId,commentAnchors)))," +
|
|
122
125
|
"layouts(objectId,commentAnchors),masters(objectId,commentAnchors),notesMaster(objectId,commentAnchors)";
|
|
123
126
|
const COMMENT_PAGE_FIELDS = "objectId,revisionId,commentsViewMode,comments,commentAnchors";
|
|
124
|
-
function asRecord(value) {
|
|
125
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
126
|
-
}
|
|
127
|
-
function asArray(value) {
|
|
128
|
-
return Array.isArray(value) ? value : [];
|
|
129
|
-
}
|
|
130
|
-
function compact(value) {
|
|
131
|
-
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
132
|
-
}
|
|
133
127
|
function text(value) {
|
|
134
128
|
return typeof value === "string" && value !== "" ? value : undefined;
|
|
135
129
|
}
|
|
@@ -226,9 +220,7 @@ function decodeCursor(value, expected) {
|
|
|
226
220
|
const refuse = (why) => new ConnectorCallError("invalid_args", `${why} Omit cursor to start from the beginning.`);
|
|
227
221
|
let cursor;
|
|
228
222
|
try {
|
|
229
|
-
|
|
230
|
-
const binary = atob(base64 + "=".repeat((4 - (base64.length % 4)) % 4));
|
|
231
|
-
cursor = JSON.parse(new TextDecoder().decode(Uint8Array.from(binary, (character) => character.charCodeAt(0))));
|
|
223
|
+
cursor = JSON.parse(new TextDecoder().decode(decodeBase64url(String(value), true)));
|
|
232
224
|
}
|
|
233
225
|
catch {
|
|
234
226
|
throw refuse("cursor is not one this connection issued; pass page.nextCursor back unchanged.");
|
|
@@ -1808,9 +1800,13 @@ export function slidesTools({ connection }) {
|
|
|
1808
1800
|
if (bytes <= BRIDGE_BYTES)
|
|
1809
1801
|
return result;
|
|
1810
1802
|
if (bytes <= MAX_IDS_RESULT_BYTES) {
|
|
1803
|
+
// Only a surface that runs programs is told what a program can carry.
|
|
1804
|
+
const { program } = routePhrases(ctx);
|
|
1805
|
+
if (!program)
|
|
1806
|
+
return result;
|
|
1811
1807
|
return {
|
|
1812
1808
|
...result,
|
|
1813
|
-
note: `${result.note ?? ""} At ${bytes} bytes this result is more than
|
|
1809
|
+
note: `${result.note ?? ""} At ${bytes} bytes this result is more than ${program} can carry (${BRIDGE_BYTES}): call the tool directly and page it with connecta.result.`.trim(),
|
|
1814
1810
|
};
|
|
1815
1811
|
}
|
|
1816
1812
|
// Past any result: the save state and how to recover the ids stay;
|
|
@@ -1827,27 +1823,24 @@ export function slidesTools({ connection }) {
|
|
|
1827
1823
|
return declarations.map((declaration) => {
|
|
1828
1824
|
const isRead = declaration.annotations.readOnlyHint === true;
|
|
1829
1825
|
const isList = declaration.name.startsWith("list_");
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1826
|
+
const fields = isRead
|
|
1827
|
+
? isList
|
|
1828
|
+
? fieldsContract("item", declaration.name === "list_layouts" ? contracts.LayoutRow : contracts.CommentRow, "Truncation metadata is preserved.")
|
|
1829
|
+
: fieldsContract("result", declaration.output, "Page and truncation metadata are preserved.")
|
|
1830
|
+
: undefined;
|
|
1834
1831
|
const spec = {
|
|
1835
1832
|
name: declaration.name,
|
|
1836
1833
|
description: declaration.description,
|
|
1837
|
-
input:
|
|
1834
|
+
input: fields
|
|
1838
1835
|
? Schema.Struct({
|
|
1839
1836
|
...declaration.input.fields,
|
|
1840
|
-
fields: Schema.optionalKey(
|
|
1841
|
-
? "Dot paths to keep in each item; truncation metadata is preserved."
|
|
1842
|
-
: "Dot paths to keep in the result; page and truncation metadata are preserved.")),
|
|
1837
|
+
fields: Schema.optionalKey(fields.input),
|
|
1843
1838
|
})
|
|
1844
1839
|
: declaration.input,
|
|
1845
1840
|
output: declaration.output,
|
|
1846
1841
|
...(declaration.writeSteps ? { writeSteps: declaration.writeSteps } : {}),
|
|
1847
1842
|
run: (args) => Effect.gen(function* () {
|
|
1848
|
-
const problem =
|
|
1849
|
-
? fieldsProblem(args["fields"], names, isList ? "a Slides list item" : "the result")
|
|
1850
|
-
: undefined;
|
|
1843
|
+
const problem = fields?.problem(args["fields"]);
|
|
1851
1844
|
if (problem)
|
|
1852
1845
|
return yield* Effect.fail(new Refusal({ error: problem }));
|
|
1853
1846
|
const current = yield* Effect.serviceOption(Operation);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readOnlyNotice } from "../_shared/guide.js";
|
|
1
2
|
import { keys, optionsOf, variants } from "../../config-schema.js";
|
|
2
3
|
import { defineApiProvider } from "../../http/provider.js";
|
|
3
4
|
import { authenticate, connectedAccount, MAX_RESPONSE_BYTES, READ_CAP_MS, MODE_COPY, probe, safety, stripeErrors, StripeApi, stripeIndex, stripeBinding, STRIPE_PAGES, STRIPE_VERDICTS, } from "./api.js";
|
|
@@ -63,7 +64,7 @@ function usageGuide(options) {
|
|
|
63
64
|
`Connected account: ${account}. Every call acts as this Connect account; the platform account's own objects are out of reach here.`,
|
|
64
65
|
]
|
|
65
66
|
: []),
|
|
66
|
-
...(options.readOnly === true
|
|
67
|
+
...readOnlyNotice(options.readOnly === true),
|
|
67
68
|
],
|
|
68
69
|
required: true,
|
|
69
70
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Stripe's reviewed value-safety table
|
|
1
|
+
// Stripe's reviewed value-safety table, on the shared mechanism in
|
|
2
2
|
// `../_shared/rest/value-safety.ts`: every operation the detector flags in the
|
|
3
3
|
// pinned spec (`evidence/value-safety.json`) has one verdict here, and
|
|
4
4
|
// `provider.safety.node.test.ts` runs the shared harness over it.
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
// - Issuing card numbers and CVCs come back only as expansions, which the
|
|
49
49
|
// connector refuses on every Issuing path (`api.ts`); the card operations
|
|
50
50
|
// also redact both fields.
|
|
51
|
-
// Embedded URL verdict
|
|
51
|
+
// Embedded URL verdict: reviewed paths + global signing parameters.
|
|
52
52
|
// Evidence: digest-verified Stripe v2555, components/schemas/charge's
|
|
53
53
|
// failure_message ("Message to user further explaining reason for charge
|
|
54
54
|
// failure") and charge_outcome.seller_message (Stripe's human-readable
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Account and money\n\nBefore investigating a payment or moving money, confirm the account and mode\nwith `get_stripe_account_info`. A Connect account's objects belong to that\naccount, rather than the platform's account. Sandbox objects do not establish anything about live\ncustomers, revenue or payouts.\n\nResolve ids before acting. Stripe ids identify types: `cus_` customer, `sub_`\nsubscription, `ch_` charge, `pi_` PaymentIntent, `in_` invoice and `acct_`\naccount. A plausible-looking id can refer to a different object or to nobody.\n\nAmounts are integers in the currency's minor unit: 1099 is 10.99 USD, while\n10 is 10 JPY. Keep currencies separate when totaling balances or payments.\n\n## Payment investigations\n\nA PaymentIntent's `latest_charge` joins it to the charge holding the payment's\n`outcome`, `failure_code` and `failure_message`. Follow that reference to\nexplain a decline. Search supports a documented field set for each resource;\ncharge search has no `payment_intent` field. Resolve the parent and follow its\nreference when the needed relationship is not searchable.\n\nSearch is eventually consistent; use a direct object read to reconcile a\nrecent write. A POST's idempotency key identifies the exact operation and\nparameters. Stripe v1 may prune keys after 24 hours, so reconcile the object\nbefore attempting the operation outside that window. A new key can create a\nsecond payment or refund.\n\nIn v1 requests, an empty string (`\"\"`) or `null` unsets a field.\n\n## Data reduction and boundaries\n\nKeep only the customer and payment facts the question needs. Reduce lists to\ncounts, totals or matching records before returning them to the conversation.\nUse the account and balance summaries when their projections answer the task;\nother account fields and balance transactions are available through the API.\n\nCredential creation, customer/account sign-in links, secret payload expansions,\nfull Issuing card details, file uploads and the meter-event stream are outside\nthis connection's surface. Use the Dashboard or application server for those\nflows. Single meter events remain available. A guest Checkout payment page is\navailable only when it is unbound to a Customer or Account and cannot manage\nsaved payment methods; use a payment link or hosted invoice for other cases.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `stripe_api_details`, `stripe_api_read`, `stripe_api_search`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `stripe_api_search` finds this API's operations and reads no account data, `stripe_api_details` shows one operation's contract, and `stripe_api_read` calls a reviewed read. `stripe_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused. 15 operations are refused by design; use `stripe_api_details` with the method and path to
|
|
3
|
+
"content": "## Account and money\n\nBefore investigating a payment or moving money, confirm the account and mode\nwith `get_stripe_account_info`. A Connect account's objects belong to that\naccount, rather than the platform's account. Sandbox objects do not establish anything about live\ncustomers, revenue or payouts.\n\nResolve ids before acting. Stripe ids identify types: `cus_` customer, `sub_`\nsubscription, `ch_` charge, `pi_` PaymentIntent, `in_` invoice and `acct_`\naccount. A plausible-looking id can refer to a different object or to nobody.\n\nAmounts are integers in the currency's minor unit: 1099 is 10.99 USD, while\n10 is 10 JPY. Keep currencies separate when totaling balances or payments.\n\n## Payment investigations\n\nA PaymentIntent's `latest_charge` joins it to the charge holding the payment's\n`outcome`, `failure_code` and `failure_message`. Follow that reference to\nexplain a decline. Search supports a documented field set for each resource;\ncharge search has no `payment_intent` field. Resolve the parent and follow its\nreference when the needed relationship is not searchable.\n\nSearch is eventually consistent; use a direct object read to reconcile a\nrecent write. A POST's idempotency key identifies the exact operation and\nparameters. Stripe v1 may prune keys after 24 hours, so reconcile the object\nbefore attempting the operation outside that window. A new key can create a\nsecond payment or refund.\n\nIn v1 requests, an empty string (`\"\"`) or `null` unsets a field.\n\n## Data reduction and boundaries\n\nKeep only the customer and payment facts the question needs. Reduce lists to\ncounts, totals or matching records before returning them to the conversation.\nUse the account and balance summaries when their projections answer the task;\nother account fields and balance transactions are available through the API.\n\nCredential creation, customer/account sign-in links, secret payload expansions,\nfull Issuing card details, file uploads and the meter-event stream are outside\nthis connection's surface. Use the Dashboard or application server for those\nflows. Single meter events remain available. A guest Checkout payment page is\navailable only when it is unbound to a Customer or Account and cannot manage\nsaved payment methods; use a payment link or hosted invoice for other cases.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `stripe_api_details`, `stripe_api_read`, `stripe_api_search`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `stripe_api_search` finds this API's operations and reads no account data, `stripe_api_details` shows one operation's contract, and `stripe_api_read` calls a reviewed read. Details' `response.fields` promises selection only; `withheld` hints are incomplete, and absence never means safe or readable. `stripe_api_write` calls a reviewed write; the host may ask the user to confirm it. An operation without a reviewed verdict is refused. 15 operations are refused by design; use `stripe_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. A write that takes `idempotencyKey` may be repeated with the same key and arguments while the vendor retains it.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. `not_found` means the record does not exist; this API tells absence from a permission gap. `conflict` means: HTTP 409 when the request conflicts with another request, including a concurrent use of the same idempotency key; HTTP 429 with code lock_timeout when another request holds the object's lock.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
4
4
|
"instructionsHeading": "Account instructions"
|
|
5
5
|
};
|