@zackbart/connecta 0.37.0 → 0.38.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.
Files changed (183) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +2 -2
  3. package/bin/connecta.mjs +1 -39
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.js +2 -4
  6. package/dist/agent-identity.js +1 -1
  7. package/dist/auth/clerk-sdk/generated.js +2 -1
  8. package/dist/auth/cloudflare-access.d.ts +3 -1
  9. package/dist/auth/cloudflare-access.js +22 -11
  10. package/dist/auth/downstream-oauth.js +195 -270
  11. package/dist/auth/downstream-revocation.js +1 -1
  12. package/dist/auth/oauth-grant.js +11 -0
  13. package/dist/auth/oauth-token-response.js +1 -1
  14. package/dist/auth/vercel-agent.js +2 -1
  15. package/dist/auth-elicitation.js +2 -2
  16. package/dist/branding.d.ts +1 -15
  17. package/dist/branding.js +2 -2
  18. package/dist/bytes.js +42 -0
  19. package/dist/call-admission-context.js +2 -5
  20. package/dist/call-admission.js +5 -0
  21. package/dist/call-recovery.js +0 -3
  22. package/dist/catalog-cache.js +26 -7
  23. package/dist/catalog-fingerprint.js +2 -18
  24. package/dist/catalog-revision.js +9 -0
  25. package/dist/catalog-service.js +51 -20
  26. package/dist/config-defaults.d.ts +1 -0
  27. package/dist/config-defaults.js +1 -0
  28. package/dist/config-schema.d.ts +1 -1
  29. package/dist/config-schema.js +6 -2
  30. package/dist/config.d.ts +41 -14
  31. package/dist/config.js +21 -71
  32. package/dist/connector-caller.js +1 -1
  33. package/dist/connectors/api-connector.js +4 -3
  34. package/dist/connectors/guarded-fetch.d.ts +0 -6
  35. package/dist/connectors/guarded-fetch.js +1 -11
  36. package/dist/connectors/remote-mcp.js +93 -69
  37. package/dist/connectors/resource-uri.js +0 -4
  38. package/dist/credentials.js +3 -0
  39. package/dist/d1.d.ts +0 -89
  40. package/dist/d1.js +0 -34
  41. package/dist/downstream-input.js +2 -2
  42. package/dist/execute.js +161 -116
  43. package/dist/executor-result.js +2 -1
  44. package/dist/executors/quickjs-runtime.js +2 -25
  45. package/dist/executors/quickjs.js +1 -1
  46. package/dist/guest-runtime.js +5 -3
  47. package/dist/guest-types.d.ts +5 -5
  48. package/dist/http/client.js +1 -1
  49. package/dist/http/errors.js +2 -2
  50. package/dist/http/json-schema.js +1 -1
  51. package/dist/http/node-transport.js +0 -1
  52. package/dist/http/oauth.js +1 -1
  53. package/dist/http/paging.js +6 -9
  54. package/dist/http/provider.js +1 -1
  55. package/dist/http/safety.js +1 -1
  56. package/dist/http/services.js +1 -8
  57. package/dist/http/tools.js +1 -7
  58. package/dist/http/vendor.js +7 -7
  59. package/dist/identity.js +2 -6
  60. package/dist/index.js +1 -2
  61. package/dist/invocation-auth.js +2 -1
  62. package/dist/invocation.js +8 -2
  63. package/dist/meta-tools.js +27 -27
  64. package/dist/native-tools.js +2 -1
  65. package/dist/oauth-handoff.js +2 -2
  66. package/dist/operation-vocabulary.js +1 -1
  67. package/dist/operator-record.d.ts +8 -2
  68. package/dist/operator-record.js +18 -50
  69. package/dist/operator-ui/contract.d.ts +4 -2
  70. package/dist/operator-ui/generated.js +3 -3
  71. package/dist/operator-ui/model.d.ts +7 -2
  72. package/dist/program-writes.js +3 -8
  73. package/dist/provider-call.js +0 -7
  74. package/dist/provider.d.ts +1 -1
  75. package/dist/providers/_shared/google/kit.js +1 -6
  76. package/dist/providers/_shared/google/service-account.js +1 -3
  77. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  78. package/dist/providers/_shared/guide.js +3 -0
  79. package/dist/providers/_shared/json.js +12 -0
  80. package/dist/providers/_shared/rest/compact-index.js +1 -1
  81. package/dist/providers/_shared/rest/kit.js +1 -3
  82. package/dist/providers/_shared/rest/operation-index.js +1 -3
  83. package/dist/providers/_shared/rest/value-safety.js +26 -28
  84. package/dist/providers/breeze/api.js +2 -7
  85. package/dist/providers/breeze/index.js +4 -2
  86. package/dist/providers/breeze/tools.js +3 -4
  87. package/dist/providers/ccb/api.js +1 -6
  88. package/dist/providers/ccb/index.js +2 -1
  89. package/dist/providers/ccb/tools.js +1 -6
  90. package/dist/providers/cloudflare/api.js +2 -2
  91. package/dist/providers/cloudflare/index.js +2 -1
  92. package/dist/providers/cloudflare/safety.js +3 -3
  93. package/dist/providers/docs/tools.js +2 -10
  94. package/dist/providers/drive/tools.js +12 -25
  95. package/dist/providers/forms/tools.js +1 -9
  96. package/dist/providers/gmail/tools.js +5 -21
  97. package/dist/providers/overflow/api.js +1 -3
  98. package/dist/providers/overflow/index.js +2 -1
  99. package/dist/providers/overflow/tools.js +3 -7
  100. package/dist/providers/planning-center/api.js +2 -1
  101. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  102. package/dist/providers/planning-center/lib/json-api.js +1 -9
  103. package/dist/providers/planning-center/tools.js +2 -1
  104. package/dist/providers/sheets/tools.js +1 -9
  105. package/dist/providers/slides/tools.js +4 -13
  106. package/dist/providers/stripe/index.js +2 -1
  107. package/dist/providers/stripe/safety.js +2 -2
  108. package/dist/providers/tithely/api.js +2 -7
  109. package/dist/providers/tithely/index.js +3 -2
  110. package/dist/providers/tithely/tools.js +7 -5
  111. package/dist/providers/vercel/api.js +1 -6
  112. package/dist/providers/vercel/lib/pin.js +1 -1
  113. package/dist/providers/vercel/safety.js +1 -1
  114. package/dist/providers/vercel/tools.js +2 -1
  115. package/dist/redaction-marker.js +2 -2
  116. package/dist/registry.d.ts +4 -1
  117. package/dist/registry.js +35 -22
  118. package/dist/request-principal.js +15 -2
  119. package/dist/request-state.js +2 -2
  120. package/dist/routes/access-tokens.js +1 -1
  121. package/dist/routes/activity-disclosure.js +1 -1
  122. package/dist/routes/connect.js +2 -2
  123. package/dist/routes/credentials.js +1 -1
  124. package/dist/routes/dispatch.js +10 -0
  125. package/dist/routes/mcp.js +23 -21
  126. package/dist/routes/oauth-management.js +1 -1
  127. package/dist/routes/operator.js +53 -7
  128. package/dist/routes/shared.d.ts +8 -8
  129. package/dist/routes/shared.js +22 -15
  130. package/dist/routes/ui-config.js +6 -6
  131. package/dist/routes/ui-data.js +2 -2
  132. package/dist/routes/ui.js +9 -4
  133. package/dist/runtime/call-admission.js +1 -1
  134. package/dist/runtime/call.js +1 -1
  135. package/dist/runtime/settlement.js +1 -1
  136. package/dist/runtime/shared-read.js +2 -15
  137. package/dist/sent-secrets.d.ts +14 -6
  138. package/dist/sent-secrets.js +231 -85
  139. package/dist/server.js +4 -4
  140. package/dist/skills.js +8 -35
  141. package/dist/sqlite.d.ts +0 -19
  142. package/dist/sqlite.js +2 -83
  143. package/dist/storage/keys.js +29 -124
  144. package/dist/storage/sql-native.js +14 -111
  145. package/dist/storage/sql.d.ts +1 -24
  146. package/dist/storage/sql.js +1 -12
  147. package/dist/tool-error.js +4 -0
  148. package/dist/types.d.ts +4 -3
  149. package/dist/ui.js +3 -0
  150. package/dist/url-safety.js +0 -4
  151. package/dist/usage-guide.js +2 -2
  152. package/dist/version.d.ts +1 -1
  153. package/dist/version.js +1 -1
  154. package/dist/worker.js +1 -1
  155. package/documentation/README.md +6 -42
  156. package/documentation/architecture.md +190 -507
  157. package/documentation/auth.md +172 -58
  158. package/documentation/code-mode.md +98 -66
  159. package/documentation/deploying.md +19 -40
  160. package/documentation/integrating.md +8 -19
  161. package/documentation/meta-tools.md +23 -6
  162. package/documentation/migration-0.33.md +5 -5
  163. package/documentation/migration-0.34.md +2 -4
  164. package/documentation/migration-retired-layouts.md +74 -0
  165. package/documentation/operator-tests.md +19 -119
  166. package/documentation/operator-ui.md +46 -8
  167. package/documentation/provider-auth-migration.md +4 -10
  168. package/documentation/provider-migration-0.29.md +4 -15
  169. package/examples/worker/AGENTS.md +4 -6
  170. package/examples/worker/README.md +20 -115
  171. package/guides/connecting-agents.md +7 -30
  172. package/guides/how-it-works.md +2 -2
  173. package/guides/safety-and-trust.md +4 -1
  174. package/package.json +19 -19
  175. package/templates/node/README.md +9 -26
  176. package/templates/node/package.json +1 -1
  177. package/dist/auth/oauth-v2-migration.js +0 -135
  178. package/dist/provider-call-facts.d.ts +0 -115
  179. package/dist/provider-call.d.ts +0 -112
  180. package/dist/storage/d1-copy.js +0 -255
  181. package/dist/storage/sql-schema.d.ts +0 -6
  182. package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
  183. package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
@@ -1,3 +1,5 @@
1
+ import { asRecord, asArray, compact } from "../_shared/json.js";
2
+ import { decodeBase64, decodeBase64url } from "../../bytes.js";
1
3
  // Google Drive named workflows, bounded projections and bound continuation on the provider kit.
2
4
  import { Effect, Option, Schema } from "effect";
3
5
  import { ConnectorCallError } from "../../errors.js";
@@ -83,15 +85,6 @@ const DETAIL_FIELDS = `${SUMMARY_FIELDS},description,createdTime,lastModifyingUs
83
85
  const PERMISSION_FIELDS = "id,type,role,emailAddress,domain,displayName,allowFileDiscovery,expirationTime,deleted,permissionDetails(inherited,inheritedFrom)";
84
86
  /** Every file request reaches shared drives as well as My Drive. */
85
87
  const ALL_DRIVES = { supportsAllDrives: true };
86
- function asRecord(value) {
87
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
88
- }
89
- function asArray(value) {
90
- return Array.isArray(value) ? value : [];
91
- }
92
- function compact(value) {
93
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
94
- }
95
88
  function text(value) {
96
89
  return typeof value === "string" && value !== "" ? value : undefined;
97
90
  }
@@ -260,8 +253,7 @@ function encodeCursor(ctx, resume) {
260
253
  const FINGERPRINT = /^[A-Za-z0-9_-]{16}$/;
261
254
  function decodeCursor(cursor) {
262
255
  try {
263
- const base = cursor.replace(/-/g, "+").replace(/_/g, "/");
264
- const json = new TextDecoder().decode(fromBase64(base + "=".repeat((4 - (base.length % 4)) % 4)));
256
+ const json = new TextDecoder().decode(decodeBase64url(cursor, true));
265
257
  const [token, skip, size, scope, page, last] = JSON.parse(json);
266
258
  const midPage = typeof page === "string" && FINGERPRINT.test(page) && typeof last === "string" && FINGERPRINT.test(last);
267
259
  if ((token === null || (typeof token === "string" && token !== "")) &&
@@ -471,19 +463,6 @@ function base64(bytes) {
471
463
  }
472
464
  return btoa(binary);
473
465
  }
474
- function fromBase64(value) {
475
- let binary;
476
- try {
477
- binary = atob(value);
478
- }
479
- catch {
480
- throw new ConnectorCallError("invalid_args", "contentBase64 is not valid base64.");
481
- }
482
- const bytes = new Uint8Array(binary.length);
483
- for (let index = 0; index < binary.length; index += 1)
484
- bytes[index] = binary.charCodeAt(index);
485
- return bytes;
486
- }
487
466
  /**
488
467
  * Why the content tool returns no content for a file, as the note says it.
489
468
  * Only a well-formed id or media type is interpolated; anything else Drive
@@ -681,7 +660,7 @@ function media(args) {
681
660
  };
682
661
  }
683
662
  if (hasBinary) {
684
- const bytes = fromBase64(args["contentBase64"]);
663
+ const bytes = contentBytes(args["contentBase64"]);
685
664
  if (bytes.length > BINARY_INLINE_BYTES) {
686
665
  throw new ConnectorCallError("invalid_args", `contentBase64 decodes to ${bytes.length} bytes, past this connection's ${BINARY_INLINE_BYTES}-byte upload cap.`);
687
666
  }
@@ -1288,3 +1267,11 @@ function decisionInputs(file, keys) {
1288
1267
  }
1289
1268
  }
1290
1269
  }
1270
+ function contentBytes(value) {
1271
+ try {
1272
+ return decodeBase64(value);
1273
+ }
1274
+ catch {
1275
+ throw new ConnectorCallError("invalid_args", "contentBase64 is not valid base64.");
1276
+ }
1277
+ }
@@ -1,3 +1,4 @@
1
+ import { asRecord, asArray, compact } from "../_shared/json.js";
1
2
  // Forms projections and revision-checked workflows on the standard provider kit.
2
3
  import { Effect, Option, Schema } from "effect";
3
4
  import { ConnectorCallError } from "../../errors.js";
@@ -40,15 +41,6 @@ const MAX_REQUESTS = 100;
40
41
  * cursor included, is measured before it is returned.
41
42
  */
42
43
  const PAGE_RESERVE_BYTES = 4 * 1024;
43
- function asRecord(value) {
44
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
45
- }
46
- function asArray(value) {
47
- return Array.isArray(value) ? value : [];
48
- }
49
- function compact(value) {
50
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
51
- }
52
44
  function text(value) {
53
45
  return typeof value === "string" && value !== "" ? value : undefined;
54
46
  }
@@ -1,3 +1,5 @@
1
+ import { asRecord, asArray, compact } from "../_shared/json.js";
2
+ import { decodeBase64url } from "../../bytes.js";
1
3
  // Curated Gmail workflows over real Effect declarations and the standard kit boundary.
2
4
  import { Effect, Option } from "effect";
3
5
  import { defined } from "../../connectors/api-connector.js";
@@ -31,15 +33,6 @@ const MAX_RECIPIENTS = 100;
31
33
  const MAX_ATTACHMENT_BYTES = 1024 * 1024;
32
34
  /** Same direct-call ceiling and minimum result budget as Drive content reads. */
33
35
  const MAX_ATTACHMENT_RESULT_BYTES = 4 * 1024 * 1024;
34
- function asRecord(value) {
35
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
36
- }
37
- function asArray(value) {
38
- return Array.isArray(value) ? value : [];
39
- }
40
- function compact(value) {
41
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
42
- }
43
36
  function text(value) {
44
37
  return typeof value === "string" && value !== "" ? value : undefined;
45
38
  }
@@ -83,15 +76,6 @@ function htmlToText(ctx, html) {
83
76
  .replace(/\n{3,}/g, "\n\n")
84
77
  .trim();
85
78
  }
86
- function base64UrlBytes(data) {
87
- const base64 = data.replace(/-/g, "+").replace(/_/g, "/");
88
- const binary = atob(base64 + "=".repeat((4 - (base64.length % 4)) % 4));
89
- const bytes = new Uint8Array(binary.length);
90
- for (let index = 0; index < binary.length; index += 1) {
91
- bytes[index] = binary.charCodeAt(index);
92
- }
93
- return bytes;
94
- }
95
79
  function charsetOf(ctx, part) {
96
80
  const type = header(part, "Content-Type") ?? "";
97
81
  if (redactBeforeCut(ctx, type) !== type)
@@ -113,7 +97,7 @@ function charsetOf(ctx, part) {
113
97
  function decodeIn(ctx, part, data) {
114
98
  let bytes;
115
99
  try {
116
- bytes = base64UrlBytes(data);
100
+ bytes = decodeBase64url(data, true);
117
101
  }
118
102
  catch {
119
103
  return { text: "" };
@@ -346,7 +330,7 @@ function attachmentBytes(client, ctx, messageId, part) {
346
330
  validateSize(provenance.originalLength);
347
331
  let bytes;
348
332
  try {
349
- bytes = base64UrlBytes(data);
333
+ bytes = decodeBase64url(data, true);
350
334
  }
351
335
  catch {
352
336
  throw new ConnectorCallError("connector_call_failed", "Gmail returned invalid base64url attachment data.", {
@@ -605,7 +589,7 @@ function openCursor(cursor, tool, call) {
605
589
  return undefined;
606
590
  let claims;
607
591
  try {
608
- const parsed = JSON.parse(new TextDecoder().decode(base64UrlBytes(String(cursor))));
592
+ const parsed = JSON.parse(new TextDecoder().decode(decodeBase64url(String(cursor), true)));
609
593
  if (parsed && typeof parsed === "object" && parsed.v === 1)
610
594
  claims = parsed;
611
595
  }
@@ -1,3 +1,4 @@
1
+ import { asRecord } from "../_shared/json.js";
1
2
  // Overflow v3: reviewed vendor declarations, derived operation index and paging contracts.
2
3
  // Public input: staging OpenAPI 3.0; exact retrieval and known upstream drift are in evidence/review.md.
3
4
  import { Effect, Schema } from "effect";
@@ -1003,6 +1004,3 @@ export const probe = {
1003
1004
  return `Overflow ${options.environment} accepted the client id and API key.${where}`;
1004
1005
  },
1005
1006
  };
1006
- function asRecord(value) {
1007
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1008
- }
@@ -1,3 +1,4 @@
1
+ import { readOnlyNotice } from "../_shared/guide.js";
1
2
  import { skill } from "./skill.generated.js";
2
3
  import { keys, optionsOf } from "../../config-schema.js";
3
4
  import { PROVIDER_COMMON } from "../../connectors/option-shapes.js";
@@ -81,7 +82,7 @@ export const overflow = defineApiProvider({
81
82
  : "Overflow staging: a sandbox with separate credentials and data.",
82
83
  `Nonprofit purpose: ${options.purpose}`,
83
84
  "Named reads minimize contact data; get_donor is the full-contact lookup. Webhook signing secrets are always removed.",
84
- ...(options.readOnly ? ["This connection is read-only: every write is refused."] : []),
85
+ ...readOnlyNotice(options.readOnly),
85
86
  ...(options.defaultPageSize !== undefined
86
87
  ? [`Paged lists return ${options.defaultPageSize} items by default on this connection.`]
87
88
  : []),
@@ -1,3 +1,5 @@
1
+ import { compact as compactDefined } from "../_shared/json.js";
2
+ import { asRecord, asArray } from "../_shared/json.js";
1
3
  // Overflow's curated kit tools: standard pages, safe projections and narrowing fields.
2
4
  import { Effect, Schema } from "effect";
3
5
  import { ConnectorCallError } from "../../errors.js";
@@ -5,14 +7,8 @@ import { Refusal } from "../../http/errors.js";
5
7
  import { Integer, isMinChars } from "../../http/json-schema.js";
6
8
  import { read, list } from "../../http/tools.js";
7
9
  import * as api from "./api.js";
8
- function asRecord(value) {
9
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
10
- }
11
- function asArray(value) {
12
- return Array.isArray(value) ? value : [];
13
- }
14
10
  function compact(value) {
15
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
11
+ return compactDefined(value);
16
12
  }
17
13
  /** A `{ id, name }` reference, or undefined when Overflow sent none. */
18
14
  function reference(value) {
@@ -1,3 +1,4 @@
1
+ import { asArray, asRecord } from "../_shared/json.js";
1
2
  import { Effect, Schema } from "effect";
2
3
  import { ConnectorCallError } from "../../errors.js";
3
4
  import { Refusal } from "../../http/errors.js";
@@ -11,7 +12,7 @@ import { PlanningCenterIndex } from "./lib/discovery.js";
11
12
  import { PCO_REFUSALS } from "./lib/verdicts.js";
12
13
  import { TABLE } from "./safety.js";
13
14
  import { excludedApps, series } from "./lib/guide.js";
14
- import { asRecord, asArray, resources, attribute, pageOf } from "./lib/json-api.js";
15
+ import { resources, attribute, pageOf } from "./lib/json-api.js";
15
16
  import { PLANNING_CENTER_API_BASE_URL, PLANNING_CENTER_APPS, PLANNING_CENTER_API_VERSIONS, DEFAULT_USER_AGENT, } from "./lib/constants.js";
16
17
  export { PLANNING_CENTER_API_BASE_URL, PLANNING_CENTER_APPS, PLANNING_CENTER_API_VERSIONS, MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, PLANNING_CENTER_MAX_RESPONSE_BYTES, PLANNING_CENTER_ADMISSION, } from "./lib/constants.js";
17
18
  export const CREDENTIAL = {
@@ -55,4 +55,4 @@ export declare const PLANNING_CENTER_MAX_RESPONSE_BYTES: number;
55
55
  */
56
56
  export declare const PLANNING_CENTER_ADMISSION: ConnectorCallAdmissionPolicy;
57
57
  /** Planning Center refuses a request without a descriptive one (HTTP 403). */
58
- export declare const DEFAULT_USER_AGENT = "connecta/0.37.0 (+https://github.com/zackbart/connecta)";
58
+ export declare const DEFAULT_USER_AGENT = "connecta/0.38.0 (+https://github.com/zackbart/connecta)";
@@ -1,13 +1,5 @@
1
+ import { asRecord, asArray, compact } from "../../_shared/json.js";
1
2
  import { ConnectorCallError } from "../../../errors.js";
2
- export function asRecord(value) {
3
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4
- }
5
- export function asArray(value) {
6
- return Array.isArray(value) ? value : [];
7
- }
8
- export function compact(value) {
9
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
10
- }
11
3
  function isResource(value) {
12
4
  const record = asRecord(value);
13
5
  return typeof record["id"] === "string" && typeof record["type"] === "string";
@@ -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 { asRecord, compact, resources, indexIncluded, relId, related, relatedMany, pick, attribute, writeDocument, pageOf, } from "./lib/json-api.js";
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 }),
@@ -1,3 +1,4 @@
1
+ import { asRecord, asArray, compact } from "../_shared/json.js";
1
2
  // Google Sheets named projections, bounded cell reads and guarded writes.
2
3
  import { Effect, Option, Schema } from "effect";
3
4
  import { ConnectorCallError } from "../../errors.js";
@@ -38,15 +39,6 @@ const MAX_CELL_CHARS = 50_000;
38
39
  const MAX_WRITE_CELLS = 50_000;
39
40
  /** Named ranges a metadata read lists before reporting a size cut. */
40
41
  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
42
  function text(value) {
51
43
  return typeof value === "string" && value !== "" ? value : undefined;
52
44
  }
@@ -1,3 +1,5 @@
1
+ import { asRecord, asArray, compact } from "../_shared/json.js";
2
+ import { decodeBase64url } from "../../bytes.js";
1
3
  // Google Slides named workflows on real kit schemas, with bound content continuation.
2
4
  import { Effect, Option, Schema } from "effect";
3
5
  import { ConnectorCallError } from "../../errors.js";
@@ -52,7 +54,7 @@ const MAX_ECHO_CHARS = 100;
52
54
  /**
53
55
  * The comment kinds of Slides' Request. A batch carrying any of them has
54
56
  * comment changes Slides saves apart from the deck's, and only `ALL_SAVED`
55
- * says they all landed (#696).
57
+ * says they all landed.
56
58
  */
57
59
  /** The comment save states that say every comment change in a batch landed, or none was asked for. */
58
60
  const COMMENTS_SETTLED = new Set(["NO_UPDATES_REQUESTED", "ALL_SAVED"]);
@@ -121,15 +123,6 @@ const COMMENT_DECK_FIELDS = "presentationId,revisionId,commentsViewMode,comments
121
123
  "slides(objectId,commentAnchors,slideProperties(notesPage(objectId,commentAnchors)))," +
122
124
  "layouts(objectId,commentAnchors),masters(objectId,commentAnchors),notesMaster(objectId,commentAnchors)";
123
125
  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
126
  function text(value) {
134
127
  return typeof value === "string" && value !== "" ? value : undefined;
135
128
  }
@@ -226,9 +219,7 @@ function decodeCursor(value, expected) {
226
219
  const refuse = (why) => new ConnectorCallError("invalid_args", `${why} Omit cursor to start from the beginning.`);
227
220
  let cursor;
228
221
  try {
229
- const base64 = String(value).replace(/-/g, "+").replace(/_/g, "/");
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))));
222
+ cursor = JSON.parse(new TextDecoder().decode(decodeBase64url(String(value), true)));
232
223
  }
233
224
  catch {
234
225
  throw refuse("cursor is not one this connection issued; pass page.nextCursor back unchanged.");
@@ -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 ? ["This connection is read-only: every write is refused."] : []),
67
+ ...readOnlyNotice(options.readOnly === true),
67
68
  ],
68
69
  required: true,
69
70
  };
@@ -1,4 +1,4 @@
1
- // Stripe's reviewed value-safety table (#801), on the shared mechanism in
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 (#812): reviewed paths + global signing parameters.
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,4 +1,5 @@
1
- // Tithe.ly's giving API (v1) as a kit vendor (decision 0010): the operations,
1
+ import { asRecord, asArray } from "../_shared/json.js";
2
+ // Tithe.ly's giving API (v1) as a kit vendor: the operations,
2
3
  // what its statuses mean, its credential, its in-band refusal, the probe, and
3
4
  // the value-safety review. Facts from the per-operation reference at
4
5
  // https://docs.tithe.ly/reference/introduction, each marked where it is
@@ -509,12 +510,6 @@ export function tithelySafety(sensitiveFields) {
509
510
  }
510
511
  // --- Shared readers ------------------------------------------------------------------
511
512
  const failed = (error) => Effect.fail(new Refusal({ error }));
512
- export function asRecord(value) {
513
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
514
- }
515
- export function asArray(value) {
516
- return Array.isArray(value) ? value : [];
517
- }
518
513
  export function text(value) {
519
514
  return typeof value === "string" && value !== "" ? value : undefined;
520
515
  }
@@ -1,6 +1,7 @@
1
+ import { readOnlyNotice } from "../_shared/guide.js";
1
2
  import { skill } from "./skill.generated.js";
2
3
  /**
3
- * Tithe.ly's giving API (v1) on the provider kit (decision 0010). No SDK
4
+ * Tithe.ly's giving API (v1) on the provider kit. No SDK
4
5
  * exists to depend on, and none would be welcome: this is a payments API, and
5
6
  * every tool was chosen and written, not generated (decision record 0001:
6
7
  * hand-authored tools). The vendor lives in `api.ts` and the tools in
@@ -67,7 +68,7 @@ function usageGuide(options) {
67
68
  sensitiveFields(options) === "return"
68
69
  ? "This connection returns organizations' payout bank and legal-contact details through tithely_api_read; the named reads still omit them."
69
70
  : "This connection withholds organizations' payout bank and legal-contact details from every result.",
70
- ...(options.readOnly === true ? ["This connection is read-only: every write is refused."] : []),
71
+ ...readOnlyNotice(options.readOnly === true),
71
72
  ...(options.defaultPageSize !== undefined
72
73
  ? [`List tools return ${options.defaultPageSize} rows by default on this connection.`]
73
74
  : []),
@@ -1,8 +1,10 @@
1
- // Tithe.ly's curated tools on the provider kit (decision 0010) and their
1
+ import { compact as compactDefined } from "../_shared/json.js";
2
+ import { asArray, asRecord } from "../_shared/json.js";
3
+ // Tithe.ly's curated tools on the provider kit and their
2
4
  // projections. Decision 0011's step 2: standard lists ({ items, page } with
3
5
  // limit, cursor and fields), fields on every named read, and no `raw`: what a
4
6
  // projection drops stays reachable through the standard hatch, which the kit
5
- // builds from `api.ts`'s index and verdicts (decision 0014). Each named read
7
+ // builds from `api.ts`'s index and verdicts. Each named read
6
8
  // earns its place against the hatch with paging Tithe.ly cannot report
7
9
  // (lookahead), units (cents, ISO dates), joins (donor, organization and
8
10
  // payment method inline) or reconciliation of a known id after a write.
@@ -13,7 +15,7 @@ import { Refusal } from "../../http/errors.js";
13
15
  import { Integer } from "../../http/json-schema.js";
14
16
  import { lookahead } from "../../http/paging.js";
15
17
  import { get as getTool, list } from "../../http/tools.js";
16
- import { asArray, asRecord, entity, getCharge, getRecurring, listAccounts, listAllAccounts, listCharges, listOrganizations, listPaymentMethods, listRecurring, MAX_PAGE_SIZE, PAYOUT_CURRENCY, text, } from "./api.js";
18
+ import { entity, getCharge, getRecurring, listAccounts, listAllAccounts, listCharges, listOrganizations, listPaymentMethods, listRecurring, MAX_PAGE_SIZE, PAYOUT_CURRENCY, text, } from "./api.js";
17
19
  // --- Schemas ---------------------------------------------------------------------
18
20
  const Id = Schema.String.check(Schema.isMinLength(1));
19
21
  const Text = Schema.optionalKey(Schema.String);
@@ -267,7 +269,7 @@ function seconds(value, name) {
267
269
  return Effect.succeed(Math.floor(parsed / 1000));
268
270
  }
269
271
  function compact(value) {
270
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
272
+ return compactDefined(value);
271
273
  }
272
274
  /** Integer cents from Tithe.ly's string-or-number amounts. */
273
275
  function cents(value) {
@@ -325,7 +327,7 @@ function projectOrganization(value) {
325
327
  return compact({
326
328
  organizationId: text(organization["organization_id"]),
327
329
  name: text(organization["name"]),
328
- // The giving widget id embeds Tithe.ly's giving form; the step-2 eval showed agents need it (decision 0014).
330
+ // The giving widget id embeds Tithe.ly's giving form; the step-2 eval showed agents need it.
329
331
  widgetId: text(organization["widget_id"]),
330
332
  ownerAccountId: text(organization["account_id"]),
331
333
  website: text(organization["website"]),
@@ -1,3 +1,4 @@
1
+ import { isRecord } from "../_shared/json.js";
1
2
  // Vercel's pinned contract, authentication, failures and value-safety boundary.
2
3
  import { Effect, Schema } from "effect";
3
4
  import { ConnectorCallError } from "../../errors.js";
@@ -24,9 +25,6 @@ const READ_POSTS = [
24
25
  ["POST", "/v1/registrar/domains/search", "Searches domain availability and pricing; buys nothing."],
25
26
  ["POST", "/v8/artifacts", "Reads Remote Cache metadata for a list of artifact hashes; uploads nothing."],
26
27
  ];
27
- export function isRecord(value) {
28
- return typeof value === "object" && value !== null && !Array.isArray(value);
29
- }
30
28
  export function record(value) {
31
29
  return isRecord(value) ? value : {};
32
30
  }
@@ -36,9 +34,6 @@ export function text(value) {
36
34
  export function invalid(message) {
37
35
  throw new ConnectorCallError("invalid_args", message);
38
36
  }
39
- export function compact(value) {
40
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
41
- }
42
37
  function truthy(value) {
43
38
  return value === true || value === 1 || (typeof value === "string" && /^(?:1|true)$/i.test(value.trim()));
44
39
  }
@@ -4,7 +4,7 @@ import { ConnectorCallError } from "../../../errors.js";
4
4
  import { Refusal } from "../../../http/errors.js";
5
5
  import { Budget } from "../../../http/services.js";
6
6
  import { checked } from "../../_shared/rest/kit.js";
7
- export const MAX_PIN_LOOKUPS = 8;
7
+ const MAX_PIN_LOOKUPS = 8;
8
8
  export const PIN_WRITE_STEPS = Object.freeze({
9
9
  write: "once",
10
10
  lookup1: "once",
@@ -214,7 +214,7 @@ const absent = {
214
214
  //
215
215
  // Residual risk: a secret a person typed into a free-text
216
216
  // name, description, log line, or identifier is out of scope.
217
- // Embedded URL verdict (#812): reviewed paths + global signing parameters.
217
+ // Embedded URL verdict: reviewed paths + global signing parameters.
218
218
  // Evidence: pinned index/candidates sha256:5a702fd2dc76 identify deployment
219
219
  // reads/creation/listing, build events and runtime logs; the operation/path
220
220
  // list below scans errorMessage, deployments.errorMessage, payload.text/text
@@ -1,3 +1,4 @@
1
+ import { compact, isRecord } from "../_shared/json.js";
1
2
  // Curated Vercel metadata and bounded logs through kit-owned call services.
2
3
  import { Effect, Schema } from "effect";
3
4
  import { ConnectorCallError } from "../../errors.js";
@@ -9,7 +10,7 @@ import { schema, typedParams } from "../_shared/rest/kit.js";
9
10
  import { refuseWithheldUpload } from "../_shared/rest/value-safety.js";
10
11
  import { rowStop } from "./lib/logs.js";
11
12
  import { checked } from "../_shared/rest/kit.js";
12
- import { admit, compact, defaultTeam, isRecord, invalid, named, queryOf, queryPairs, record, reviewedCode, restCall, text, vercelIndex, refuse, checkServer, } from "./api.js";
13
+ import { admit, defaultTeam, invalid, named, queryOf, queryPairs, record, reviewedCode, restCall, text, vercelIndex, refuse, checkServer, } from "./api.js";
13
14
  import { pinOptions, readPinnedTeam, PIN_WRITE_STEPS } from "./lib/pin.js";
14
15
  const MAX_LOG_BYTES = 1024 * 1024;
15
16
  const MAX_LOG_TEXT = 4_000;
@@ -7,7 +7,7 @@ export const REDACTED = "[redacted]";
7
7
  * secrets from every value of a vendor body before result safety reads it, so a rule that decides
8
8
  * on a value (a type discriminator, a record's label or type, a URL's scheme or parameter names,
9
9
  * a Content-Type) can find the placeholder where the vendor wrote what the rule matches.
10
- * Such a decision takes its most protective outcome (#703).
10
+ * Such a decision takes its most protective outcome.
11
11
  */
12
12
  export function redactionAltered(value) {
13
13
  return typeof value === "string" && value.includes(REDACTED);
@@ -92,7 +92,7 @@ function credentialUnit(code) {
92
92
  }
93
93
  /**
94
94
  * The text right after the placeholder at `at` that may be the rest of a credential whose public
95
- * prefix redaction took (#703): up to the first unit `ends` accepts, reading later placeholders
95
+ * prefix redaction took: up to the first unit `ends` accepts, reading later placeholders
96
96
  * whole so none is split. Redaction can take `sk_live_`, `whsec_`, `Bearer `, or a signing
97
97
  * parameter's name and `=`, leaving what follows; with that prefix known, any remainder rebuilds
98
98
  * the credential, however short or encoded, so `readable` holds whenever it carries any base64 or
@@ -155,6 +155,8 @@ export interface RegistryScope {
155
155
  currentResultAccess?: RegistryView["recheckResultAccess"];
156
156
  /** The admitted caller, readable only by built-in connectors; see connector-caller.ts. */
157
157
  caller?: ConnectorCaller;
158
+ /** Operator admission, forwarded only to principal-forwarding remote MCP connectors. */
159
+ operatorCaller?: ConnectorCaller;
158
160
  }
159
161
  /**
160
162
  * Holds the connector set, resolves addresses, and coalesces tool listings
@@ -238,7 +240,7 @@ export declare class Registry implements RegistryView {
238
240
  *
239
241
  * `chunks[n]` lands on `resultKeys.chunk(id, n)` inside `partition` — the
240
242
  * layout connecta.result reads back, so a page fetches only the chunks it covers
241
- * instead of the whole stored result (issue #540). Chunking is a read-cost
243
+ * instead of the whole stored result. Chunking is a read-cost
242
244
  * decision, not a capacity one: however many keys an envelope occupies, it
243
245
  * is one stash entry charged its total ASCII length.
244
246
  *
@@ -246,6 +248,7 @@ export declare class Registry implements RegistryView {
246
248
  * row in one ledger record in storage, booked by compare-and-set before any
247
249
  * chunk is written, so every isolate and process sharing the store sees the
248
250
  * same entries and bytes. A pending write keeps its finite charge reserved.
251
+ * The whole operation, including reservation, stops after 45 seconds.
249
252
  * Writes stop after 30 seconds; absolute chunk expiries cannot pass the
250
253
  * booked deadline even on late commit. Rejected responses leave writes
251
254
  * unconfirmed and retain their charge. Cleanup/release and settlement each