@slates/provider 1.0.0-rc.2 → 1.0.0-rc.20

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 (124) hide show
  1. package/dist/index.cjs +2847 -2
  2. package/dist/index.d.cts +1019 -0
  3. package/dist/index.d.ts +1019 -9
  4. package/dist/index.module.js +2737 -2
  5. package/package.json +9 -6
  6. package/src/action/action.ts +92 -4
  7. package/src/action/attachment.ts +87 -0
  8. package/src/action/builder.test.ts +136 -0
  9. package/src/action/builder.ts +113 -15
  10. package/src/action/directUpload.ts +59 -0
  11. package/src/action/getFileUrlTool.ts +41 -0
  12. package/src/action/index.ts +4 -2
  13. package/src/action/publicTool.ts +36 -0
  14. package/src/action/scopes.test.ts +76 -0
  15. package/src/action/scopes.ts +103 -0
  16. package/src/action/tool.ts +22 -6
  17. package/src/action/trigger.ts +19 -11
  18. package/src/adapter/adapter.ts +38 -0
  19. package/src/adapter/index.test.ts +322 -0
  20. package/src/adapter/index.ts +2 -0
  21. package/src/adapter/spec.ts +154 -0
  22. package/src/auth/auth.ts +3 -3
  23. package/src/auth/index.ts +2 -0
  24. package/src/auth/oauth.test.ts +83 -0
  25. package/src/auth/oauth.ts +150 -0
  26. package/src/auth/redact.test.ts +103 -0
  27. package/src/auth/redact.ts +111 -0
  28. package/src/auth/types.ts +72 -13
  29. package/src/axios/http.test.ts +83 -0
  30. package/src/axios/index.ts +116 -10
  31. package/src/axios/trace.test.ts +377 -0
  32. package/src/axios/trace.ts +803 -0
  33. package/src/config/config.ts +14 -3
  34. package/src/context/context.ts +264 -17
  35. package/src/context/hook.test.ts +46 -0
  36. package/src/context/hook.ts +15 -6
  37. package/src/context/index.ts +1 -0
  38. package/src/context/pQueue.ts +4 -0
  39. package/src/crypto/hmac.test.ts +40 -0
  40. package/src/crypto/hmac.ts +32 -0
  41. package/src/crypto/index.ts +1 -0
  42. package/src/docs.ts +22 -0
  43. package/src/error/api.test.ts +114 -0
  44. package/src/error/api.ts +236 -0
  45. package/src/error/axios.ts +220 -0
  46. package/src/error/base.test.ts +178 -0
  47. package/src/error/base.ts +108 -2
  48. package/src/error/declaration.ts +7 -2
  49. package/src/error/defaults.ts +50 -0
  50. package/src/error/index.ts +4 -0
  51. package/src/error/service.ts +170 -0
  52. package/src/error/types.ts +75 -0
  53. package/src/error/utils.ts +157 -0
  54. package/src/file/index.test.ts +36 -0
  55. package/src/file/index.ts +27 -0
  56. package/src/index.ts +8 -0
  57. package/src/logger/logger.test.ts +57 -0
  58. package/src/logger/logger.ts +72 -11
  59. package/src/module/resolveDefaultExport.test.ts +32 -0
  60. package/src/module/resolveDefaultExport.ts +14 -0
  61. package/src/object/index.test.ts +38 -0
  62. package/src/object/index.ts +12 -0
  63. package/src/specification/slate.ts +40 -4
  64. package/src/specification/specification.ts +10 -6
  65. package/src/webhook/graph.test.ts +37 -0
  66. package/src/webhook/graph.ts +22 -0
  67. package/src/webhook/index.ts +2 -0
  68. package/src/webhook/meta.test.ts +52 -0
  69. package/src/webhook/meta.ts +43 -0
  70. package/dist/action/action.d.ts +0 -98
  71. package/dist/action/action.d.ts.map +0 -1
  72. package/dist/action/builder.d.ts +0 -27
  73. package/dist/action/builder.d.ts.map +0 -1
  74. package/dist/action/index.d.ts +0 -5
  75. package/dist/action/index.d.ts.map +0 -1
  76. package/dist/action/tool.d.ts +0 -14
  77. package/dist/action/tool.d.ts.map +0 -1
  78. package/dist/action/trigger.d.ts +0 -32
  79. package/dist/action/trigger.d.ts.map +0 -1
  80. package/dist/auth/auth.d.ts +0 -30
  81. package/dist/auth/auth.d.ts.map +0 -1
  82. package/dist/auth/index.d.ts +0 -3
  83. package/dist/auth/index.d.ts.map +0 -1
  84. package/dist/auth/types.d.ts +0 -148
  85. package/dist/auth/types.d.ts.map +0 -1
  86. package/dist/axios/index.d.ts +0 -4
  87. package/dist/axios/index.d.ts.map +0 -1
  88. package/dist/config/config.d.ts +0 -26
  89. package/dist/config/config.d.ts.map +0 -1
  90. package/dist/config/index.d.ts +0 -2
  91. package/dist/config/index.d.ts.map +0 -1
  92. package/dist/context/context.d.ts +0 -20
  93. package/dist/context/context.d.ts.map +0 -1
  94. package/dist/context/hook.d.ts +0 -4
  95. package/dist/context/hook.d.ts.map +0 -1
  96. package/dist/context/index.d.ts +0 -2
  97. package/dist/context/index.d.ts.map +0 -1
  98. package/dist/error/base.d.ts +0 -5
  99. package/dist/error/base.d.ts.map +0 -1
  100. package/dist/error/declaration.d.ts +0 -6
  101. package/dist/error/declaration.d.ts.map +0 -1
  102. package/dist/error/index.d.ts +0 -3
  103. package/dist/error/index.d.ts.map +0 -1
  104. package/dist/index.cjs.map +0 -1
  105. package/dist/index.d.ts.map +0 -1
  106. package/dist/index.modern.js +0 -2
  107. package/dist/index.modern.js.map +0 -1
  108. package/dist/index.module.js.map +0 -1
  109. package/dist/index.umd.js +0 -2
  110. package/dist/index.umd.js.map +0 -1
  111. package/dist/logger/index.d.ts +0 -2
  112. package/dist/logger/index.d.ts.map +0 -1
  113. package/dist/logger/logger.d.ts +0 -20
  114. package/dist/logger/logger.d.ts.map +0 -1
  115. package/dist/specification/index.d.ts +0 -3
  116. package/dist/specification/index.d.ts.map +0 -1
  117. package/dist/specification/slate.d.ts +0 -15
  118. package/dist/specification/slate.d.ts.map +0 -1
  119. package/dist/specification/specification.d.ts +0 -30
  120. package/dist/specification/specification.d.ts.map +0 -1
  121. package/dist/specification/zero.d.ts +0 -13
  122. package/dist/specification/zero.d.ts.map +0 -1
  123. package/dist/tokens.d.ts +0 -34
  124. package/dist/tokens.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/provider",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0-rc.20",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -11,7 +11,7 @@
11
11
  "package.json"
12
12
  ],
13
13
  "author": "Tobias Herber",
14
- "license": "Apache 2",
14
+ "license": "FSL 1.1",
15
15
  "type": "module",
16
16
  "source": "src/index.ts",
17
17
  "exports": {
@@ -23,20 +23,23 @@
23
23
  "main": "./dist/index.cjs",
24
24
  "module": "./dist/index.module.js",
25
25
  "types": "dist/index.d.ts",
26
- "unpkg": "./dist/index.umd.js",
26
+ "unpkg": "./dist/index.module.js",
27
27
  "scripts": {
28
28
  "test": "vitest run --passWithNoTests",
29
- "lint": "prettier src/**/*.ts --check",
30
- "build": "microbundle"
29
+ "build": "tsup --config ../../tsup.packages.config.ts --external async_hooks",
30
+ "typecheck": "tsc --noEmit",
31
+ "biome:check": "biome check --write src"
31
32
  },
32
33
  "dependencies": {
34
+ "@lowerdeck/error": "^1.1.0",
33
35
  "@toon-format/toon": "^2.1.0",
34
36
  "axios": "^1.13.2",
37
+ "p-queue": "^6.6.2",
35
38
  "zod": "^4.2.1"
36
39
  },
37
40
  "devDependencies": {
38
41
  "microbundle": "^0.15.1",
39
- "@slates/tsconfig": "^1.0.0",
42
+ "@slates/tsconfig": "1.0.0-rc.1",
40
43
  "typescript": "5.8.2",
41
44
  "vitest": "^3.1.2"
42
45
  }
@@ -1,9 +1,19 @@
1
- import { z } from 'zod';
2
- import { SlateContext } from '../context';
3
- import { SlateSpecification } from '../specification/specification';
1
+ import type { z } from 'zod';
2
+ import type { SlateContext, SlatePublicContext } from '../context';
3
+ import type { SlateActionDocsReference } from '../docs';
4
+ import type { SlateSpecification } from '../specification/specification';
5
+ import type { SlateAttachment } from './attachment';
4
6
 
5
7
  export type SlateActionType = 'tool' | 'trigger';
6
8
 
9
+ export interface SlateActionScopeClause {
10
+ OR: string[];
11
+ }
12
+
13
+ export interface SlateActionScopes {
14
+ AND: SlateActionScopeClause[];
15
+ }
16
+
7
17
  export interface SlateActionParameters {
8
18
  key: string;
9
19
  name: string;
@@ -16,6 +26,11 @@ export interface SlateActionParameters {
16
26
  [key: string]: boolean | undefined;
17
27
  };
18
28
  metadata?: Record<string, any>;
29
+ scopes?: SlateActionScopes;
30
+ authMethods?: string[];
31
+ docs?: SlateActionDocsReference[];
32
+ adapter?: string | null;
33
+ isPublic?: boolean;
19
34
  }
20
35
 
21
36
  export type SlateToolInvocationHandler<
@@ -26,6 +41,15 @@ export type SlateToolInvocationHandler<
26
41
  > = (context: SlateContext<ConfigType, AuthType, InputType>) => Promise<{
27
42
  output: OutputType;
28
43
  message: string;
44
+ attachments?: SlateAttachment[];
45
+ }>;
46
+
47
+ export type SlatePublicToolInvocationHandler<InputType extends {}, OutputType extends {}> = (
48
+ context: SlatePublicContext<InputType>
49
+ ) => Promise<{
50
+ output: OutputType;
51
+ message: string;
52
+ attachments?: SlateAttachment[];
29
53
  }>;
30
54
 
31
55
  export type SlateTriggerMappingHandler<
@@ -48,15 +72,58 @@ export type SlateTriggerPollingHandler<
48
72
  updatedState?: any;
49
73
  }>;
50
74
 
75
+ export interface SlateWebhookHttpResponseInit {
76
+ status?: number;
77
+ headers?: Record<string, string>;
78
+ body?: string | Uint8Array | null;
79
+ }
80
+
81
+ export type SlateWebhookHttpMethod =
82
+ | 'GET'
83
+ | 'POST'
84
+ | 'PUT'
85
+ | 'PATCH'
86
+ | 'DELETE'
87
+ | 'HEAD'
88
+ | 'OPTIONS';
89
+
90
+ export interface SlateWebhookRequestMatcher {
91
+ method?: string;
92
+ hasQueryParam?: string;
93
+ hasHeader?: string;
94
+ jsonBodyField?: {
95
+ path: string;
96
+ equals?: string;
97
+ };
98
+ formBodyField?: {
99
+ path: string;
100
+ equals?: string;
101
+ };
102
+ }
103
+
104
+ export interface SlateWebhookHttpOptions {
105
+ methods?: SlateWebhookHttpMethod[];
106
+ sync?: {
107
+ mode: 'never' | 'match' | 'always';
108
+ match?: SlateWebhookRequestMatcher[];
109
+ timeoutMs?: number;
110
+ };
111
+ }
112
+
51
113
  export type SlateTriggerWebhookRequestHandler<
52
114
  ConfigType extends {},
53
115
  AuthType extends {},
54
116
  InputType extends {}
55
117
  > = (
56
- context: SlateContext<ConfigType, AuthType, { request: Request; state: any | null }>
118
+ context: SlateContext<
119
+ ConfigType,
120
+ AuthType,
121
+ { request: Request; state: any | null; registrationDetails: any | null }
122
+ >
57
123
  ) => Promise<{
58
124
  inputs: InputType[];
59
125
  updatedState?: any;
126
+ response?: Response | SlateWebhookHttpResponseInit;
60
127
  }>;
61
128
 
62
129
  export type SlateTriggerWebhookAutoRegistrationHandler<
@@ -103,6 +170,7 @@ export interface SlateActionParametersTrigger<
103
170
  polling?: SlatePollingOptions;
104
171
  handleEvent: SlateTriggerMappingHandler<ConfigType, AuthType, InputType, OutputType>;
105
172
  handleRequest?: SlateTriggerWebhookRequestHandler<ConfigType, AuthType, InputType>;
173
+ http?: SlateWebhookHttpOptions;
106
174
  pollEvents?: SlateTriggerPollingHandler<ConfigType, AuthType, InputType>;
107
175
  autoRegisterWebhook?: SlateTriggerWebhookAutoRegistrationHandler<ConfigType, AuthType>;
108
176
  autoUnregisterWebhook?: SlateTriggerWebhookAutoUnregistrationHandler<ConfigType, AuthType>;
@@ -188,4 +256,24 @@ export abstract class SlateAction<
188
256
  get metadata() {
189
257
  return this._params.metadata;
190
258
  }
259
+
260
+ get scopes() {
261
+ return this._params.scopes;
262
+ }
263
+
264
+ get authMethods() {
265
+ return this._params.authMethods;
266
+ }
267
+
268
+ get docs() {
269
+ return this._params.docs;
270
+ }
271
+
272
+ get adapter() {
273
+ return this._params.adapter ?? null;
274
+ }
275
+
276
+ get isPublic() {
277
+ return this._params.isPublic === true;
278
+ }
191
279
  }
@@ -0,0 +1,87 @@
1
+ export interface SlateAttachmentUrlContent {
2
+ type: 'url';
3
+ url: string;
4
+ headers?: Record<string, string>;
5
+ query?: Record<string, string>;
6
+ refreshReference?: unknown;
7
+ refreshAt?: string;
8
+ }
9
+
10
+ export interface SlateAttachmentInlineContent {
11
+ type: 'content';
12
+ encoding: 'base64' | 'utf-8';
13
+ content: string;
14
+ }
15
+
16
+ export interface SlateAttachmentUploadReferenceContent {
17
+ type: 'upload_reference';
18
+ referenceId: string;
19
+ }
20
+
21
+ export interface SlateAttachment {
22
+ mimeType?: string;
23
+ attachmentHash?: string;
24
+ content:
25
+ | SlateAttachmentUrlContent
26
+ | SlateAttachmentInlineContent
27
+ | SlateAttachmentUploadReferenceContent;
28
+ }
29
+
30
+ export let createUrlAttachment = (
31
+ url: string,
32
+ opts: {
33
+ mimeType?: string;
34
+ attachmentHash?: string;
35
+ headers?: Record<string, string>;
36
+ query?: Record<string, string>;
37
+ refreshReference?: unknown;
38
+ refreshAt?: string;
39
+ } = {}
40
+ ): SlateAttachment => {
41
+ if (opts.refreshReference !== undefined && !opts.refreshAt) {
42
+ throw new Error('createUrlAttachment: refreshAt is required when refreshReference is set');
43
+ }
44
+
45
+ return {
46
+ mimeType: opts.mimeType,
47
+ attachmentHash: opts.attachmentHash,
48
+ content: {
49
+ type: 'url',
50
+ url,
51
+ ...(opts.headers ? { headers: opts.headers } : {}),
52
+ ...(opts.query ? { query: opts.query } : {}),
53
+ ...(opts.refreshReference !== undefined
54
+ ? { refreshReference: opts.refreshReference }
55
+ : {}),
56
+ ...(opts.refreshAt ? { refreshAt: opts.refreshAt } : {})
57
+ }
58
+ };
59
+ };
60
+
61
+ export let createBase64Attachment = (
62
+ content: string,
63
+ mimeType?: string,
64
+ attachmentHash?: string
65
+ ): SlateAttachment => ({
66
+ mimeType,
67
+ attachmentHash,
68
+ content: {
69
+ type: 'content',
70
+ encoding: 'base64',
71
+ content
72
+ }
73
+ });
74
+
75
+ export let createTextAttachment = (
76
+ content: string,
77
+ mimeType?: string,
78
+ attachmentHash?: string
79
+ ): SlateAttachment => ({
80
+ mimeType,
81
+ attachmentHash,
82
+ content: {
83
+ type: 'content',
84
+ encoding: 'utf-8',
85
+ content
86
+ }
87
+ });
@@ -0,0 +1,136 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { z } from 'zod';
3
+ import { SlateAuth } from '../auth';
4
+ import { SlateConfig } from '../config';
5
+ import { SlatePublicContext } from '../context';
6
+ import { SlateSpecification } from '../specification/specification';
7
+ import { SlatePublicTool } from './publicTool';
8
+ import { SlateTool } from './tool';
9
+
10
+ let createTestSpec = () => {
11
+ let config = SlateConfig.create(
12
+ z.object({
13
+ prefix: z.string()
14
+ })
15
+ );
16
+ let auth = SlateAuth.create<{ token: string }>().output(
17
+ z.object({
18
+ token: z.string()
19
+ })
20
+ );
21
+
22
+ return SlateSpecification.create({
23
+ key: 'demo',
24
+ name: 'Demo',
25
+ config,
26
+ auth
27
+ });
28
+ };
29
+
30
+ describe('SlateActionBuilder.lockInterface', () => {
31
+ it('prevents changing input and output schemas after the interface is locked', () => {
32
+ let spec = createTestSpec();
33
+ let builder = SlateTool.create(spec, {
34
+ key: 'send',
35
+ name: 'Send'
36
+ })
37
+ .input(z.object({ to: z.string() }))
38
+ .output(z.object({ id: z.string() }))
39
+ .lockInterface();
40
+
41
+ expect(() => builder.input(z.object({}))).toThrow(
42
+ 'Adapter contract input schema cannot be changed'
43
+ );
44
+ expect(() => builder.output(z.object({}))).toThrow(
45
+ 'Adapter contract output schema cannot be changed'
46
+ );
47
+ });
48
+ });
49
+
50
+ describe('SlatePublicTool', () => {
51
+ it('builds a public tool that omits config and auth from the handler context', async () => {
52
+ let spec = createTestSpec();
53
+ let tool = SlatePublicTool.create(spec, {
54
+ key: 'setup',
55
+ name: 'Setup'
56
+ })
57
+ .input(
58
+ z.object({
59
+ name: z.string()
60
+ })
61
+ )
62
+ .output(
63
+ z.object({
64
+ greeting: z.string()
65
+ })
66
+ )
67
+ .handleInvocation(async ctx => {
68
+ expect(ctx).not.toHaveProperty('config');
69
+ expect(ctx).not.toHaveProperty('auth');
70
+
71
+ return {
72
+ output: { greeting: `Hello ${ctx.input.name}` },
73
+ message: 'ok'
74
+ };
75
+ })
76
+ .build();
77
+
78
+ expect(tool.isPublic).toBe(true);
79
+ expect(tool.key).toBe('setup');
80
+
81
+ let result = await tool.handleInvocation(
82
+ new SlatePublicContext({ name: 'Ada' }, spec, {
83
+ debug: () => {},
84
+ error: () => {},
85
+ info: () => {},
86
+ progress: () => {},
87
+ warn: () => {}
88
+ } as any) as any
89
+ );
90
+
91
+ expect(result.output).toEqual({ greeting: 'Hello Ada' });
92
+ });
93
+
94
+ it('keeps regular tools private by default', () => {
95
+ let spec = createTestSpec();
96
+ let tool = SlateTool.create(spec, {
97
+ key: 'send',
98
+ name: 'Send'
99
+ })
100
+ .input(z.object({}))
101
+ .output(z.object({}))
102
+ .handleInvocation(async () => ({
103
+ output: {},
104
+ message: 'ok'
105
+ }))
106
+ .build();
107
+
108
+ expect(tool.isPublic).toBe(false);
109
+ });
110
+
111
+ it('rejects authentication methods on public tools', () => {
112
+ let spec = createTestSpec();
113
+
114
+ expect(() =>
115
+ SlatePublicTool.create(spec, {
116
+ key: 'setup',
117
+ name: 'Setup'
118
+ }).authMethods(['token_auth'])
119
+ ).toThrow('Public tools cannot require authentication methods');
120
+
121
+ expect(() =>
122
+ SlatePublicTool.create(spec, {
123
+ key: 'setup',
124
+ name: 'Setup',
125
+ authMethods: ['token_auth']
126
+ })
127
+ .input(z.object({}))
128
+ .output(z.object({}))
129
+ .handleInvocation(async () => ({
130
+ output: {},
131
+ message: 'ok'
132
+ }))
133
+ .build()
134
+ ).toThrow('Public tools cannot require authentication methods');
135
+ });
136
+ });
@@ -1,21 +1,25 @@
1
- import z from 'zod';
1
+ import type z from 'zod';
2
2
  import { SlateDeclarationError } from '../error';
3
- import { SlateSpecification } from '../specification/specification';
4
- import {
3
+ import type { SlateSpecification } from '../specification/specification';
4
+ import type {
5
5
  SlateAction,
6
6
  SlateActionCreateParameters,
7
7
  SlateActionParameters,
8
8
  SlateActionParametersTool,
9
9
  SlateActionParametersTrigger,
10
+ SlateActionScopes,
10
11
  SlateActionType,
11
12
  SlatePollingOptions,
13
+ SlatePublicToolInvocationHandler,
12
14
  SlateToolInvocationHandler,
13
15
  SlateTriggerMappingHandler,
14
16
  SlateTriggerPollingHandler,
15
17
  SlateTriggerWebhookAutoRegistrationHandler,
16
18
  SlateTriggerWebhookAutoUnregistrationHandler,
17
- SlateTriggerWebhookRequestHandler
19
+ SlateTriggerWebhookRequestHandler,
20
+ SlateWebhookHttpOptions
18
21
  } from './action';
22
+ import { validateScopes } from './scopes';
19
23
 
20
24
  export class SlateActionBuilder<
21
25
  Type extends SlateActionType,
@@ -23,12 +27,15 @@ export class SlateActionBuilder<
23
27
  AuthType extends {},
24
28
  InputType extends {},
25
29
  OutputType extends {},
26
- Result extends SlateAction<Type, ConfigType, AuthType, any, any>
30
+ Result extends SlateAction<Type, ConfigType, AuthType, any, any>,
31
+ IsPublic extends boolean = false
27
32
  > {
28
33
  #configSchema: z.ZodType<ConfigType>;
29
34
  #authSchema: z.ZodType<AuthType>;
30
35
  #inputSchema: z.ZodType<InputType> | null = null;
31
36
  #outputSchema: z.ZodType<OutputType> | null = null;
37
+ #scopes: SlateActionScopes | undefined;
38
+ #authMethods: string[] | undefined;
32
39
 
33
40
  #toolParams: SlateActionParametersTool<ConfigType, AuthType, InputType, OutputType> | null =
34
41
  null;
@@ -38,14 +45,16 @@ export class SlateActionBuilder<
38
45
  InputType,
39
46
  OutputType
40
47
  > | null = null;
48
+ #interfaceLocked = false;
41
49
 
42
50
  constructor(
43
51
  private readonly type: Type,
44
- private readonly spec: SlateSpecification<ConfigType, AuthType>,
52
+ readonly spec: SlateSpecification<ConfigType, AuthType>,
45
53
  private readonly params: SlateActionParameters,
46
54
  private readonly factory: (
47
55
  params: SlateActionCreateParameters<any, any, any, any>
48
- ) => Result
56
+ ) => Result,
57
+ private readonly isPublicTool: IsPublic = false as IsPublic
49
58
  ) {
50
59
  this.#configSchema = spec.configSchema;
51
60
  this.#authSchema = spec.authSchema;
@@ -53,28 +62,102 @@ export class SlateActionBuilder<
53
62
 
54
63
  input<NewInputType extends {}>(
55
64
  schema: z.ZodType<NewInputType>
56
- ): SlateActionBuilder<Type, ConfigType, AuthType, NewInputType, OutputType, Result> {
65
+ ): SlateActionBuilder<
66
+ Type,
67
+ ConfigType,
68
+ AuthType,
69
+ NewInputType,
70
+ OutputType,
71
+ Result,
72
+ IsPublic
73
+ > {
74
+ if (this.#interfaceLocked) {
75
+ throw new SlateDeclarationError('Adapter contract input schema cannot be changed');
76
+ }
77
+
57
78
  this.#inputSchema = schema as any;
58
79
  return this as any;
59
80
  }
60
81
 
61
82
  output<NewOutputType extends {}>(
62
83
  schema: z.ZodType<NewOutputType>
63
- ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, NewOutputType, Result> {
84
+ ): SlateActionBuilder<
85
+ Type,
86
+ ConfigType,
87
+ AuthType,
88
+ InputType,
89
+ NewOutputType,
90
+ Result,
91
+ IsPublic
92
+ > {
93
+ if (this.#interfaceLocked) {
94
+ throw new SlateDeclarationError('Adapter contract output schema cannot be changed');
95
+ }
96
+
64
97
  this.#outputSchema = schema as any;
65
98
  return this as any;
66
99
  }
67
100
 
101
+ lockInterface(): SlateActionBuilder<
102
+ Type,
103
+ ConfigType,
104
+ AuthType,
105
+ InputType,
106
+ OutputType,
107
+ Result,
108
+ IsPublic
109
+ > {
110
+ this.#interfaceLocked = true;
111
+ return this;
112
+ }
113
+
114
+ scopes(
115
+ scopes: SlateActionScopes
116
+ ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result, IsPublic> {
117
+ validateScopes(scopes);
118
+ this.#scopes = scopes;
119
+ return this;
120
+ }
121
+
122
+ authMethods(
123
+ authMethods: string[]
124
+ ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result, IsPublic> {
125
+ if (this.isPublicTool) {
126
+ throw new SlateDeclarationError('Public tools cannot require authentication methods');
127
+ }
128
+
129
+ this.#authMethods = this.validateAuthMethods(authMethods);
130
+ return this;
131
+ }
132
+
133
+ private validateAuthMethods(authMethods: string[] | undefined) {
134
+ if (!authMethods) return undefined;
135
+
136
+ let normalized = [...new Set(authMethods.map(authMethod => authMethod.trim()))];
137
+ if (normalized.some(authMethod => !authMethod)) {
138
+ throw new SlateDeclarationError('Auth method IDs must be non-empty strings');
139
+ }
140
+
141
+ return normalized;
142
+ }
143
+
68
144
  handleInvocation(
69
- handler: SlateToolInvocationHandler<ConfigType, AuthType, InputType, OutputType>
70
- ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
145
+ handler: IsPublic extends true
146
+ ? SlatePublicToolInvocationHandler<InputType, OutputType>
147
+ : SlateToolInvocationHandler<ConfigType, AuthType, InputType, OutputType>
148
+ ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result, IsPublic> {
71
149
  if (this.type !== 'tool') {
72
150
  throw new SlateDeclarationError('handleInvocation can only be set for tool actions');
73
151
  }
74
152
 
75
153
  this.#toolParams = {
76
154
  type: 'tool',
77
- handleInvocation: handler
155
+ handleInvocation: handler as SlateToolInvocationHandler<
156
+ ConfigType,
157
+ AuthType,
158
+ InputType,
159
+ OutputType
160
+ >
78
161
  };
79
162
 
80
163
  return this;
@@ -85,7 +168,8 @@ export class SlateActionBuilder<
85
168
  handleRequest: SlateTriggerWebhookRequestHandler<ConfigType, AuthType, InputType>;
86
169
  autoRegisterWebhook?: SlateTriggerWebhookAutoRegistrationHandler<ConfigType, AuthType>;
87
170
  autoUnregisterWebhook?: SlateTriggerWebhookAutoUnregistrationHandler<ConfigType, AuthType>;
88
- }): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
171
+ http?: SlateWebhookHttpOptions;
172
+ }): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result, IsPublic> {
89
173
  if (this.type !== 'trigger') {
90
174
  throw new SlateDeclarationError('handleEvent can only be set for trigger actions');
91
175
  }
@@ -96,7 +180,8 @@ export class SlateActionBuilder<
96
180
  handleEvent: props.handleEvent,
97
181
  handleRequest: props.handleRequest,
98
182
  autoRegisterWebhook: props.autoRegisterWebhook,
99
- autoUnregisterWebhook: props.autoUnregisterWebhook
183
+ autoUnregisterWebhook: props.autoUnregisterWebhook,
184
+ http: props.http
100
185
  };
101
186
 
102
187
  return this;
@@ -106,7 +191,7 @@ export class SlateActionBuilder<
106
191
  options?: SlatePollingOptions;
107
192
  pollEvents?: SlateTriggerPollingHandler<ConfigType, AuthType, InputType>;
108
193
  handleEvent: SlateTriggerMappingHandler<ConfigType, AuthType, InputType, OutputType>;
109
- }): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
194
+ }): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result, IsPublic> {
110
195
  if (this.type !== 'trigger') {
111
196
  throw new SlateDeclarationError('handleEvent can only be set for trigger actions');
112
197
  }
@@ -123,6 +208,16 @@ export class SlateActionBuilder<
123
208
  }
124
209
 
125
210
  build() {
211
+ let scopes = this.#scopes ?? this.params.scopes;
212
+ if (scopes) {
213
+ validateScopes(scopes);
214
+ }
215
+ let authMethods = this.validateAuthMethods(this.#authMethods ?? this.params.authMethods);
216
+
217
+ if (this.isPublicTool && authMethods) {
218
+ throw new SlateDeclarationError('Public tools cannot require authentication methods');
219
+ }
220
+
126
221
  if (!this.#inputSchema) {
127
222
  throw new SlateDeclarationError('Input schema is not defined');
128
223
  }
@@ -138,6 +233,9 @@ export class SlateActionBuilder<
138
233
 
139
234
  return this.factory({
140
235
  ...this.params,
236
+ scopes,
237
+ authMethods,
238
+ isPublic: this.isPublicTool === true,
141
239
  configSchema: this.#configSchema,
142
240
  authSchema: this.#authSchema,
143
241
  inputSchema: this.#inputSchema,
@@ -0,0 +1,59 @@
1
+ import type { SlateAttachment } from './attachment';
2
+
3
+ export interface SlateLiveInvocationInfo {
4
+ token: string;
5
+ baseUrl: string;
6
+ maxAttachmentSizeBytes: number;
7
+ }
8
+
9
+ export interface SlateDirectUploadInput {
10
+ live: SlateLiveInvocationInfo;
11
+ mimeType?: string;
12
+ filename?: string;
13
+ body: ReadableStream<Uint8Array> | Uint8Array;
14
+ }
15
+
16
+ export let uploadAttachmentDirect = async (
17
+ d: SlateDirectUploadInput
18
+ ): Promise<SlateAttachment> => {
19
+ let baseUrl = d.live.baseUrl.replace(/\/$/, '');
20
+
21
+ let registerRes = await fetch(`${baseUrl}/slates-hub/live-invocation/attachments`, {
22
+ method: 'POST',
23
+ headers: {
24
+ 'content-type': 'application/json',
25
+ authorization: `Bearer ${d.live.token}`
26
+ },
27
+ body: JSON.stringify({
28
+ attachments: [{ mimeType: d.mimeType, filename: d.filename }]
29
+ })
30
+ });
31
+ if (!registerRes.ok) {
32
+ throw new Error(
33
+ `Failed to register attachment for direct upload (status ${registerRes.status})`
34
+ );
35
+ }
36
+
37
+ let { attachments } = (await registerRes.json()) as {
38
+ attachments: { referenceId: string; uploadUrl: string }[];
39
+ };
40
+ let registration = attachments[0];
41
+ if (!registration) {
42
+ throw new Error('Live invocation API returned no attachment registrations');
43
+ }
44
+
45
+ let putRes = await fetch(registration.uploadUrl, {
46
+ method: 'PUT',
47
+ ...(d.body instanceof Uint8Array ? {} : ({ duplex: 'half' } as any)),
48
+ headers: d.mimeType ? { 'content-type': d.mimeType } : undefined,
49
+ body: d.body
50
+ });
51
+ if (!putRes.ok) {
52
+ throw new Error(`Failed to upload attachment content (status ${putRes.status})`);
53
+ }
54
+
55
+ return {
56
+ mimeType: d.mimeType,
57
+ content: { type: 'upload_reference', referenceId: registration.referenceId }
58
+ };
59
+ };