@project-ajax/sdk 0.0.68 → 0.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities/automation.d.ts +8 -28
- package/dist/capabilities/automation.d.ts.map +1 -1
- package/dist/capabilities/automation.js +3 -2
- package/dist/capabilities/oauth.d.ts +6 -31
- package/dist/capabilities/oauth.d.ts.map +1 -1
- package/dist/capabilities/oauth.js +4 -2
- package/dist/capabilities/sync.d.ts +71 -30
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/capabilities/sync.js +6 -5
- package/dist/capabilities/tool.d.ts +5 -25
- package/dist/capabilities/tool.d.ts.map +1 -1
- package/dist/capabilities/tool.js +3 -2
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -9
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.d.ts +175 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +218 -0
- package/package.json +1 -5
- package/src/capabilities/automation.test.ts +17 -23
- package/src/capabilities/automation.ts +14 -30
- package/src/capabilities/oauth.test.ts +16 -15
- package/src/capabilities/oauth.ts +7 -31
- package/src/capabilities/sync.ts +81 -31
- package/src/capabilities/tool.test.ts +105 -89
- package/src/capabilities/tool.ts +12 -31
- package/src/index.ts +9 -5
- package/src/types.ts +11 -0
- package/src/worker.ts +278 -0
|
@@ -88,46 +88,21 @@ export type OAuthConfiguration =
|
|
|
88
88
|
| NotionManagedOAuthConfiguration
|
|
89
89
|
| UserManagedOAuthConfiguration;
|
|
90
90
|
|
|
91
|
+
export type OAuthCapability = ReturnType<typeof createOAuthCapability>;
|
|
92
|
+
|
|
91
93
|
/**
|
|
92
94
|
* Creates an OAuth provider configuration for authenticating with third-party services.
|
|
93
95
|
*
|
|
94
|
-
* There are two ways to configure OAuth:
|
|
95
|
-
*
|
|
96
|
-
* 1. Notion-managed providers:
|
|
97
|
-
* ```ts
|
|
98
|
-
* oauth({
|
|
99
|
-
* type: "notion_managed",
|
|
100
|
-
* name: "my-google-auth",
|
|
101
|
-
* provider: "google"
|
|
102
|
-
* })
|
|
103
|
-
* ```
|
|
104
|
-
*
|
|
105
|
-
* 2. User-managed OAuth configuration:
|
|
106
|
-
* ```ts
|
|
107
|
-
* oauth({
|
|
108
|
-
* type: "user_managed",
|
|
109
|
-
* name: "my-custom-oauth",
|
|
110
|
-
* authorizationEndpoint: "https://provider.com/oauth/authorize",
|
|
111
|
-
* tokenEndpoint: "https://provider.com/oauth/token",
|
|
112
|
-
* scope: "read write",
|
|
113
|
-
* clientId: process.env.CLIENT_ID,
|
|
114
|
-
* clientSecret: process.env.CLIENT_SECRET,
|
|
115
|
-
* authorizationParams: {
|
|
116
|
-
* access_type: "offline",
|
|
117
|
-
* prompt: "consent"
|
|
118
|
-
* }
|
|
119
|
-
* })
|
|
120
|
-
* ```
|
|
121
|
-
*
|
|
122
96
|
* @param config - The OAuth configuration (Notion-managed or user-managed).
|
|
123
97
|
* @returns An OAuth provider definition.
|
|
124
98
|
*/
|
|
125
|
-
export function
|
|
99
|
+
export function createOAuthCapability(key: string, config: OAuthConfiguration) {
|
|
126
100
|
const envKey = oauthNameToEnvKey(config.name);
|
|
127
101
|
|
|
128
102
|
if ("provider" in config) {
|
|
129
103
|
return {
|
|
130
|
-
_tag: "oauth",
|
|
104
|
+
_tag: "oauth" as const,
|
|
105
|
+
key,
|
|
131
106
|
envKey,
|
|
132
107
|
async accessToken(): Promise<string> {
|
|
133
108
|
return readRequiredEnvVar(envKey, { name: config.name });
|
|
@@ -142,7 +117,8 @@ export function oauth(config: OAuthConfiguration) {
|
|
|
142
117
|
}
|
|
143
118
|
|
|
144
119
|
return {
|
|
145
|
-
_tag: "oauth",
|
|
120
|
+
_tag: "oauth" as const,
|
|
121
|
+
key,
|
|
146
122
|
envKey,
|
|
147
123
|
async accessToken(): Promise<string> {
|
|
148
124
|
return readRequiredEnvVar(envKey, { name: config.name });
|
package/src/capabilities/sync.ts
CHANGED
|
@@ -26,44 +26,90 @@ type PropertyValueType<T extends PropertyConfiguration> = T extends {
|
|
|
26
26
|
: TextValue;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Sync mode determines how the sync handles data lifecycle.
|
|
30
30
|
*/
|
|
31
|
-
export type
|
|
31
|
+
export type SyncMode = "replace" | "incremental";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A change representing a record to be created or updated.
|
|
35
|
+
*/
|
|
36
|
+
export type SyncChangeUpsert<
|
|
37
|
+
PK extends string,
|
|
38
|
+
S extends PropertySchema<PK>,
|
|
39
|
+
> = {
|
|
40
|
+
/**
|
|
41
|
+
* The type of change. Use `"upsert"` to create or update a record.
|
|
42
|
+
*/
|
|
43
|
+
type: "upsert";
|
|
44
|
+
/**
|
|
45
|
+
* A unique identifier for this record, used to match against existing pages.
|
|
46
|
+
* This value will be stored in the property specified by `primaryKeyProperty`.
|
|
47
|
+
*/
|
|
32
48
|
key: string;
|
|
49
|
+
/**
|
|
50
|
+
* The property values for this record.
|
|
51
|
+
* Keys must match the property names defined in the schema.
|
|
52
|
+
* Use the Builder helpers (e.g., `Builder.title()`, `Builder.richText()`) to create values.
|
|
53
|
+
*/
|
|
33
54
|
properties: {
|
|
34
55
|
[Property in keyof S]: PropertyValueType<S[Property]>;
|
|
35
56
|
};
|
|
36
57
|
/**
|
|
37
58
|
* Optional icon to use as the icon for this row's page.
|
|
38
|
-
* Use the `
|
|
59
|
+
* Use the `Builder.emojiIcon()`, `Builder.notionIcon()`, or `Builder.imageIcon()` helpers.
|
|
39
60
|
*/
|
|
40
61
|
icon?: Icon;
|
|
41
62
|
/**
|
|
42
|
-
* Optional markdown content to add to the page.
|
|
43
|
-
* This will be converted to blocks and added as page content.
|
|
63
|
+
* Optional markdown content to add to the page body.
|
|
64
|
+
* This will be converted to Notion blocks and added as page content.
|
|
44
65
|
*/
|
|
45
66
|
pageContentMarkdown?: string;
|
|
46
67
|
};
|
|
47
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A change representing a record to be deleted.
|
|
71
|
+
* Only applicable when using `mode: "incremental"`.
|
|
72
|
+
*/
|
|
73
|
+
export type SyncChangeDelete = {
|
|
74
|
+
/**
|
|
75
|
+
* The type of change. Use `"delete"` to remove a record.
|
|
76
|
+
*/
|
|
77
|
+
type: "delete";
|
|
78
|
+
/**
|
|
79
|
+
* The unique identifier of the record to delete.
|
|
80
|
+
* Must match the `key` of a previously upserted record.
|
|
81
|
+
*/
|
|
82
|
+
key: string;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A change to be applied to the synced database.
|
|
87
|
+
* Can be either an upsert (create/update) or a delete.
|
|
88
|
+
*/
|
|
89
|
+
export type SyncChange<PK extends string, S extends PropertySchema<PK>> =
|
|
90
|
+
| SyncChangeUpsert<PK, S>
|
|
91
|
+
| SyncChangeDelete;
|
|
92
|
+
|
|
48
93
|
/**
|
|
49
94
|
* Result returned from the sync execute function.
|
|
50
95
|
*/
|
|
51
96
|
export type SyncExecutionResult<PK extends string, Context = unknown> = {
|
|
52
97
|
/**
|
|
53
|
-
* The batch of
|
|
98
|
+
* The batch of changes to apply in this execution.
|
|
99
|
+
* Can include upserts (create/update) and deletes.
|
|
54
100
|
*/
|
|
55
|
-
|
|
101
|
+
changes: SyncChange<PK, PropertySchema<PK>>[];
|
|
56
102
|
|
|
57
103
|
/**
|
|
58
|
-
* Indicates whether
|
|
59
|
-
* - `true`:
|
|
60
|
-
* - `false`:
|
|
104
|
+
* Indicates whether there is more data to fetch.
|
|
105
|
+
* - `true`: More data available, will trigger another execution with nextContext
|
|
106
|
+
* - `false`: No more data to fetch, sync is complete
|
|
61
107
|
*/
|
|
62
|
-
|
|
108
|
+
hasMore: boolean;
|
|
63
109
|
|
|
64
110
|
/**
|
|
65
111
|
* Optional context data to pass to the next execution.
|
|
66
|
-
* Required if `
|
|
112
|
+
* Required if `hasMore` is `true`, ignored if `hasMore` is `false`.
|
|
67
113
|
* This can be any type of data (cursor, page number, timestamp, etc.).
|
|
68
114
|
* The same data will be provided in the context parameter of the next execution.
|
|
69
115
|
*/
|
|
@@ -92,15 +138,16 @@ export type SyncConfiguration<
|
|
|
92
138
|
schema: S;
|
|
93
139
|
|
|
94
140
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* -
|
|
99
|
-
*
|
|
141
|
+
* How the sync handles data lifecycle:
|
|
142
|
+
* - "replace": Each sync returns the complete dataset. After hasMore:false,
|
|
143
|
+
* pages not seen in this sync run are deleted.
|
|
144
|
+
* - "incremental": Sync returns changes only. After hasMore:false, sync
|
|
145
|
+
* continues from saved cursor. Use delete markers
|
|
146
|
+
* to explicitly remove pages.
|
|
100
147
|
*
|
|
101
|
-
* @default
|
|
148
|
+
* @default "replace"
|
|
102
149
|
*/
|
|
103
|
-
|
|
150
|
+
mode?: SyncMode;
|
|
104
151
|
|
|
105
152
|
/**
|
|
106
153
|
* How often the sync should run.
|
|
@@ -118,27 +165,29 @@ export type SyncConfiguration<
|
|
|
118
165
|
* A function that fetches the data to sync from the third-party service.
|
|
119
166
|
*
|
|
120
167
|
* This function can return all data at once, or implement pagination by:
|
|
121
|
-
* 1. Returning a batch of
|
|
168
|
+
* 1. Returning a batch of changes with `hasMore: true` and a `nextContext`
|
|
122
169
|
* 2. The runtime will call execute again with that context data
|
|
123
|
-
* 3. Continue until `
|
|
170
|
+
* 3. Continue until `hasMore: false` is returned
|
|
124
171
|
*
|
|
125
172
|
* The runtime will handle diffing against the data source and creating,
|
|
126
173
|
* updating, and deleting pages as necessary.
|
|
127
174
|
*
|
|
128
175
|
* @param context - Optional context data from previous execution (undefined on first call)
|
|
129
|
-
* @returns A result containing
|
|
176
|
+
* @returns A result containing changes, hasMore status, and optional nextContext
|
|
130
177
|
*/
|
|
131
178
|
execute: (context?: Context) => Promise<SyncExecutionResult<PK, Context>>;
|
|
132
179
|
};
|
|
133
180
|
|
|
134
181
|
export type SyncHandlerResult<PK extends string, Context = unknown> = {
|
|
135
182
|
primaryKeyProperty: PK;
|
|
136
|
-
|
|
137
|
-
|
|
183
|
+
changes: SyncChange<PK, PropertySchema<PK>>[];
|
|
184
|
+
hasMore: boolean;
|
|
138
185
|
nextContext?: Context;
|
|
139
|
-
|
|
186
|
+
mode?: SyncMode;
|
|
140
187
|
};
|
|
141
188
|
|
|
189
|
+
export type SyncCapability = ReturnType<typeof createSyncCapability>;
|
|
190
|
+
|
|
142
191
|
/**
|
|
143
192
|
* Creates a special handler for syncing third-party data to a collection.
|
|
144
193
|
*
|
|
@@ -146,17 +195,18 @@ export type SyncHandlerResult<PK extends string, Context = unknown> = {
|
|
|
146
195
|
* @returns A handler function that executes the sync function, and passes data
|
|
147
196
|
* needed to complete the sync back to the platform.
|
|
148
197
|
*/
|
|
149
|
-
export function
|
|
198
|
+
export function createSyncCapability<
|
|
150
199
|
PK extends string,
|
|
151
200
|
S extends Schema<PK>,
|
|
152
201
|
Context = unknown,
|
|
153
|
-
>(syncConfiguration: SyncConfiguration<PK, S, Context>) {
|
|
202
|
+
>(key: string, syncConfiguration: SyncConfiguration<PK, S, Context>) {
|
|
154
203
|
return {
|
|
155
|
-
_tag: "sync",
|
|
204
|
+
_tag: "sync" as const,
|
|
205
|
+
key,
|
|
156
206
|
config: {
|
|
157
207
|
primaryKeyProperty: syncConfiguration.primaryKeyProperty,
|
|
158
208
|
schema: syncConfiguration.schema,
|
|
159
|
-
|
|
209
|
+
mode: syncConfiguration.mode,
|
|
160
210
|
schedule: parseSchedule(syncConfiguration.schedule),
|
|
161
211
|
},
|
|
162
212
|
async handler(context?: Context) {
|
|
@@ -167,8 +217,8 @@ export function sync<
|
|
|
167
217
|
});
|
|
168
218
|
|
|
169
219
|
const result = {
|
|
170
|
-
|
|
171
|
-
|
|
220
|
+
changes: executionResult.changes,
|
|
221
|
+
hasMore: executionResult.hasMore,
|
|
172
222
|
nextContext: executionResult.nextContext,
|
|
173
223
|
};
|
|
174
224
|
|
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
vi,
|
|
9
9
|
} from "vitest";
|
|
10
10
|
import {
|
|
11
|
+
createToolCapability,
|
|
11
12
|
InvalidToolInputError,
|
|
12
13
|
InvalidToolOutputError,
|
|
13
14
|
ToolExecutionError,
|
|
14
|
-
tool,
|
|
15
15
|
} from "./tool.js";
|
|
16
16
|
|
|
17
|
-
describe("tool", () => {
|
|
17
|
+
describe("Worker.tool", () => {
|
|
18
18
|
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
19
19
|
|
|
20
20
|
beforeEach(() => {
|
|
@@ -28,28 +28,28 @@ describe("tool", () => {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
it("sync execution", async () => {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
const capability = createToolCapability<{ name: string }, string>(
|
|
32
|
+
"sayHello",
|
|
33
|
+
{
|
|
34
|
+
title: "Say Hello",
|
|
35
|
+
description: "Greet a user",
|
|
36
|
+
schema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
name: { type: "string" },
|
|
40
|
+
},
|
|
41
|
+
required: ["name"],
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
},
|
|
44
|
+
execute: (input: { name: string }) => {
|
|
45
|
+
return `Hello, ${input.name}!`;
|
|
38
46
|
},
|
|
39
|
-
required: ["name"],
|
|
40
|
-
additionalProperties: false,
|
|
41
|
-
},
|
|
42
|
-
execute: (input) => {
|
|
43
|
-
return `Hello, ${input.name}!`;
|
|
44
47
|
},
|
|
45
|
-
|
|
48
|
+
);
|
|
46
49
|
|
|
47
|
-
const result = await
|
|
50
|
+
const result = await capability.handler({ name: "Alice" });
|
|
48
51
|
|
|
49
|
-
expect(result
|
|
50
|
-
if (result._tag === "success") {
|
|
51
|
-
expect(result.value).toBe("Hello, Alice!");
|
|
52
|
-
}
|
|
52
|
+
expect(result).toEqual({ _tag: "success", value: "Hello, Alice!" });
|
|
53
53
|
|
|
54
54
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
55
55
|
`\n<output>"Hello, Alice!"</output>\n`,
|
|
@@ -57,30 +57,33 @@ describe("tool", () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
it("async execution", async () => {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
const capability = createToolCapability<{ id: number }, { data: string }>(
|
|
61
|
+
"fetchData",
|
|
62
|
+
{
|
|
63
|
+
title: "Fetch Data",
|
|
64
|
+
description: "Fetch data asynchronously",
|
|
65
|
+
schema: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
id: { type: "number" },
|
|
69
|
+
},
|
|
70
|
+
required: ["id"],
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
},
|
|
73
|
+
execute: async (input: { id: number }) => {
|
|
74
|
+
// Simulate async operation
|
|
75
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
76
|
+
return { data: `Data for ID ${input.id}` };
|
|
67
77
|
},
|
|
68
|
-
required: ["id"],
|
|
69
|
-
additionalProperties: false,
|
|
70
|
-
},
|
|
71
|
-
execute: async (input) => {
|
|
72
|
-
// Simulate async operation
|
|
73
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
74
|
-
return { data: `Data for ID ${input.id}` };
|
|
75
78
|
},
|
|
76
|
-
|
|
79
|
+
);
|
|
77
80
|
|
|
78
|
-
const result = await
|
|
81
|
+
const result = await capability.handler({ id: 42 });
|
|
79
82
|
|
|
80
|
-
expect(result
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
83
|
+
expect(result).toEqual({
|
|
84
|
+
_tag: "success",
|
|
85
|
+
value: { data: "Data for ID 42" },
|
|
86
|
+
});
|
|
84
87
|
|
|
85
88
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
86
89
|
`\n<output>{"data":"Data for ID 42"}</output>\n`,
|
|
@@ -88,27 +91,31 @@ describe("tool", () => {
|
|
|
88
91
|
});
|
|
89
92
|
|
|
90
93
|
it("execution error", async () => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
const capability = createToolCapability<Record<string, never>, string>(
|
|
95
|
+
"throwError",
|
|
96
|
+
{
|
|
97
|
+
title: "Throw Error",
|
|
98
|
+
description: "Throws an error",
|
|
99
|
+
schema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {},
|
|
102
|
+
required: [],
|
|
103
|
+
additionalProperties: false,
|
|
104
|
+
},
|
|
105
|
+
execute: () => {
|
|
106
|
+
throw new Error("Something went wrong");
|
|
107
|
+
},
|
|
102
108
|
},
|
|
103
|
-
|
|
109
|
+
);
|
|
104
110
|
|
|
105
|
-
const result = await
|
|
111
|
+
const result = (await capability.handler({})) as {
|
|
112
|
+
_tag: "error";
|
|
113
|
+
error: ToolExecutionError;
|
|
114
|
+
};
|
|
106
115
|
|
|
107
116
|
expect(result._tag).toBe("error");
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
expect(result.error.message).toBe("Something went wrong");
|
|
111
|
-
}
|
|
117
|
+
expect(result.error).toBeInstanceOf(ToolExecutionError);
|
|
118
|
+
expect(result.error.message).toBe("Something went wrong");
|
|
112
119
|
|
|
113
120
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
114
121
|
`\n<output>{"_tag":"error","error":{"name":"ToolExecutionError","message":"Something went wrong"}}</output>\n`,
|
|
@@ -116,29 +123,33 @@ describe("tool", () => {
|
|
|
116
123
|
});
|
|
117
124
|
|
|
118
125
|
it("invalid input", async () => {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
const capability = createToolCapability<{ name: string }, string>(
|
|
127
|
+
"sayHelloStrict",
|
|
128
|
+
{
|
|
129
|
+
title: "Say Hello",
|
|
130
|
+
description: "Requires a name property",
|
|
131
|
+
schema: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
name: { type: "string" },
|
|
135
|
+
},
|
|
136
|
+
required: ["name"],
|
|
137
|
+
additionalProperties: false,
|
|
138
|
+
},
|
|
139
|
+
execute: (input: { name: string }) => {
|
|
140
|
+
return `Hello, ${input.name}!`;
|
|
126
141
|
},
|
|
127
|
-
required: ["name"],
|
|
128
|
-
additionalProperties: false,
|
|
129
|
-
},
|
|
130
|
-
execute: (input) => {
|
|
131
|
-
return `Hello, ${input.name}!`;
|
|
132
142
|
},
|
|
133
|
-
|
|
143
|
+
);
|
|
134
144
|
|
|
135
|
-
const result = await
|
|
145
|
+
const result = (await capability.handler({ age: 25 })) as {
|
|
146
|
+
_tag: "error";
|
|
147
|
+
error: InvalidToolInputError;
|
|
148
|
+
};
|
|
136
149
|
|
|
137
150
|
expect(result._tag).toBe("error");
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
expect(result.error.message).toContain("name");
|
|
141
|
-
}
|
|
151
|
+
expect(result.error).toBeInstanceOf(InvalidToolInputError);
|
|
152
|
+
expect(result.error.message).toContain("name");
|
|
142
153
|
|
|
143
154
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
144
155
|
expect.stringContaining(
|
|
@@ -148,7 +159,10 @@ describe("tool", () => {
|
|
|
148
159
|
});
|
|
149
160
|
|
|
150
161
|
it("invalid output", async () => {
|
|
151
|
-
const
|
|
162
|
+
const capability = createToolCapability<
|
|
163
|
+
Record<string, never>,
|
|
164
|
+
{ result: string }
|
|
165
|
+
>("invalidOutput", {
|
|
152
166
|
title: "Return Invalid Output",
|
|
153
167
|
description: "Returns output that doesn't match schema",
|
|
154
168
|
schema: {
|
|
@@ -171,13 +185,14 @@ describe("tool", () => {
|
|
|
171
185
|
},
|
|
172
186
|
});
|
|
173
187
|
|
|
174
|
-
const result = await
|
|
188
|
+
const result = (await capability.handler({})) as {
|
|
189
|
+
_tag: "error";
|
|
190
|
+
error: InvalidToolOutputError;
|
|
191
|
+
};
|
|
175
192
|
|
|
176
193
|
expect(result._tag).toBe("error");
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
expect(result.error.message).toContain("result");
|
|
180
|
-
}
|
|
194
|
+
expect(result.error).toBeInstanceOf(InvalidToolOutputError);
|
|
195
|
+
expect(result.error.message).toContain("result");
|
|
181
196
|
|
|
182
197
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
183
198
|
expect.stringContaining(
|
|
@@ -187,10 +202,10 @@ describe("tool", () => {
|
|
|
187
202
|
});
|
|
188
203
|
|
|
189
204
|
it("invalid output with custom output schema", async () => {
|
|
190
|
-
const
|
|
205
|
+
const capability = createToolCapability<
|
|
191
206
|
{ value: number },
|
|
192
207
|
{ doubled: number; message: string }
|
|
193
|
-
>({
|
|
208
|
+
>("customOutput", {
|
|
194
209
|
title: "Custom Output",
|
|
195
210
|
description: "Has custom output schema",
|
|
196
211
|
schema: {
|
|
@@ -211,20 +226,21 @@ describe("tool", () => {
|
|
|
211
226
|
additionalProperties: false,
|
|
212
227
|
},
|
|
213
228
|
// @ts-expect-error Testing invalid output - intentionally missing message property
|
|
214
|
-
execute: (input) => {
|
|
229
|
+
execute: (input: { value: number }) => {
|
|
215
230
|
return {
|
|
216
231
|
doubled: input.value * 2,
|
|
217
232
|
};
|
|
218
233
|
},
|
|
219
234
|
});
|
|
220
235
|
|
|
221
|
-
const result = await
|
|
236
|
+
const result = (await capability.handler({ value: 5 })) as {
|
|
237
|
+
_tag: "error";
|
|
238
|
+
error: InvalidToolOutputError;
|
|
239
|
+
};
|
|
222
240
|
|
|
223
241
|
expect(result._tag).toBe("error");
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
expect(result.error.message).toContain("message");
|
|
227
|
-
}
|
|
242
|
+
expect(result.error).toBeInstanceOf(InvalidToolOutputError);
|
|
243
|
+
expect(result.error.message).toContain("message");
|
|
228
244
|
|
|
229
245
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
230
246
|
expect.stringContaining(
|
package/src/capabilities/tool.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { Ajv, type JSONSchemaType } from "ajv";
|
|
2
|
-
|
|
3
|
-
type JSONValue =
|
|
4
|
-
| string
|
|
5
|
-
| number
|
|
6
|
-
| boolean
|
|
7
|
-
| null
|
|
8
|
-
| JSONValue[]
|
|
9
|
-
| { [key: string]: JSONValue };
|
|
2
|
+
import type { JSONValue } from "../types.js";
|
|
10
3
|
|
|
11
4
|
export interface ToolConfiguration<
|
|
12
5
|
I extends JSONValue,
|
|
@@ -70,34 +63,21 @@ export class ToolExecutionError extends Error {
|
|
|
70
63
|
}
|
|
71
64
|
}
|
|
72
65
|
|
|
66
|
+
export type ToolCapability<
|
|
67
|
+
I extends JSONValue,
|
|
68
|
+
O extends JSONValue = JSONValue,
|
|
69
|
+
> = ReturnType<typeof createToolCapability<I, O>>;
|
|
70
|
+
|
|
73
71
|
/**
|
|
74
72
|
* Creates a capability definition for a tool to be used by an agent.
|
|
75
73
|
*
|
|
76
|
-
* Example:
|
|
77
|
-
*
|
|
78
|
-
* ```ts
|
|
79
|
-
* tool<{ name: string }>({
|
|
80
|
-
* title: "Say Hello",
|
|
81
|
-
* description: "Say hello to the user",
|
|
82
|
-
* schema: {
|
|
83
|
-
* type: "object",
|
|
84
|
-
* properties: {
|
|
85
|
-
* name: { type: "string" },
|
|
86
|
-
* },
|
|
87
|
-
* required: ["name"],
|
|
88
|
-
* },
|
|
89
|
-
* execute: ({ name }) => {
|
|
90
|
-
* return `Hello, ${name}!`;
|
|
91
|
-
* },
|
|
92
|
-
* })
|
|
93
|
-
* ```
|
|
94
|
-
*
|
|
95
74
|
* @param config - The configuration for the tool.
|
|
96
75
|
* @returns A capability definition for the tool.
|
|
97
76
|
*/
|
|
98
|
-
export function
|
|
99
|
-
|
|
100
|
-
|
|
77
|
+
export function createToolCapability<
|
|
78
|
+
I extends JSONValue,
|
|
79
|
+
O extends JSONValue = JSONValue,
|
|
80
|
+
>(key: string, config: ToolConfiguration<I, O>) {
|
|
101
81
|
const ajv = new Ajv();
|
|
102
82
|
const validateInput = ajv.compile(config.schema);
|
|
103
83
|
const validateOutput = config.outputSchema
|
|
@@ -105,7 +85,8 @@ export function tool<I extends JSONValue, O extends JSONValue = JSONValue>(
|
|
|
105
85
|
: null;
|
|
106
86
|
|
|
107
87
|
return {
|
|
108
|
-
_tag: "tool",
|
|
88
|
+
_tag: "tool" as const,
|
|
89
|
+
key,
|
|
109
90
|
config: {
|
|
110
91
|
title: config.title,
|
|
111
92
|
description: config.description,
|
package/src/index.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
export { emojiIcon, imageIcon, notionIcon, place } from "./builder.js";
|
|
2
2
|
export type {
|
|
3
|
+
AutomationCapability,
|
|
3
4
|
AutomationConfiguration,
|
|
4
5
|
AutomationContext,
|
|
5
6
|
PageObjectResponse,
|
|
6
7
|
} from "./capabilities/automation.js";
|
|
7
|
-
export { automation } from "./capabilities/automation.js";
|
|
8
8
|
export type {
|
|
9
9
|
NotionManagedOAuthConfiguration,
|
|
10
|
+
OAuthCapability,
|
|
10
11
|
OAuthConfiguration,
|
|
11
12
|
UserManagedOAuthConfiguration,
|
|
12
13
|
} from "./capabilities/oauth.js";
|
|
13
|
-
export { oauth } from "./capabilities/oauth.js";
|
|
14
14
|
export type {
|
|
15
|
+
SyncCapability,
|
|
16
|
+
SyncChange,
|
|
17
|
+
SyncChangeDelete,
|
|
18
|
+
SyncChangeUpsert,
|
|
15
19
|
SyncConfiguration,
|
|
16
20
|
SyncExecutionResult,
|
|
17
|
-
|
|
21
|
+
SyncMode,
|
|
18
22
|
} from "./capabilities/sync.js";
|
|
19
|
-
export {
|
|
20
|
-
export { tool } from "./capabilities/tool.js";
|
|
23
|
+
export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
|
|
21
24
|
export type {
|
|
22
25
|
Icon,
|
|
23
26
|
ImageIcon,
|
|
@@ -26,3 +29,4 @@ export type {
|
|
|
26
29
|
PlaceValue,
|
|
27
30
|
Schedule,
|
|
28
31
|
} from "./types.js";
|
|
32
|
+
export { Worker } from "./worker.js";
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A JSON value is a string, number, boolean, null, array, or object.
|
|
3
|
+
*/
|
|
4
|
+
export type JSONValue =
|
|
5
|
+
| string
|
|
6
|
+
| number
|
|
7
|
+
| boolean
|
|
8
|
+
| null
|
|
9
|
+
| JSONValue[]
|
|
10
|
+
| { [key: string]: JSONValue };
|
|
11
|
+
|
|
1
12
|
/**
|
|
2
13
|
* A text token is a tuple where the first element is the text content
|
|
3
14
|
* and optional subsequent elements are formatting annotations.
|