esoul-sdk 0.19.0 → 0.20.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 (46) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/api-reference.md +658 -14
  3. package/dist/machine/program-manager.js +13 -1
  4. package/dist/manifest.d.ts +544 -18
  5. package/dist/manifest.js +210 -7
  6. package/dist/react.d.ts +35 -0
  7. package/dist/react.js +13 -0
  8. package/dist/server.d.ts +84 -2
  9. package/dist/server.js +47 -0
  10. package/dist/testing/index.d.ts +5 -0
  11. package/dist/testing/index.js +4 -0
  12. package/dist/testing/sim-gmail/bytes.d.ts +21 -0
  13. package/dist/testing/sim-gmail/bytes.js +125 -0
  14. package/dist/testing/sim-gmail/corpus.d.ts +69 -0
  15. package/dist/testing/sim-gmail/corpus.js +471 -0
  16. package/dist/testing/sim-gmail/errors.d.ts +26 -0
  17. package/dist/testing/sim-gmail/errors.js +71 -0
  18. package/dist/testing/sim-gmail/faults.d.ts +74 -0
  19. package/dist/testing/sim-gmail/faults.js +108 -0
  20. package/dist/testing/sim-gmail/http.d.ts +81 -0
  21. package/dist/testing/sim-gmail/http.js +793 -0
  22. package/dist/testing/sim-gmail/index.d.ts +149 -0
  23. package/dist/testing/sim-gmail/index.js +416 -0
  24. package/dist/testing/sim-gmail/mime.d.ts +78 -0
  25. package/dist/testing/sim-gmail/mime.js +352 -0
  26. package/dist/testing/sim-gmail/names.d.ts +22 -0
  27. package/dist/testing/sim-gmail/names.js +83 -0
  28. package/dist/testing/sim-gmail/personas.d.ts +40 -0
  29. package/dist/testing/sim-gmail/personas.js +250 -0
  30. package/dist/testing/sim-gmail/query.d.ts +51 -0
  31. package/dist/testing/sim-gmail/query.js +280 -0
  32. package/dist/testing/sim-gmail/random.d.ts +33 -0
  33. package/dist/testing/sim-gmail/random.js +116 -0
  34. package/dist/testing/sim-gmail/render.d.ts +30 -0
  35. package/dist/testing/sim-gmail/render.js +89 -0
  36. package/dist/testing/sim-gmail/types.d.ts +243 -0
  37. package/dist/testing/sim-gmail/types.js +2 -0
  38. package/dist/testing/sim-gmail/world.d.ts +217 -0
  39. package/dist/testing/sim-gmail/world.js +657 -0
  40. package/dist/types.d.ts +9 -0
  41. package/docs/02-manifest.md +2 -2
  42. package/docs/08-connections.md +146 -8
  43. package/docs/18-your-computer.md +3 -1
  44. package/llms-full.txt +151 -11
  45. package/package.json +3 -2
  46. package/schemas/plugin.schema.json +53 -2
package/api-reference.md CHANGED
@@ -7,11 +7,11 @@ the complete list it teaches from. In a Forge workbench the source itself is rea
7
7
  Regenerate: `npm run docs:api`.
8
8
 
9
9
  ==============================================================================
10
- ## `esoul-sdk` — 196 exports
10
+ ## `esoul-sdk` — 210 exports
11
11
 
12
12
  Manifest, events, tools, ops, bindings, contracts, charts, access words — what app.tsx and the shared modules import.
13
13
 
14
- ### Functions and values (115)
14
+ ### Functions and values (126)
15
15
 
16
16
  #### `ASSET_APP_MAX_BYTES` — const · src/assets.ts
17
17
 
@@ -221,6 +221,22 @@ Validate the envelope against the models it names. `modelFields` maps a model na
221
221
  function compileEnvelope( raw: CustomRolesEnvelopeRaw | undefined, models: Record<string, { fields: string[]; filterable: string[] }>, surfaces: string[], /** `roles.attributes` — every declared attribute may scope a composed role too. */ declared: Record<string, AttrType> = {}, ): CustomRolesEnvelope | null
222
222
  ```
223
223
 
224
+ #### `CREDENTIAL_FAMILIES` — const · src/manifest.ts
225
+
226
+ PLATFORM ACCOUNTS — the person's Google account, used by an app without the app ever holding it.
227
+
228
+ ```ts
229
+ const CREDENTIAL_FAMILIES: readonly ["google"]
230
+ ```
231
+
232
+ #### `CREDENTIAL_SLOT_RE` — const · src/manifest.ts
233
+
234
+ Slot names: lower-case words joined by `-` (`mailbox`, `work-calendar`).
235
+
236
+ ```ts
237
+ const CREDENTIAL_SLOT_RE: RegExp
238
+ ```
239
+
224
240
  #### `currentShareId` — function · src/helpers.ts
225
241
 
226
242
  The public share this page is being viewed through, or null.
@@ -421,6 +437,22 @@ A number the way a person reads a measurement: few digits, no noise.
421
437
  function formatValue(v: number): string
422
438
  ```
423
439
 
440
+ #### `GOOGLE_CREDENTIAL_SCOPES` — const · src/manifest.ts
441
+
442
+ The Google scopes an app may declare, by short name. Everything else is refused.
443
+
444
+ ```ts
445
+ const GOOGLE_CREDENTIAL_SCOPES: readonly ["gmail.readonly", "gmail.modify", "gmail.send", "gmail.compose", "gmail.labels", "calendar.events", "calendar.readonly", "contacts", "contacts.readonly", "drive.readonly", "drive.file"]
446
+ ```
447
+
448
+ #### `GOOGLE_IMPLICIT_SCOPES` — const · src/manifest.ts
449
+
450
+ Granted with every sign-in; never declared, never counted as missing.
451
+
452
+ ```ts
453
+ const GOOGLE_IMPLICIT_SCOPES: readonly ["openid", "email", "profile"]
454
+ ```
455
+
424
456
  #### `handleOp` — function · src/ops.ts
425
457
 
426
458
  Wrap an op so it receives PARSED input and refuses what does not fit. A missing body is `{}`, so an op declared `z.object({})` is called with no arguments the way `callPluginOp(id, "browse", nodeId)` calls it.
@@ -629,6 +661,22 @@ Generate secure URL-friendly unique ID.
629
661
  function nanoid<Type extends string>(size?: number): Type
630
662
  ```
631
663
 
664
+ #### `normaliseCredentialScopes` — function · src/manifest.ts
665
+
666
+ A slot's declared scopes as full URLs, implicit ones dropped, duplicates removed, order kept. Unknown scopes are dropped (the schema refused them already).
667
+
668
+ ```ts
669
+ function normaliseCredentialScopes(scopes: readonly string[]): string[]
670
+ ```
671
+
672
+ #### `normaliseGoogleScope` — function · src/manifest.ts
673
+
674
+ One declared Google scope as Google's full URL, `"implicit"` for openid/email/profile, or null when it is not a scope an app may declare. `gmail.modify` and `https://www.googleapis.com/auth/gmail.modify` are the same scope.
675
+
676
+ ```ts
677
+ function normaliseGoogleScope(scope: string): string | "implicit" | null
678
+ ```
679
+
632
680
  #### `OpInputError` — class · src/ops.ts
633
681
 
634
682
  What a handler throws when the call does not fit the declared input. The platform's op routes answer it as `invalid` (400) — the same code the door uses for a malformed call — so a tool relays "add-product does not take imageUrl" rather than a 502 with a zod dump in it.
@@ -746,7 +794,47 @@ class PluginCallError extends Error { … }
746
794
  The zod schema one entry of a manifest's `connections` must satisfy.
747
795
 
748
796
  ```ts
749
- const PluginConnectionSchema: z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind: z.ZodEnum<["oauth2", "apiKey"]>; label: z.ZodString; description: z.ZodOptional<z.ZodString>; authorizeUrl: z.ZodOptional<z.ZodString>; tokenUrl: z.ZodOptional<z.ZodString>; oauthScopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; clientIdEnv: z.ZodOptional<z.ZodString>; clientSecretEnv: z.ZodOptional<z.ZodString>; headerNames: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>
797
+ const PluginConnectionSchema: z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind: z.ZodEnum<["oauth2", "apiKey"]>; label: z.ZodString; description: z.ZodOptional<z.ZodString>; authorizeUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>; tokenUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>; oauthScopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; clientIdEnv: z.ZodOptional<z.ZodString>; clientSecretEnv: z.ZodOptional<z.ZodString>; headerNames: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>
798
+ ```
799
+
800
+ #### `PluginCredentialSlotSchema` — const · src/manifest.ts
801
+
802
+ One slot of a manifest's `credentials` block.
803
+
804
+ ```ts
805
+ const PluginCredentialSlotSchema: z.ZodObject<{ family: z.ZodLiteral<"google">; scopes: z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>; label: z.ZodOptional<z.ZodString>; why: z.ZodString; background: z.ZodOptional<z.ZodBoolean>; }, "strict", z.ZodTypeAny, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>
806
+ ```
807
+
808
+ #### `PluginCredentialsSchema` — const · src/manifest.ts
809
+
810
+ The `credentials` block: slot name → slot. At most ONE slot per app for now.
811
+
812
+ ```ts
813
+ const PluginCredentialsSchema: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodObject<{ family: z.ZodLiteral<"google">; scopes: z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>; label: z.ZodOptional<z.ZodString>; why: z.ZodString; background: z.ZodOptional<z.ZodBoolean>; }, "strict", z.ZodTypeAny, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>>, Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>, Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>>
814
+ ```
815
+
816
+ #### `pluginEndpointProblem` — function · src/manifest.ts
817
+
818
+ Why `url` may not be an oauth2 endpoint, or null when it may.
819
+
820
+ ```ts
821
+ function pluginEndpointProblem(url: string): string | null
822
+ ```
823
+
824
+ #### `pluginEnvNameProblem` — function · src/manifest.ts
825
+
826
+ Why app `pluginId` may not read the env var `name`, or null when it may.
827
+
828
+ ```ts
829
+ function pluginEnvNameProblem(pluginId: string, name: string): string | null
830
+ ```
831
+
832
+ #### `pluginEnvPrefix` — function · src/manifest.ts
833
+
834
+ WHOSE ENVIRONMENT VARIABLE IS THIS.
835
+
836
+ ```ts
837
+ function pluginEnvPrefix(pluginId: string): string
750
838
  ```
751
839
 
752
840
  #### `PluginManifestSchema` — const · src/manifest.ts
@@ -754,7 +842,7 @@ const PluginConnectionSchema: z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind:
754
842
  The zod schema `plugin.json` must satisfy — the sync and `check_app` refuse a manifest that fails it, with the path.
755
843
 
756
844
  ```ts
757
- const PluginManifestSchema: z.ZodObject<{ manifestVersion: z.ZodLiteral<1>; id: z.ZodString; name: z.ZodString; version: z.ZodString; description: z.ZodString; applicationType: z.ZodString; entry: z.ZodString; icon: z.ZodOptional<z.ZodString>; author: z.ZodOptional<z.ZodObject<{ name: z.ZodString; email: z.ZodOptional<z.ZodString>; url: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { name?: string; email?: string; url?: string; }, { name?: string; email?: string; url?: string; }>>; kickableTasks: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; webhooks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; ops: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; routes: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; pollTasks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ task: z.ZodString; everyMinutes: z.ZodNumber; }, "strip", z.ZodTypeAny, { task?: string; everyMinutes?: number; }, { task?: string; everyMinutes?: number; }>, "many">>>; platformApi: z.ZodOptional<z.ZodObject<{ min: z.ZodString; max: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { min?: string; max?: string; }, { min?: string; max?: string; }>>; scopes: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; roles: z.ZodOptional<z.ZodEffects<z.ZodObject<{ vocabulary: z.ZodArray<z.ZodString, "many">; default: z.ZodDefault<z.ZodOptional<z.ZodObject<{ owner: z.ZodOptional<z.ZodString>; "member-edit": z.ZodOptional<z.ZodString>; "member-readonly": z.ZodOptional<z.ZodString>; visitor: z.ZodOptional<z.ZodString>; anonymous: z.ZodOptional<z.ZodString>; agent: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }>>>; describe: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["string", "int", "boolean"]>>>; custom: z.ZodOptional<z.ZodObject<{ attributes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ where: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; hide: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; update: z.ZodOptional<z.ZodObject<{ fields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; transitions: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { fields?: string[]; transitions?: string; }, { fields?: string[]; transitions?: string; }>>; }, "strict", z.ZodTypeAny, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>>>; ops: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }>>; }, "strict", z.ZodTypeAny, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>>; workspaceTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; device: z.ZodOptional<z.ZodEffects<z.ZodObject<{ commands: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{ argv: z.ZodArray<z.ZodString, "many">; params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodEnum<["int", "number", "bool", "string"]>, z.ZodObject<{ type: z.ZodEnum<["int", "number"]>; min: z.ZodOptional<z.ZodNumber>; max: z.ZodOptional<z.ZodNumber>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "number" | "int"; min?: number; max?: number; describe?: string; }, { type?: "number" | "int"; min?: number; max?: number; describe?: string; }>, z.ZodObject<{ type: z.ZodLiteral<"string">; pattern: z.ZodOptional<z.ZodString>; maxLength: z.ZodOptional<z.ZodNumber>; allowDash: z.ZodOptional<z.ZodBoolean>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; }, { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; }>, z.ZodObject<{ type: z.ZodLiteral<"enum">; values: z.ZodArray<z.ZodString, "many">; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { values?: string[]; type?: "enum"; describe?: string; }, { values?: string[]; type?: "enum"; describe?: string; }>, z.ZodObject<{ type: z.ZodLiteral<"bool">; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "bool"; describe?: string; }, { type?: "bool"; describe?: string; }>]>>>; cwd: z.ZodOptional<z.ZodString>; env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; config: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; timeoutSeconds: z.ZodOptional<z.ZodNumber>; onRestart: z.ZodOptional<z.ZodEnum<["report", "rerun"]>>; output: z.ZodOptional<z.ZodObject<{ mode: z.ZodOptional<z.ZodEnum<["live", "batched", "final"]>>; flushMs: z.ZodOptional<z.ZodNumber>; maxChunkKb: z.ZodOptional<z.ZodNumber>; keep: z.ZodOptional<z.ZodEnum<["head", "tail", "both"]>>; maxKb: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }, { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }>>; result: z.ZodOptional<z.ZodEnum<["text", "json"]>>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>>>; program: z.ZodOptional<z.ZodObject<{ main: z.ZodString; resident: z.ZodOptional<z.ZodBoolean>; setup: z.ZodOptional<z.ZodArray<z.ZodObject<{ id: z.ZodString; describe: z.ZodString; run: z.ZodArray<z.ZodString, "many">; inputs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; timeoutSeconds: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }, { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }>, "many">>; selfTest: z.ZodOptional<z.ZodObject<{ run: z.ZodArray<z.ZodString, "many">; timeoutSeconds: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { timeoutSeconds?: number; run?: string[]; }, { timeoutSeconds?: number; run?: string[]; }>>; }, "strict", z.ZodTypeAny, { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }, { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }>>; config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ secret: z.ZodOptional<z.ZodBoolean>; required: z.ZodOptional<z.ZodBoolean>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { required?: boolean; describe?: string; secret?: boolean; }, { required?: boolean; describe?: string; secret?: boolean; }>>>; redact: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; onAppDeleted: z.ZodOptional<z.ZodEnum<["kill", "finish"]>>; }, "strict", z.ZodTypeAny, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }>, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }>>; connections: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind: z.ZodEnum<["oauth2", "apiKey"]>; label: z.ZodString; description: z.ZodOptional<z.ZodString>; authorizeUrl: z.ZodOptional<z.ZodString>; tokenUrl: z.ZodOptional<z.ZodString>; oauthScopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; clientIdEnv: z.ZodOptional<z.ZodString>; clientSecretEnv: z.ZodOptional<z.ZodString>; headerNames: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, "many">>>; fileSources: z.ZodOptional<z.ZodEffects<z.ZodObject<{ workspace: z.ZodOptional<z.ZodEnum<["read", "readwrite"]>>; providers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; write: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>>; fileProviders: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ key: z.ZodString; connectionKey: z.ZodString; label: z.ZodString; }, "strict", z.ZodTypeAny, { key?: string; label?: string; connectionKey?: string; }, { key?: string; label?: string; connectionKey?: string; }>, "many">>>; uses: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ contract: z.ZodString; label: z.ZodOptional<z.ZodString>; optional: z.ZodOptional<z.ZodBoolean>; }, "strict", z.ZodTypeAny, { label?: string; contract?: string; optional?: boolean; }, { label?: string; contract?: string; optional?: boolean; }>>>; provides: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; events: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { models?: string[]; tools?: string[]; events?: string[]; }, { models?: string[]; tools?: string[]; events?: string[]; }>>>; channel: z.ZodOptional<z.ZodObject<{ topics: z.ZodRecord<z.ZodString, z.ZodObject<{ audience: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"all">, z.ZodLiteral<"viewer">, z.ZodString]>>; mayAddress: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; description: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { description?: string; audience?: string; mayAddress?: string[]; }, { description?: string; audience?: string; mayAddress?: string[]; }>>; }, "strict", z.ZodTypeAny, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }>>; db: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ scope: z.ZodOptional<z.ZodEnum<["instance", "workspace", "user"]>>; owner: z.ZodOptional<z.ZodLiteral<"creator">>; fields: z.ZodRecord<z.ZodString, z.ZodString>; sealed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; unique: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>; indexes: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodObject<{ fields: z.ZodArray<z.ZodString, "many">; kind: z.ZodOptional<z.ZodEnum<["btree", "contains", "text"]>>; }, "strict", z.ZodTypeAny, { kind?: "text" | "btree" | "contains"; fields?: string[]; }, { kind?: "text" | "btree" | "contains"; fields?: string[]; }>]>, "many">>; rules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; }, "strict", z.ZodTypeAny, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>>>; }, "strict", z.ZodTypeAny, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { name?: string; email?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; scopes?: string[]; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { name?: string; email?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; scopes?: string[]; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }>
845
+ const PluginManifestSchema: z.ZodEffects<z.ZodObject<{ manifestVersion: z.ZodLiteral<1>; id: z.ZodString; name: z.ZodString; version: z.ZodString; description: z.ZodString; applicationType: z.ZodString; entry: z.ZodString; icon: z.ZodOptional<z.ZodString>; author: z.ZodOptional<z.ZodObject<{ name: z.ZodString; email: z.ZodOptional<z.ZodString>; url: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { email?: string; name?: string; url?: string; }, { email?: string; name?: string; url?: string; }>>; kickableTasks: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; webhooks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; ops: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; routes: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; pollTasks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ task: z.ZodString; everyMinutes: z.ZodNumber; }, "strip", z.ZodTypeAny, { task?: string; everyMinutes?: number; }, { task?: string; everyMinutes?: number; }>, "many">>>; platformApi: z.ZodOptional<z.ZodObject<{ min: z.ZodString; max: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { min?: string; max?: string; }, { min?: string; max?: string; }>>; scopes: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; roles: z.ZodOptional<z.ZodEffects<z.ZodObject<{ vocabulary: z.ZodArray<z.ZodString, "many">; default: z.ZodDefault<z.ZodOptional<z.ZodObject<{ owner: z.ZodOptional<z.ZodString>; "member-edit": z.ZodOptional<z.ZodString>; "member-readonly": z.ZodOptional<z.ZodString>; visitor: z.ZodOptional<z.ZodString>; anonymous: z.ZodOptional<z.ZodString>; agent: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }>>>; describe: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["string", "int", "boolean"]>>>; custom: z.ZodOptional<z.ZodObject<{ attributes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ where: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; hide: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; update: z.ZodOptional<z.ZodObject<{ fields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; transitions: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { fields?: string[]; transitions?: string; }, { fields?: string[]; transitions?: string; }>>; }, "strict", z.ZodTypeAny, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>>>; ops: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }>>; }, "strict", z.ZodTypeAny, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>>; workspaceTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; device: z.ZodOptional<z.ZodEffects<z.ZodObject<{ commands: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{ argv: z.ZodArray<z.ZodString, "many">; params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodEnum<["int", "number", "bool", "string"]>, z.ZodObject<{ type: z.ZodEnum<["int", "number"]>; min: z.ZodOptional<z.ZodNumber>; max: z.ZodOptional<z.ZodNumber>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "number" | "int"; min?: number; max?: number; describe?: string; }, { type?: "number" | "int"; min?: number; max?: number; describe?: string; }>, z.ZodObject<{ type: z.ZodLiteral<"string">; pattern: z.ZodOptional<z.ZodString>; maxLength: z.ZodOptional<z.ZodNumber>; allowDash: z.ZodOptional<z.ZodBoolean>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; }, { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; }>, z.ZodObject<{ type: z.ZodLiteral<"enum">; values: z.ZodArray<z.ZodString, "many">; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { values?: string[]; type?: "enum"; describe?: string; }, { values?: string[]; type?: "enum"; describe?: string; }>, z.ZodObject<{ type: z.ZodLiteral<"bool">; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "bool"; describe?: string; }, { type?: "bool"; describe?: string; }>]>>>; cwd: z.ZodOptional<z.ZodString>; env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; config: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; timeoutSeconds: z.ZodOptional<z.ZodNumber>; onRestart: z.ZodOptional<z.ZodEnum<["report", "rerun"]>>; output: z.ZodOptional<z.ZodObject<{ mode: z.ZodOptional<z.ZodEnum<["live", "batched", "final"]>>; flushMs: z.ZodOptional<z.ZodNumber>; maxChunkKb: z.ZodOptional<z.ZodNumber>; keep: z.ZodOptional<z.ZodEnum<["head", "tail", "both"]>>; maxKb: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }, { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }>>; result: z.ZodOptional<z.ZodEnum<["text", "json"]>>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>>>; program: z.ZodOptional<z.ZodObject<{ main: z.ZodString; resident: z.ZodOptional<z.ZodBoolean>; setup: z.ZodOptional<z.ZodArray<z.ZodObject<{ id: z.ZodString; describe: z.ZodString; run: z.ZodArray<z.ZodString, "many">; inputs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; timeoutSeconds: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }, { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }>, "many">>; selfTest: z.ZodOptional<z.ZodObject<{ run: z.ZodArray<z.ZodString, "many">; timeoutSeconds: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { timeoutSeconds?: number; run?: string[]; }, { timeoutSeconds?: number; run?: string[]; }>>; }, "strict", z.ZodTypeAny, { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }, { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }>>; config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ secret: z.ZodOptional<z.ZodBoolean>; required: z.ZodOptional<z.ZodBoolean>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { required?: boolean; describe?: string; secret?: boolean; }, { required?: boolean; describe?: string; secret?: boolean; }>>>; redact: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; onAppDeleted: z.ZodOptional<z.ZodEnum<["kill", "finish"]>>; }, "strict", z.ZodTypeAny, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }>, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }>>; connections: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind: z.ZodEnum<["oauth2", "apiKey"]>; label: z.ZodString; description: z.ZodOptional<z.ZodString>; authorizeUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>; tokenUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>; oauthScopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; clientIdEnv: z.ZodOptional<z.ZodString>; clientSecretEnv: z.ZodOptional<z.ZodString>; headerNames: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, "many">>>; credentials: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodObject<{ family: z.ZodLiteral<"google">; scopes: z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>; label: z.ZodOptional<z.ZodString>; why: z.ZodString; background: z.ZodOptional<z.ZodBoolean>; }, "strict", z.ZodTypeAny, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>>, Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>, Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>>>; fileSources: z.ZodOptional<z.ZodEffects<z.ZodObject<{ workspace: z.ZodOptional<z.ZodEnum<["read", "readwrite"]>>; providers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; write: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>>; fileProviders: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ key: z.ZodString; connectionKey: z.ZodString; label: z.ZodString; }, "strict", z.ZodTypeAny, { key?: string; label?: string; connectionKey?: string; }, { key?: string; label?: string; connectionKey?: string; }>, "many">>>; uses: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ contract: z.ZodString; label: z.ZodOptional<z.ZodString>; optional: z.ZodOptional<z.ZodBoolean>; }, "strict", z.ZodTypeAny, { label?: string; contract?: string; optional?: boolean; }, { label?: string; contract?: string; optional?: boolean; }>>>; provides: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; events: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { models?: string[]; tools?: string[]; events?: string[]; }, { models?: string[]; tools?: string[]; events?: string[]; }>>>; channel: z.ZodOptional<z.ZodObject<{ topics: z.ZodRecord<z.ZodString, z.ZodObject<{ audience: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"all">, z.ZodLiteral<"viewer">, z.ZodString]>>; mayAddress: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; description: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { description?: string; audience?: string; mayAddress?: string[]; }, { description?: string; audience?: string; mayAddress?: string[]; }>>; }, "strict", z.ZodTypeAny, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }>>; db: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ scope: z.ZodOptional<z.ZodEnum<["instance", "workspace", "user"]>>; owner: z.ZodOptional<z.ZodLiteral<"creator">>; fields: z.ZodRecord<z.ZodString, z.ZodString>; sealed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; unique: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>; indexes: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodObject<{ fields: z.ZodArray<z.ZodString, "many">; kind: z.ZodOptional<z.ZodEnum<["btree", "contains", "text"]>>; }, "strict", z.ZodTypeAny, { kind?: "text" | "btree" | "contains"; fields?: string[]; }, { kind?: "text" | "btree" | "contains"; fields?: string[]; }>]>, "many">>; rules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; }, "strict", z.ZodTypeAny, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>>>; }, "strict", z.ZodTypeAny, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; scopes?: string[]; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { email?: string; name?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; credentials?: Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; scopes?: string[]; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { email?: string; name?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; credentials?: Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }>, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; scopes?: string[]; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { email?: string; name?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; credentials?: Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; scopes?: string[]; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { email?: string; name?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; credentials?: Record<string, { label?: string; family?: "google"; scopes?: string[]; why?: string; background?: boolean; }>; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }>
758
846
  ```
759
847
 
760
848
  #### `PluginRolesSchema` — const · src/manifest.ts
@@ -933,7 +1021,7 @@ A manifest with one entry removed (the bytes stay; they may be shared). Pure.
933
1021
  function withoutAsset(assets: AssetsManifest | null | undefined, pluginId: string, name: string): AssetsManifest
934
1022
  ```
935
1023
 
936
- ### Types (81)
1024
+ ### Types (84)
937
1025
 
938
1026
  #### `AgentRunToolContext` — interface · src/types.ts
939
1027
 
@@ -1051,6 +1139,7 @@ interface AppTaskContext<TState extends ApplicationIdentifier> {
1051
1139
  getState(): Promise<TState>;
1052
1140
  dispatchEvent(eventName: string, eventData: any): Promise<void>;
1053
1141
  notify(topic: string, data: any, opts?: { to?: { viewerIds: string[] } | { role: string } }): Promise<void>;
1142
+ credentials(slot: string): CredentialSlot;
1054
1143
  }
1055
1144
  ```
1056
1145
 
@@ -1311,6 +1400,12 @@ interface ContractId {
1311
1400
  }
1312
1401
  ```
1313
1402
 
1403
+ #### `CredentialFamilyName` — type · src/manifest.ts
1404
+
1405
+ ```ts
1406
+ type CredentialFamilyName = (typeof CREDENTIAL_FAMILIES)[number];
1407
+ ```
1408
+
1314
1409
  #### `CustomRoleDefinitionRaw` — interface · src/db/custom-roles.ts
1315
1410
 
1316
1411
  What the owner wrote (the event's data, minus bookkeeping).
@@ -1599,6 +1694,12 @@ interface FileWriteResult {
1599
1694
  }
1600
1695
  ```
1601
1696
 
1697
+ #### `GoogleCredentialScope` — type · src/manifest.ts
1698
+
1699
+ ```ts
1700
+ type GoogleCredentialScope = (typeof GOOGLE_CREDENTIAL_SCOPES)[number];
1701
+ ```
1702
+
1602
1703
  #### `InstalledPluginInfo` — interface · src/manifest.ts
1603
1704
 
1604
1705
  Data-only row the host codegen inlines into registry.gen.ts.
@@ -1760,6 +1861,12 @@ type PluginAccessLevel = (typeof PLUGIN_ACCESS_LEVELS)[number];
1760
1861
  type PluginConnectionDecl = z.infer<typeof PluginConnectionSchema>;
1761
1862
  ```
1762
1863
 
1864
+ #### `PluginCredentialSlot` — type · src/manifest.ts
1865
+
1866
+ ```ts
1867
+ type PluginCredentialSlot = z.infer<typeof PluginCredentialSlotSchema>;
1868
+ ```
1869
+
1763
1870
  #### `PluginManifest` — type · src/manifest.ts
1764
1871
 
1765
1872
  ```ts
@@ -1927,11 +2034,11 @@ interface UsesDecl {
1927
2034
  ```
1928
2035
 
1929
2036
  ==============================================================================
1930
- ## `esoul-sdk/server` — 79 exports
2037
+ ## `esoul-sdk/server` — 86 exports
1931
2038
 
1932
2039
  Server code only (server.ts, ops, routes, tasks): the viewer, the app's database, files, connections, machines, charts, route tokens.
1933
2040
 
1934
- ### Functions and values (29)
2041
+ ### Functions and values (32)
1935
2042
 
1936
2043
  #### `APPROVAL_WAIT` — const · src/computer.ts
1937
2044
 
@@ -1957,6 +2064,22 @@ A paired machine (`my_computer` app) from server code: status, a shell command,
1957
2064
  const computer: (ctx: { pluginId: string; nodeId: string; }, machineNodeId: string) => Computer
1958
2065
  ```
1959
2066
 
2067
+ #### `credentials` — function · src/server.ts
2068
+
2069
+ THE PERSON'S ACCOUNTS, WITHOUT THE TOKEN. Hand it the context your op, route or task was called with — that exact object, not a copy: the platform knows which call it belongs to, who is calling and which app instance is asking, and refuses a context from another call or one whose call has finished.
2070
+
2071
+ ```ts
2072
+ function credentials(_ctx: object): Credentials
2073
+ ```
2074
+
2075
+ #### `CredentialUnavailable` — class · src/server.ts
2076
+
2077
+ A credential call the platform refused. The message is a sentence a tool can relay; it never contains a token. `code` is what a program branches on.
2078
+
2079
+ ```ts
2080
+ class CredentialUnavailable extends Error { … }
2081
+ ```
2082
+
1960
2083
  #### `defineAppRole` — function · src/server.ts
1961
2084
 
1962
2085
  COMPOSE A ROLE, as the owner: a name of its own, a base word from the vocabulary it stands on, and what it takes away — which rows (`where`), which fields (`hide`), which changes (`update.fields`, `update.transitions`), which surfaces it may call (`ops`). Everything must fit the manifest's `roles.custom` envelope; a refusal names the key and the allowed values. Stored on the app's own timeline; `setAppRole` may then hand it to a person.
@@ -2013,6 +2136,14 @@ Read the sealed credentials of a plugin-declared connection (auto- refreshes exp
2013
2136
  function getPluginConnectionCredentials( _connectionId: string, _pluginId: string, ): Promise<PluginConnectionCredentials>
2014
2137
  ```
2015
2138
 
2139
+ #### `isCredentialUnavailable` — function · src/server.ts
2140
+
2141
+ True for a `CredentialUnavailable`, also across bundle boundaries where `instanceof` fails.
2142
+
2143
+ ```ts
2144
+ function isCredentialUnavailable(err: unknown): err is CredentialUnavailable
2145
+ ```
2146
+
2016
2147
  #### `isTerminal` — const · src/computer.ts
2017
2148
 
2018
2149
  Whether a command has finished for good (`done`, `failed`, `denied`) — anything else is worth waiting on.
@@ -2165,7 +2296,7 @@ WHO IS THIS, IN WORDS. The caller's own name and email, for an app with a reason
2165
2296
  function viewerProfile(_viewer: PluginViewer): Promise<ViewerProfile | null>
2166
2297
  ```
2167
2298
 
2168
- ### Types (50)
2299
+ ### Types (54)
2169
2300
 
2170
2301
  #### `AppRolePerson` — interface · src/server.ts
2171
2302
 
@@ -2354,6 +2485,54 @@ interface ComputerStatus {
2354
2485
  }
2355
2486
  ```
2356
2487
 
2488
+ #### `Credentials` — interface · src/server.ts
2489
+
2490
+ ```ts
2491
+ interface Credentials {
2492
+ slot(name: string): CredentialSlot;
2493
+ }
2494
+ ```
2495
+
2496
+ #### `CredentialSlot` — interface · src/server.ts
2497
+
2498
+ One declared slot, bound to the call it was asked from.
2499
+
2500
+ ```ts
2501
+ interface CredentialSlot {
2502
+ status(): Promise<CredentialStatus>;
2503
+ fetch(url: string | URL, init?: RequestInit): Promise<Response>;
2504
+ }
2505
+ ```
2506
+
2507
+ #### `CredentialStatus` — interface · src/server.ts
2508
+
2509
+ What `status()` answers. `account` is present only when the caller is the account's owner.
2510
+
2511
+ ```ts
2512
+ interface CredentialStatus {
2513
+ state: "ready" | "not_bound" | "needs_consent" | "reconnect" | "not_allowed";
2514
+ account?: { email: string | null };
2515
+ missingScopes?: string[];
2516
+ connectUrl?: string;
2517
+ reason?: string;
2518
+ }
2519
+ ```
2520
+
2521
+ #### `CredentialUnavailableCode` — type · src/server.ts
2522
+
2523
+ Why a credential call was refused.
2524
+
2525
+ ```ts
2526
+ type CredentialUnavailableCode =
2527
+ | "not_bound"
2528
+ | "needs_consent"
2529
+ | "reconnect"
2530
+ | "not_allowed"
2531
+ | "not_declared"
2532
+ | "rate_limited"
2533
+ | "provider_unavailable";
2534
+ ```
2535
+
2357
2536
  #### `CustomRoleDefinitionRaw` — interface · src/db/custom-roles.ts
2358
2537
 
2359
2538
  What the owner wrote (the event's data, minus bookkeeping).
@@ -2865,11 +3044,19 @@ type WorkspaceGrant = { scope: "folder"; root: string } | { scope: "computer" };
2865
3044
  ```
2866
3045
 
2867
3046
  ==============================================================================
2868
- ## `esoul-sdk/react` — 64 exports
3047
+ ## `esoul-sdk/react` — 67 exports
2869
3048
 
2870
3049
  The app's UI: hooks for the viewer, the app's state, realtime, workspace files and tools.
2871
3050
 
2872
- ### Functions and values (33)
3051
+ ### Functions and values (35)
3052
+
3053
+ #### `ConnectAccount` — function · src/react.ts
3054
+
3055
+ A plain line and one button for a credential slot: connect, grant, reconnect or change.
3056
+
3057
+ ```ts
3058
+ function ConnectAccount(_props: { slot: string; nodeId: string; className?: string }): any
3059
+ ```
2873
3060
 
2874
3061
  #### `ConnectComputer` — function · src/react.ts
2875
3062
 
@@ -2983,6 +3170,14 @@ True when the current viewer may mutate this workspace.
2983
3170
  function useAppCanEdit(): boolean
2984
3171
  ```
2985
3172
 
3173
+ #### `useCredential` — function · src/react.ts
3174
+
3175
+ The state of one of your plugin.json `credentials` slots for the person looking at the app.
3176
+
3177
+ ```ts
3178
+ function useCredential(_slot: string, _where: { nodeId: string }): UseCredential
3179
+ ```
3180
+
2986
3181
  #### `useDeviceActions` — function · src/react.ts
2987
3182
 
2988
3183
  The owner's buttons from the UI: run a declared command, stop a run, disconnect a computer, approve an update's new commands. Needs edit access.
@@ -3135,7 +3330,7 @@ The account's computers as a panel: online, the apps each serves, Disconnect.
3135
3330
  function YourComputers(_props: { className?: string }): any
3136
3331
  ```
3137
3332
 
3138
- ### Types (31)
3333
+ ### Types (32)
3139
3334
 
3140
3335
  #### `DeviceRunStarted` — interface · src/device.ts
3141
3336
 
@@ -3504,6 +3699,21 @@ interface SignInWall {
3504
3699
  }
3505
3700
  ```
3506
3701
 
3702
+ #### `UseCredential` — interface · src/react.ts
3703
+
3704
+ What `useCredential` answers — the same state the app's server code gets from `status()`.
3705
+
3706
+ ```ts
3707
+ interface UseCredential {
3708
+ state: "ready" | "not_bound" | "needs_consent" | "reconnect" | "not_allowed" | "loading" | "error";
3709
+ account?: { email: string | null };
3710
+ missingScopes?: string[];
3711
+ reason?: string;
3712
+ connect(): void;
3713
+ refresh(): Promise<void>;
3714
+ }
3715
+ ```
3716
+
3507
3717
  #### `WorkspaceAppSummary` — interface · src/react.ts
3508
3718
 
3509
3719
  ```ts
@@ -3563,11 +3773,11 @@ interface WorkspaceTools {
3563
3773
  ```
3564
3774
 
3565
3775
  ==============================================================================
3566
- ## `esoul-sdk/testing` — 21 exports
3776
+ ## `esoul-sdk/testing` — 48 exports
3567
3777
 
3568
3778
  Tests: run an op as a persona, an in-memory database with the real rules, recorders.
3569
3779
 
3570
- ### Functions and values (8)
3780
+ ### Functions and values (11)
3571
3781
 
3572
3782
  #### `capture` — function · src/testing/ops.ts
3573
3783
 
@@ -3577,6 +3787,22 @@ A call recorder, for anything `runOp` does not already capture.
3577
3787
  function capture<A extends unknown[] = unknown[]>(): Recorder<A>
3578
3788
  ```
3579
3789
 
3790
+ #### `DEFAULT_CROWD_MIX` — const · src/testing/sim-gmail/personas.ts
3791
+
3792
+ The behaviour mix `crowd(n)` draws from when none is given — a cold campaign: 20 % reply in some form, 3 % out of office, 3 % bounce, 1 % opt out, the rest silent.
3793
+
3794
+ ```ts
3795
+ const DEFAULT_CROWD_MIX: Required<CrowdMix>
3796
+ ```
3797
+
3798
+ #### `DEFAULT_SIM_ACCOUNT` — const · src/testing/sim-gmail/index.ts
3799
+
3800
+ The account a sim has when `accounts` is not given (RFC 2606 reserved domain — never a real address).
3801
+
3802
+ ```ts
3803
+ const DEFAULT_SIM_ACCOUNT: "owner@sim.mail.example"
3804
+ ```
3805
+
3580
3806
  #### `fakeApps` — function · src/testing/ops.ts
3581
3807
 
3582
3808
  Fill a `uses` slot with a stand-in provider, so a consumer's op can be tested without the other app existing. Calls to a tool the fixture does not define come back as a refusal, which is what an unbound slot really does.
@@ -3625,6 +3851,14 @@ Call `pluginServer.ops[name]` with a context shaped like the platform's. Throws
3625
3851
  async function runOp<T = unknown>( server: ServerModuleLike, opName: string, options: RunOpOptions, ): Promise<RunOpResult<T>>
3626
3852
  ```
3627
3853
 
3854
+ #### `simGmail` — function · src/testing/sim-gmail/index.ts
3855
+
3856
+ A fake Gmail at the HTTP level. `sim.fetch` answers the Gmail REST endpoints the platform's mail code calls (profile, messages list/get/send/modify/trash/delete/attachments, threads, labels, history, watch/stop) and Google's OAuth token endpoints, with Gmail's field names, types and units; mailboxes deliver to each other and thread like Gmail; `addPersona`/`crowd` script counterparties; `faults` injects 429/5xx/401/403/network errors deterministically; `corpus` seeds a production-shaped synthetic mailbox; `snapshot()` / `{ restore }` persist the world. No Node built-ins.
3857
+
3858
+ ```ts
3859
+ function simGmail(opts: SimGmailOptions = {}): SimGmail
3860
+ ```
3861
+
3628
3862
  #### `startMockOAuth` — function · src/testing/index.ts
3629
3863
 
3630
3864
  A real (tiny) OAuth2 provider for exercising the plugin-connection flow: /authorize (PKCE challenge bound to a one-time code), /token (authorization_code with verifier check + refresh_token grant), and a Bearer-gated /api/items resource. Access tokens expire fast (default 20s) so refresh paths are exercised without waiting.
@@ -3633,7 +3867,7 @@ A real (tiny) OAuth2 provider for exercising the plugin-connection flow: /author
3633
3867
  function startMockOAuth(opts?: { port?: number; clientId?: string; accessTtlMs?: number; }): Promise<MockOAuthServer>
3634
3868
  ```
3635
3869
 
3636
- ### Types (13)
3870
+ ### Types (37)
3637
3871
 
3638
3872
  #### `BoundAppFixture` — interface · src/testing/ops.ts
3639
3873
 
@@ -3647,6 +3881,92 @@ interface BoundAppFixture {
3647
3881
  }
3648
3882
  ```
3649
3883
 
3884
+ #### `CorpusSpec` — interface · src/testing/sim-gmail/types.ts
3885
+
3886
+ ```ts
3887
+ interface CorpusSpec {
3888
+ messages: number;
3889
+ seed?: number;
3890
+ days?: number;
3891
+ preset?: "owner-like" | "quiet";
3892
+ }
3893
+ ```
3894
+
3895
+ #### `CrowdBehaviour` — type · src/testing/sim-gmail/types.ts
3896
+
3897
+ ```ts
3898
+ type CrowdBehaviour = keyof CrowdMix;
3899
+ ```
3900
+
3901
+ #### `CrowdMember` — interface · src/testing/sim-gmail/types.ts
3902
+
3903
+ ```ts
3904
+ interface CrowdMember {
3905
+ address: string;
3906
+ name: string;
3907
+ behaviour: CrowdBehaviour;
3908
+ latencyMs: number;
3909
+ }
3910
+ ```
3911
+
3912
+ #### `CrowdMix` — interface · src/testing/sim-gmail/types.ts
3913
+
3914
+ ```ts
3915
+ interface CrowdMix {
3916
+ reply?: number;
3917
+ twoReplies?: number;
3918
+ replyAll?: number;
3919
+ subjectChange?: number;
3920
+ attachment?: number;
3921
+ ooo?: number;
3922
+ optout?: number;
3923
+ bounce?: number;
3924
+ softBounce?: number;
3925
+ silent?: number;
3926
+ }
3927
+ ```
3928
+
3929
+ #### `CrowdOptions` — interface · src/testing/sim-gmail/types.ts
3930
+
3931
+ ```ts
3932
+ interface CrowdOptions {
3933
+ domain?: string;
3934
+ latencyMs?: number;
3935
+ raceShare?: number;
3936
+ prefix?: string;
3937
+ }
3938
+ ```
3939
+
3940
+ #### `DeliverResult` — interface · src/testing/sim-gmail/types.ts
3941
+
3942
+ ```ts
3943
+ interface DeliverResult {
3944
+ messageIdHeader: string;
3945
+ id: string;
3946
+ threadId: string;
3947
+ copies: Array<{ address: string; id: string; threadId: string }>;
3948
+ }
3949
+ ```
3950
+
3951
+ #### `DeliverSpec` — interface · src/testing/sim-gmail/types.ts
3952
+
3953
+ ```ts
3954
+ interface DeliverSpec {
3955
+ from: string;
3956
+ to: string | string[];
3957
+ cc?: string | string[];
3958
+ subject: string;
3959
+ text: string;
3960
+ html?: string;
3961
+ attachments?: Array<{ filename: string; mimeType?: string; content: string | Uint8Array }>;
3962
+ inReplyTo?: string;
3963
+ references?: string[];
3964
+ headers?: Record<string, string>;
3965
+ labelIds?: string[];
3966
+ messageId?: string;
3967
+ }
3968
+ ```
3969
+
3650
3970
  #### `EmitCall` — interface · src/testing/ops.ts
3651
3971
 
3652
3972
  ```ts
@@ -3656,6 +3976,25 @@ interface EmitCall {
3656
3976
  }
3657
3977
  ```
3658
3978
 
3979
+ #### `FaultRule` — interface · src/testing/sim-gmail/faults.ts
3980
+
3981
+ ```ts
3982
+ interface FaultRule {
3983
+ endpoint?: SimEndpoint | SimEndpoint[];
3984
+ account?: string;
3985
+ pathIncludes?: string;
3986
+ status: number | "network";
3987
+ reason?: string;
3988
+ message?: string;
3989
+ retryAfter?: number;
3990
+ times?: number;
3991
+ skip?: number;
3992
+ rate?: number;
3993
+ afterCommit?: boolean;
3994
+ label?: string;
3995
+ }
3996
+ ```
3997
+
3659
3998
  #### `MemoryDbHandle` — interface · src/testing/db.ts
3660
3999
 
3661
4000
  ```ts
@@ -3719,6 +4058,69 @@ interface NotifyCall {
3719
4058
  }
3720
4059
  ```
3721
4060
 
4061
+ #### `PersonaAttachment` — interface · src/testing/sim-gmail/types.ts
4062
+
4063
+ ```ts
4064
+ interface PersonaAttachment {
4065
+ filename: string;
4066
+ mimeType?: string;
4067
+ content: string | Uint8Array;
4068
+ }
4069
+ ```
4070
+
4071
+ #### `PersonaInbound` — interface · src/testing/sim-gmail/types.ts
4072
+
4073
+ What a persona sees of a message it received.
4074
+
4075
+ ```ts
4076
+ interface PersonaInbound {
4077
+ id: string;
4078
+ threadId: string;
4079
+ from: string;
4080
+ fromAddress: string;
4081
+ to: string[];
4082
+ cc: string[];
4083
+ subject: string;
4084
+ text: string;
4085
+ html: string;
4086
+ messageIdHeader: string;
4087
+ references: string[];
4088
+ at: number;
4089
+ count: number;
4090
+ }
4091
+ ```
4092
+
4093
+ #### `PersonaKind` — type · src/testing/sim-gmail/types.ts
4094
+
4095
+ ```ts
4096
+ type PersonaKind = "reply" | "ooo" | "bounce" | "optout" | "silent";
4097
+ ```
4098
+
4099
+ #### `PersonaScript` — type · src/testing/sim-gmail/types.ts
4100
+
4101
+ ```ts
4102
+ type PersonaScript = PersonaStep[];
4103
+ ```
4104
+
4105
+ #### `PersonaStep` — interface · src/testing/sim-gmail/types.ts
4106
+
4107
+ ```ts
4108
+ interface PersonaStep {
4109
+ match?: RegExp | ((msg: PersonaInbound) => boolean);
4110
+ reply?: string | ((msg: PersonaInbound) => string);
4111
+ html?: string | ((msg: PersonaInbound) => string);
4112
+ latencyMs?: number;
4113
+ attachment?: PersonaAttachment | PersonaAttachment[];
4114
+ kind?: PersonaKind;
4115
+ soft?: boolean;
4116
+ replyAll?: boolean;
4117
+ cc?: string[];
4118
+ subject?: string;
4119
+ repeat?: boolean;
4120
+ also?: PersonaStep[];
4121
+ }
4122
+ ```
4123
+
3722
4124
  #### `Recorder` — interface · src/testing/ops.ts
3723
4125
 
3724
4126
  ```ts
@@ -3755,6 +4157,248 @@ interface RunOpResult<T = unknown> {
3755
4157
  }
3756
4158
  ```
3757
4159
 
4160
+ #### `SimAccountSpec` — interface · src/testing/sim-gmail/types.ts
4161
+
4162
+ One simulated Google account = one Gmail mailbox.
4163
+
4164
+ ```ts
4165
+ interface SimAccountSpec {
4166
+ address: string;
4167
+ name?: string;
4168
+ corpus?: CorpusSpec | false;
4169
+ scopes?: string[];
4170
+ }
4171
+ ```
4172
+
4173
+ #### `SimCall` — interface · src/testing/sim-gmail/types.ts
4174
+
4175
+ ```ts
4176
+ interface SimCall {
4177
+ at: number;
4178
+ method: string;
4179
+ url: string;
4180
+ endpoint: SimEndpoint;
4181
+ account: string | null;
4182
+ status: number;
4183
+ fault?: string;
4184
+ }
4185
+ ```
4186
+
4187
+ #### `SimEndpoint` — type · src/testing/sim-gmail/types.ts
4188
+
4189
+ Names SimGmail uses for each endpoint — in faults, the call log and quota.
4190
+
4191
+ ```ts
4192
+ type SimEndpoint =
4193
+ | "users.getProfile"
4194
+ | "messages.list"
4195
+ | "messages.get"
4196
+ | "messages.send"
4197
+ | "messages.modify"
4198
+ | "messages.batchModify"
4199
+ | "messages.batchDelete"
4200
+ | "messages.trash"
4201
+ | "messages.untrash"
4202
+ | "messages.delete"
4203
+ | "messages.attachments.get"
4204
+ | "threads.list"
4205
+ | "threads.get"
4206
+ | "threads.modify"
4207
+ | "threads.trash"
4208
+ | "threads.untrash"
4209
+ | "threads.delete"
4210
+ | "labels.list"
4211
+ | "labels.get"
4212
+ | "labels.create"
4213
+ | "history.list"
4214
+ | "users.watch"
4215
+ | "users.stop"
4216
+ | "oauth.authorize"
4217
+ | "oauth.token"
4218
+ | "oauth.tokeninfo"
4219
+ | "oauth.revoke"
4220
+ | "unmodelled";
4221
+ ```
4222
+
4223
+ #### `SimGmail` — interface · src/testing/sim-gmail/index.ts
4224
+
4225
+ ```ts
4226
+ interface SimGmail {
4227
+ fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
4228
+ install(): () => void;
4229
+ tokenFor(address: string, opts?: { ttlMs?: number; scopes?: string[] }): string;
4230
+ refreshTokenFor(address: string): string;
4231
+ revokeTokens(address: string): void;
4232
+ expireTokens(address: string): void;
4233
+ addAccount(spec: SimAccountSpec): SimMailboxView;
4234
+ mailbox(address: string): SimMailboxView;
4235
+ readonly world: {
4236
+ mailboxes(): SimMailboxView[];
4237
+ readonly outbox: SimOutboxEntry[];
4238
+ readonly sent: SimSentEntry[];
4239
+ };
4240
+ deliver(spec: DeliverSpec): DeliverResult;
4241
+ userAction(address: string, action: "read" | "unread" | "archive" | "unarchive" | "star" | "unstar" | "spam" | "trash" | "untrash" | "delete", messageId: string): void;
4242
+ saveDraft(address: string, spec: { to: string; subject: string; text: string; inReplyTo?: string; threadId?: string }, replacing?: string): string;
4243
+ addPersona(address: string, script: PersonaScript, opts?: { name?: string }): void;
4244
+ crowd(n: number, mix?: CrowdMix, opts?: CrowdOptions): CrowdMember[];
4245
+ advance(ms: number): number;
4246
+ flush(): number;
4247
+ now(): number;
4248
+ pending(): Array<{ dueAt: number; kind: string; from?: string; to?: string; via: string }>;
4249
+ expireHistory(address?: string): void;
4250
+ readonly faults: Faults;
4251
+ readonly outbox: SimOutboxEntry[];
4252
+ readonly sent: SimSentEntry[];
4253
+ readonly calls: SimCall[];
4254
+ readonly pushes: SimPush[];
4255
+ quotaUsed(address: string): number;
4256
+ snapshot(): SimSnapshot;
4257
+ }
4258
+ ```
4259
+
4260
+ #### `SimGmailOptions` — interface · src/testing/sim-gmail/types.ts
4261
+
4262
+ ```ts
4263
+ interface SimGmailOptions {
4264
+ seed?: number;
4265
+ now?: () => number;
4266
+ accounts?: SimAccountSpec[];
4267
+ corpus?: CorpusSpec | false;
4268
+ restore?: SimSnapshot;
4269
+ history?: {
4270
+ retentionMs?: number;
4271
+ pageSize?: number;
4272
+ };
4273
+ metadataParts?: boolean;
4274
+ rejectMetadataAttachmentIds?: boolean;
4275
+ googleClockOffsetMs?: number;
4276
+ accessTokenTtlMs?: number;
4277
+ keepMessageId?: boolean;
4278
+ limits?: {
4279
+ sendsPerDay?: number;
4280
+ sendLockMode?: "http" | "dsn";
4281
+ };
4282
+ quota?: { unitsPerSecond?: number };
4283
+ onPush?: (n: SimPush) => void;
4284
+ passthrough?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
4285
+ subjectPrefixes?: string[];
4286
+ }
4287
+ ```
4288
+
4289
+ #### `SimMailboxView` — interface · src/testing/sim-gmail/index.ts
4290
+
4291
+ ```ts
4292
+ interface SimMailboxView {
4293
+ address: string;
4294
+ name: string;
4295
+ persona: boolean;
4296
+ historyId: string;
4297
+ messages(): SimMessageView[];
4298
+ message(id: string): SimMessageView | undefined;
4299
+ thread(threadId: string): SimMessageView[];
4300
+ threadIds(): string[];
4301
+ withLabels(...labelIds: string[]): SimMessageView[];
4302
+ labels(): LabelDef[];
4303
+ history(): HistoryRecord[];
4304
+ watch(): { topicName: string; labelIds: string[]; expiration: number } | null;
4305
+ }
4306
+ ```
4307
+
4308
+ #### `SimMessageView` — interface · src/testing/sim-gmail/index.ts
4309
+
4310
+ A read-only view of one message, for assertions.
4311
+
4312
+ ```ts
4313
+ interface SimMessageView {
4314
+ id: string;
4315
+ threadId: string;
4316
+ labelIds: string[];
4317
+ internalDate: number;
4318
+ subject: string;
4319
+ from: string;
4320
+ to: string;
4321
+ cc: string;
4322
+ messageIdHeader: string;
4323
+ inReplyTo: string;
4324
+ references: string[];
4325
+ text(): string;
4326
+ html(): string;
4327
+ raw(): string;
4328
+ header(name: string): string | undefined;
4329
+ attachments(): Array<{ filename: string; mimeType: string; size: number; bytes: Uint8Array }>;
4330
+ }
4331
+ ```
4332
+
4333
+ #### `SimOutboxEntry` — interface · src/testing/sim-gmail/types.ts
4334
+
4335
+ A message a simulated mailbox sent to an address SimGmail does not host.
4336
+
4337
+ ```ts
4338
+ interface SimOutboxEntry {
4339
+ at: number;
4340
+ from: string;
4341
+ to: string;
4342
+ subject: string;
4343
+ messageIdHeader: string;
4344
+ id: string;
4345
+ threadId: string;
4346
+ text: string;
4347
+ html: string;
4348
+ raw: string;
4349
+ }
4350
+ ```
4351
+
4352
+ #### `SimPush` — interface · src/testing/sim-gmail/types.ts
4353
+
4354
+ ```ts
4355
+ interface SimPush {
4356
+ emailAddress: string;
4357
+ historyId: string;
4358
+ at: number;
4359
+ }
4360
+ ```
4361
+
4362
+ #### `SimSentEntry` — interface · src/testing/sim-gmail/types.ts
4363
+
4364
+ Every accepted `messages.send` (and persona reply), in order.
4365
+
4366
+ ```ts
4367
+ interface SimSentEntry {
4368
+ at: number;
4369
+ from: string;
4370
+ to: string[];
4371
+ cc: string[];
4372
+ bcc: string[];
4373
+ subject: string;
4374
+ messageIdHeader: string;
4375
+ inReplyTo: string;
4376
+ references: string[];
4377
+ id: string;
4378
+ threadId: string;
4379
+ via: "api" | "persona" | "dsn";
4380
+ }
4381
+ ```
4382
+
4383
+ #### `SimSnapshot` — interface · src/testing/sim-gmail/types.ts
4384
+
4385
+ ```ts
4386
+ interface SimSnapshot {
4387
+ version: 1;
4388
+ seed: number;
4389
+ clockOffset: number;
4390
+ virtualStart: number | null;
4391
+ counter: number;
4392
+ mailboxes: unknown[];
4393
+ outbox: SimOutboxEntry[];
4394
+ sent: SimSentEntry[];
4395
+ pending: unknown[];
4396
+ personas: unknown[];
4397
+ crowds: unknown[];
4398
+ refreshTokens: Array<[string, { address: string; revoked: boolean }]>;
4399
+ }
4400
+ ```
4401
+
3758
4402
  #### `TestManifest` — interface · src/testing/db.ts
3759
4403
 
3760
4404
  The parts of a plugin.json these helpers read.