@slashfi/agents-sdk 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{auth.d.ts → agent-definitions/auth.d.ts} +36 -2
- package/dist/agent-definitions/auth.d.ts.map +1 -0
- package/dist/{auth.js → agent-definitions/auth.js} +69 -8
- package/dist/agent-definitions/auth.js.map +1 -0
- package/dist/agent-definitions/integrations.d.ts +162 -0
- package/dist/agent-definitions/integrations.d.ts.map +1 -0
- package/dist/agent-definitions/integrations.js +861 -0
- package/dist/agent-definitions/integrations.js.map +1 -0
- package/dist/agent-definitions/secrets.d.ts +51 -0
- package/dist/agent-definitions/secrets.d.ts.map +1 -0
- package/dist/agent-definitions/secrets.js +165 -0
- package/dist/agent-definitions/secrets.js.map +1 -0
- package/dist/agent-definitions/users.d.ts +80 -0
- package/dist/agent-definitions/users.d.ts.map +1 -0
- package/dist/agent-definitions/users.js +397 -0
- package/dist/agent-definitions/users.js.map +1 -0
- package/dist/crypto.d.ts +14 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +40 -0
- package/dist/crypto.js.map +1 -0
- package/dist/define.d.ts +6 -1
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +1 -0
- package/dist/define.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts +2 -0
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js.map +1 -1
- package/dist/server.d.ts +28 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +478 -27
- package/dist/server.js.map +1 -1
- package/dist/slack-oauth.d.ts +27 -0
- package/dist/slack-oauth.d.ts.map +1 -0
- package/dist/slack-oauth.js +48 -0
- package/dist/slack-oauth.js.map +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web-pages.d.ts +8 -0
- package/dist/web-pages.d.ts.map +1 -0
- package/dist/web-pages.js +169 -0
- package/dist/web-pages.js.map +1 -0
- package/package.json +2 -1
- package/src/{auth.ts → agent-definitions/auth.ts} +134 -15
- package/src/agent-definitions/integrations.ts +1209 -0
- package/src/agent-definitions/secrets.ts +241 -0
- package/src/agent-definitions/users.ts +533 -0
- package/src/crypto.ts +71 -0
- package/src/define.ts +8 -0
- package/src/index.ts +62 -4
- package/src/jwt.ts +9 -5
- package/src/server.ts +567 -35
- package/src/slack-oauth.ts +66 -0
- package/src/types.ts +83 -0
- package/src/web-pages.ts +178 -0
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/secrets.d.ts +0 -44
- package/dist/secrets.d.ts.map +0 -1
- package/dist/secrets.js +0 -106
- package/dist/secrets.js.map +0 -1
- package/src/secrets.ts +0 -154
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Users Agent (@users)
|
|
3
|
+
*
|
|
4
|
+
* Built-in agent for user and user identity management.
|
|
5
|
+
* Provides user CRUD and identity linking (OAuth providers, SSO, etc.).
|
|
6
|
+
*
|
|
7
|
+
* A User is a human entity. A UserIdentity links a user to an external
|
|
8
|
+
* identity provider (e.g. Google, Slack, GitHub) — one user can have
|
|
9
|
+
* many identities.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { createAgentRegistry, createUsersAgent } from '@slashfi/agents-sdk';
|
|
14
|
+
*
|
|
15
|
+
* const registry = createAgentRegistry();
|
|
16
|
+
* registry.register(createUsersAgent({
|
|
17
|
+
* store: myUserStore,
|
|
18
|
+
* }));
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { defineAgent, defineTool } from "../define.js";
|
|
23
|
+
import type { AgentDefinition, ToolContext, ToolDefinition } from "../types.js";
|
|
24
|
+
|
|
25
|
+
// ============================================
|
|
26
|
+
// User Types
|
|
27
|
+
// ============================================
|
|
28
|
+
|
|
29
|
+
export interface User {
|
|
30
|
+
id: string;
|
|
31
|
+
tenantId: string;
|
|
32
|
+
email?: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
avatarUrl?: string;
|
|
35
|
+
metadata?: Record<string, unknown>;
|
|
36
|
+
createdAt: number;
|
|
37
|
+
updatedAt: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A link between a User and an external identity provider.
|
|
42
|
+
* One user can have many identities (e.g. Google + Slack + GitHub).
|
|
43
|
+
*/
|
|
44
|
+
export interface UserIdentity {
|
|
45
|
+
id: string;
|
|
46
|
+
userId: string;
|
|
47
|
+
provider: string;
|
|
48
|
+
/** External provider's user ID */
|
|
49
|
+
providerUserId: string;
|
|
50
|
+
/** Display info from the provider */
|
|
51
|
+
email?: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
avatarUrl?: string;
|
|
54
|
+
/** OAuth tokens (encrypted at rest in the store) */
|
|
55
|
+
accessToken?: string;
|
|
56
|
+
refreshToken?: string;
|
|
57
|
+
expiresAt?: number;
|
|
58
|
+
tokenType?: string;
|
|
59
|
+
scopes?: string[];
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
connectedAt: number;
|
|
62
|
+
updatedAt: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ============================================
|
|
66
|
+
// User Store Interface
|
|
67
|
+
// ============================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Pluggable storage backend for users and identities.
|
|
71
|
+
*/
|
|
72
|
+
export interface UserStore {
|
|
73
|
+
// Users
|
|
74
|
+
createUser(user: Omit<User, "createdAt" | "updatedAt">): Promise<User>;
|
|
75
|
+
getUser(userId: string): Promise<User | null>;
|
|
76
|
+
getUserByEmail(tenantId: string, email: string): Promise<User | null>;
|
|
77
|
+
listUsers(tenantId: string): Promise<User[]>;
|
|
78
|
+
updateUser(
|
|
79
|
+
userId: string,
|
|
80
|
+
updates: Partial<Pick<User, "email" | "name" | "avatarUrl" | "metadata">>,
|
|
81
|
+
): Promise<User | null>;
|
|
82
|
+
deleteUser(userId: string): Promise<boolean>;
|
|
83
|
+
|
|
84
|
+
// Identities
|
|
85
|
+
createIdentity(
|
|
86
|
+
identity: Omit<UserIdentity, "connectedAt" | "updatedAt">,
|
|
87
|
+
): Promise<UserIdentity>;
|
|
88
|
+
getIdentity(identityId: string): Promise<UserIdentity | null>;
|
|
89
|
+
getIdentityByProvider(
|
|
90
|
+
userId: string,
|
|
91
|
+
provider: string,
|
|
92
|
+
): Promise<UserIdentity | null>;
|
|
93
|
+
findIdentityByProviderUserId(
|
|
94
|
+
provider: string,
|
|
95
|
+
providerUserId: string,
|
|
96
|
+
): Promise<UserIdentity | null>;
|
|
97
|
+
listIdentities(userId: string): Promise<UserIdentity[]>;
|
|
98
|
+
updateIdentity(
|
|
99
|
+
identityId: string,
|
|
100
|
+
updates: Partial<
|
|
101
|
+
Pick<
|
|
102
|
+
UserIdentity,
|
|
103
|
+
| "accessToken"
|
|
104
|
+
| "refreshToken"
|
|
105
|
+
| "expiresAt"
|
|
106
|
+
| "email"
|
|
107
|
+
| "name"
|
|
108
|
+
| "avatarUrl"
|
|
109
|
+
| "metadata"
|
|
110
|
+
>
|
|
111
|
+
>,
|
|
112
|
+
): Promise<UserIdentity | null>;
|
|
113
|
+
deleteIdentity(identityId: string): Promise<boolean>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ============================================
|
|
117
|
+
// In-Memory User Store
|
|
118
|
+
// ============================================
|
|
119
|
+
|
|
120
|
+
function generateId(prefix: string): string {
|
|
121
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
122
|
+
let id = prefix;
|
|
123
|
+
for (let i = 0; i < 20; i++) {
|
|
124
|
+
id += chars[Math.floor(Math.random() * chars.length)];
|
|
125
|
+
}
|
|
126
|
+
return id;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createInMemoryUserStore(): UserStore {
|
|
130
|
+
const users = new Map<string, User>();
|
|
131
|
+
const identities = new Map<string, UserIdentity>();
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
async createUser(input) {
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
const user: User = { ...input, createdAt: now, updatedAt: now };
|
|
137
|
+
users.set(user.id, user);
|
|
138
|
+
return user;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
async getUser(userId) {
|
|
142
|
+
return users.get(userId) ?? null;
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
async getUserByEmail(tenantId, email) {
|
|
146
|
+
for (const u of users.values()) {
|
|
147
|
+
if (u.tenantId === tenantId && u.email === email) return u;
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
async listUsers(tenantId) {
|
|
153
|
+
return Array.from(users.values()).filter((u) => u.tenantId === tenantId);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async updateUser(userId, updates) {
|
|
157
|
+
const user = users.get(userId);
|
|
158
|
+
if (!user) return null;
|
|
159
|
+
const updated = { ...user, ...updates, updatedAt: Date.now() };
|
|
160
|
+
users.set(userId, updated);
|
|
161
|
+
return updated;
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
async deleteUser(userId) {
|
|
165
|
+
// Delete identities too
|
|
166
|
+
for (const [id, identity] of identities) {
|
|
167
|
+
if (identity.userId === userId) identities.delete(id);
|
|
168
|
+
}
|
|
169
|
+
return users.delete(userId);
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
async createIdentity(input) {
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
const identity: UserIdentity = {
|
|
175
|
+
...input,
|
|
176
|
+
connectedAt: now,
|
|
177
|
+
updatedAt: now,
|
|
178
|
+
};
|
|
179
|
+
identities.set(identity.id, identity);
|
|
180
|
+
return identity;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
async getIdentity(identityId) {
|
|
184
|
+
return identities.get(identityId) ?? null;
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
async getIdentityByProvider(userId, provider) {
|
|
188
|
+
for (const identity of identities.values()) {
|
|
189
|
+
if (identity.userId === userId && identity.provider === provider)
|
|
190
|
+
return identity;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
async findIdentityByProviderUserId(provider, providerUserId) {
|
|
196
|
+
for (const identity of identities.values()) {
|
|
197
|
+
if (
|
|
198
|
+
identity.provider === provider &&
|
|
199
|
+
identity.providerUserId === providerUserId
|
|
200
|
+
)
|
|
201
|
+
return identity;
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
async listIdentities(userId) {
|
|
207
|
+
return Array.from(identities.values()).filter((i) => i.userId === userId);
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
async updateIdentity(identityId, updates) {
|
|
211
|
+
const identity = identities.get(identityId);
|
|
212
|
+
if (!identity) return null;
|
|
213
|
+
const updated = { ...identity, ...updates, updatedAt: Date.now() };
|
|
214
|
+
identities.set(identityId, updated);
|
|
215
|
+
return updated;
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
async deleteIdentity(identityId) {
|
|
219
|
+
return identities.delete(identityId);
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ============================================
|
|
225
|
+
// Create Users Agent
|
|
226
|
+
// ============================================
|
|
227
|
+
|
|
228
|
+
export interface UsersAgentOptions {
|
|
229
|
+
/** User store backend */
|
|
230
|
+
store: UserStore;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
234
|
+
const { store } = options;
|
|
235
|
+
|
|
236
|
+
// ---- create_user ----
|
|
237
|
+
const createUserTool = defineTool({
|
|
238
|
+
name: "create_user",
|
|
239
|
+
description: "Create a new user.",
|
|
240
|
+
visibility: "public" as const,
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: "object" as const,
|
|
243
|
+
properties: {
|
|
244
|
+
id: {
|
|
245
|
+
type: "string",
|
|
246
|
+
description: "User ID (optional, auto-generated if omitted)",
|
|
247
|
+
},
|
|
248
|
+
tenantId: { type: "string", description: "Tenant ID" },
|
|
249
|
+
email: { type: "string", description: "Email address" },
|
|
250
|
+
name: { type: "string", description: "Display name" },
|
|
251
|
+
avatarUrl: { type: "string", description: "Avatar URL" },
|
|
252
|
+
metadata: { type: "object", description: "Additional metadata" },
|
|
253
|
+
},
|
|
254
|
+
required: ["tenantId"],
|
|
255
|
+
},
|
|
256
|
+
execute: async (input: any, __ctx: ToolContext) => {
|
|
257
|
+
const user = await store.createUser({
|
|
258
|
+
id: input.id ?? generateId("user_"),
|
|
259
|
+
tenantId: input.tenantId,
|
|
260
|
+
email: input.email,
|
|
261
|
+
name: input.name,
|
|
262
|
+
avatarUrl: input.avatarUrl,
|
|
263
|
+
metadata: input.metadata,
|
|
264
|
+
});
|
|
265
|
+
return { success: true, user };
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ---- get_user ----
|
|
270
|
+
const getUserTool = defineTool({
|
|
271
|
+
name: "get_user",
|
|
272
|
+
description: "Get a user by ID.",
|
|
273
|
+
visibility: "public" as const,
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: "object" as const,
|
|
276
|
+
properties: {
|
|
277
|
+
userId: { type: "string", description: "User ID" },
|
|
278
|
+
},
|
|
279
|
+
required: ["userId"],
|
|
280
|
+
},
|
|
281
|
+
execute: async (input: { userId: string }, __ctx: ToolContext) => {
|
|
282
|
+
const user = await store.getUser(input.userId);
|
|
283
|
+
if (!user) return { error: `User '${input.userId}' not found` };
|
|
284
|
+
return { user };
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// ---- list_users ----
|
|
289
|
+
const listUsersTool = defineTool({
|
|
290
|
+
name: "list_users",
|
|
291
|
+
description: "List users in a tenant.",
|
|
292
|
+
visibility: "public" as const,
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: "object" as const,
|
|
295
|
+
properties: {
|
|
296
|
+
tenantId: { type: "string", description: "Tenant ID" },
|
|
297
|
+
},
|
|
298
|
+
required: ["tenantId"],
|
|
299
|
+
},
|
|
300
|
+
execute: async (input: { tenantId: string }, __ctx: ToolContext) => {
|
|
301
|
+
const users = await store.listUsers(input.tenantId);
|
|
302
|
+
return { users };
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// ---- update_user ----
|
|
307
|
+
const updateUserTool = defineTool({
|
|
308
|
+
name: "update_user",
|
|
309
|
+
description: "Update a user's profile.",
|
|
310
|
+
visibility: "public" as const,
|
|
311
|
+
inputSchema: {
|
|
312
|
+
type: "object" as const,
|
|
313
|
+
properties: {
|
|
314
|
+
userId: { type: "string", description: "User ID" },
|
|
315
|
+
email: { type: "string", description: "New email" },
|
|
316
|
+
name: { type: "string", description: "New name" },
|
|
317
|
+
avatarUrl: { type: "string", description: "New avatar URL" },
|
|
318
|
+
metadata: { type: "object", description: "New metadata (merged)" },
|
|
319
|
+
},
|
|
320
|
+
required: ["userId"],
|
|
321
|
+
},
|
|
322
|
+
execute: async (input: any, __ctx: ToolContext) => {
|
|
323
|
+
const { userId, ...updates } = input;
|
|
324
|
+
const user = await store.updateUser(userId, updates);
|
|
325
|
+
if (!user) return { error: `User '${userId}' not found` };
|
|
326
|
+
return { success: true, user };
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// ---- link_identity ----
|
|
331
|
+
const linkIdentityTool = defineTool({
|
|
332
|
+
name: "link_identity",
|
|
333
|
+
description:
|
|
334
|
+
"Link an external identity (OAuth provider) to a user. " +
|
|
335
|
+
"Creates a UserIdentity record associating the user with a provider account.",
|
|
336
|
+
visibility: "public" as const,
|
|
337
|
+
inputSchema: {
|
|
338
|
+
type: "object" as const,
|
|
339
|
+
properties: {
|
|
340
|
+
userId: { type: "string", description: "User ID to link to" },
|
|
341
|
+
provider: {
|
|
342
|
+
type: "string",
|
|
343
|
+
description: "Provider name (e.g. 'google', 'slack', 'github')",
|
|
344
|
+
},
|
|
345
|
+
providerUserId: {
|
|
346
|
+
type: "string",
|
|
347
|
+
description: "The user's ID in the external provider",
|
|
348
|
+
},
|
|
349
|
+
email: { type: "string", description: "Email from the provider" },
|
|
350
|
+
name: { type: "string", description: "Name from the provider" },
|
|
351
|
+
avatarUrl: {
|
|
352
|
+
type: "string",
|
|
353
|
+
description: "Avatar URL from the provider",
|
|
354
|
+
},
|
|
355
|
+
accessToken: { type: "string", description: "OAuth access token" },
|
|
356
|
+
refreshToken: { type: "string", description: "OAuth refresh token" },
|
|
357
|
+
expiresAt: {
|
|
358
|
+
type: "number",
|
|
359
|
+
description: "Token expiry timestamp (ms)",
|
|
360
|
+
},
|
|
361
|
+
scopes: {
|
|
362
|
+
type: "array",
|
|
363
|
+
items: { type: "string" },
|
|
364
|
+
description: "Granted scopes",
|
|
365
|
+
},
|
|
366
|
+
metadata: {
|
|
367
|
+
type: "object",
|
|
368
|
+
description: "Additional provider-specific data",
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
required: ["userId", "provider", "providerUserId"],
|
|
372
|
+
},
|
|
373
|
+
execute: async (input: any, __ctx: ToolContext) => {
|
|
374
|
+
// Check if identity already exists
|
|
375
|
+
const existing = await store.getIdentityByProvider(
|
|
376
|
+
input.userId,
|
|
377
|
+
input.provider,
|
|
378
|
+
);
|
|
379
|
+
if (existing) {
|
|
380
|
+
// Update existing
|
|
381
|
+
const updated = await store.updateIdentity(existing.id, {
|
|
382
|
+
accessToken: input.accessToken,
|
|
383
|
+
refreshToken: input.refreshToken,
|
|
384
|
+
expiresAt: input.expiresAt,
|
|
385
|
+
email: input.email,
|
|
386
|
+
name: input.name,
|
|
387
|
+
avatarUrl: input.avatarUrl,
|
|
388
|
+
metadata: input.metadata,
|
|
389
|
+
});
|
|
390
|
+
return { success: true, identity: updated, updated: true };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const identity = await store.createIdentity({
|
|
394
|
+
id: generateId("ident_"),
|
|
395
|
+
userId: input.userId,
|
|
396
|
+
provider: input.provider,
|
|
397
|
+
providerUserId: input.providerUserId,
|
|
398
|
+
email: input.email,
|
|
399
|
+
name: input.name,
|
|
400
|
+
avatarUrl: input.avatarUrl,
|
|
401
|
+
accessToken: input.accessToken,
|
|
402
|
+
refreshToken: input.refreshToken,
|
|
403
|
+
expiresAt: input.expiresAt,
|
|
404
|
+
tokenType: input.tokenType,
|
|
405
|
+
scopes: input.scopes,
|
|
406
|
+
metadata: input.metadata,
|
|
407
|
+
});
|
|
408
|
+
return { success: true, identity, created: true };
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// ---- list_identities ----
|
|
413
|
+
const listIdentitiesTool = defineTool({
|
|
414
|
+
name: "list_identities",
|
|
415
|
+
description: "List all linked identities for a user.",
|
|
416
|
+
visibility: "public" as const,
|
|
417
|
+
inputSchema: {
|
|
418
|
+
type: "object" as const,
|
|
419
|
+
properties: {
|
|
420
|
+
userId: { type: "string", description: "User ID" },
|
|
421
|
+
},
|
|
422
|
+
required: ["userId"],
|
|
423
|
+
},
|
|
424
|
+
execute: async (input: { userId: string }, __ctx: ToolContext) => {
|
|
425
|
+
const identities = await store.listIdentities(input.userId);
|
|
426
|
+
return {
|
|
427
|
+
identities: identities.map((i) => ({
|
|
428
|
+
id: i.id,
|
|
429
|
+
provider: i.provider,
|
|
430
|
+
providerUserId: i.providerUserId,
|
|
431
|
+
email: i.email,
|
|
432
|
+
name: i.name,
|
|
433
|
+
connectedAt: i.connectedAt,
|
|
434
|
+
hasAccessToken: !!i.accessToken,
|
|
435
|
+
expiresAt: i.expiresAt,
|
|
436
|
+
})),
|
|
437
|
+
};
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ---- unlink_identity ----
|
|
442
|
+
const unlinkIdentityTool = defineTool({
|
|
443
|
+
name: "unlink_identity",
|
|
444
|
+
description: "Remove a linked identity from a user.",
|
|
445
|
+
visibility: "public" as const,
|
|
446
|
+
inputSchema: {
|
|
447
|
+
type: "object" as const,
|
|
448
|
+
properties: {
|
|
449
|
+
identityId: { type: "string", description: "Identity ID to unlink" },
|
|
450
|
+
},
|
|
451
|
+
required: ["identityId"],
|
|
452
|
+
},
|
|
453
|
+
execute: async (input: { identityId: string }, __ctx: ToolContext) => {
|
|
454
|
+
const deleted = await store.deleteIdentity(input.identityId);
|
|
455
|
+
return { success: deleted };
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// ---- resolve_identity ----
|
|
460
|
+
const resolveIdentityTool = defineTool({
|
|
461
|
+
name: "resolve_identity",
|
|
462
|
+
description:
|
|
463
|
+
"Find a user by their external provider identity. " +
|
|
464
|
+
"Useful for login flows — given a provider + providerUserId, find the linked user.",
|
|
465
|
+
visibility: "public" as const,
|
|
466
|
+
inputSchema: {
|
|
467
|
+
type: "object" as const,
|
|
468
|
+
properties: {
|
|
469
|
+
provider: {
|
|
470
|
+
type: "string",
|
|
471
|
+
description: "Provider name (e.g. 'google')",
|
|
472
|
+
},
|
|
473
|
+
providerUserId: {
|
|
474
|
+
type: "string",
|
|
475
|
+
description: "External user ID from the provider",
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
required: ["provider", "providerUserId"],
|
|
479
|
+
},
|
|
480
|
+
execute: async (
|
|
481
|
+
input: { provider: string; providerUserId: string },
|
|
482
|
+
__ctx: ToolContext,
|
|
483
|
+
) => {
|
|
484
|
+
const identity = await store.findIdentityByProviderUserId(
|
|
485
|
+
input.provider,
|
|
486
|
+
input.providerUserId,
|
|
487
|
+
);
|
|
488
|
+
if (!identity) return { found: false };
|
|
489
|
+
|
|
490
|
+
const user = await store.getUser(identity.userId);
|
|
491
|
+
return {
|
|
492
|
+
found: true,
|
|
493
|
+
user: user
|
|
494
|
+
? {
|
|
495
|
+
id: user.id,
|
|
496
|
+
email: user.email,
|
|
497
|
+
name: user.name,
|
|
498
|
+
tenantId: user.tenantId,
|
|
499
|
+
}
|
|
500
|
+
: null,
|
|
501
|
+
identity: {
|
|
502
|
+
id: identity.id,
|
|
503
|
+
provider: identity.provider,
|
|
504
|
+
providerUserId: identity.providerUserId,
|
|
505
|
+
email: identity.email,
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
return defineAgent({
|
|
512
|
+
path: "@users",
|
|
513
|
+
entrypoint:
|
|
514
|
+
"You are the users agent. You manage user accounts and their linked external identities " +
|
|
515
|
+
"(OAuth providers like Google, Slack, GitHub, etc.).",
|
|
516
|
+
config: {
|
|
517
|
+
name: "Users",
|
|
518
|
+
description: "User and identity management",
|
|
519
|
+
supportedActions: ["execute_tool", "describe_tools", "load"],
|
|
520
|
+
},
|
|
521
|
+
visibility: "public",
|
|
522
|
+
tools: [
|
|
523
|
+
createUserTool,
|
|
524
|
+
getUserTool,
|
|
525
|
+
listUsersTool,
|
|
526
|
+
updateUserTool,
|
|
527
|
+
linkIdentityTool,
|
|
528
|
+
listIdentitiesTool,
|
|
529
|
+
unlinkIdentityTool,
|
|
530
|
+
resolveIdentityTool,
|
|
531
|
+
] as ToolDefinition[],
|
|
532
|
+
});
|
|
533
|
+
}
|
package/src/crypto.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AES-256-GCM encryption utilities for the agents SDK.
|
|
3
|
+
* Uses Web Crypto API - no external dependencies.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const decoder = new TextDecoder();
|
|
8
|
+
|
|
9
|
+
async function deriveKey(secret: string): Promise<CryptoKey> {
|
|
10
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
11
|
+
"raw",
|
|
12
|
+
encoder.encode(secret),
|
|
13
|
+
"PBKDF2",
|
|
14
|
+
false,
|
|
15
|
+
["deriveKey"],
|
|
16
|
+
);
|
|
17
|
+
return crypto.subtle.deriveKey(
|
|
18
|
+
{
|
|
19
|
+
name: "PBKDF2",
|
|
20
|
+
salt: encoder.encode("agents-sdk-secrets-v1"),
|
|
21
|
+
iterations: 100000,
|
|
22
|
+
hash: "SHA-256",
|
|
23
|
+
},
|
|
24
|
+
keyMaterial,
|
|
25
|
+
{ name: "AES-GCM", length: 256 },
|
|
26
|
+
false,
|
|
27
|
+
["encrypt", "decrypt"],
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Encrypt a string with AES-256-GCM.
|
|
33
|
+
* Returns a base64-encoded string containing IV + ciphertext.
|
|
34
|
+
*/
|
|
35
|
+
export async function encryptSecret(
|
|
36
|
+
plaintext: string,
|
|
37
|
+
encryptionKey: string,
|
|
38
|
+
): Promise<string> {
|
|
39
|
+
const key = await deriveKey(encryptionKey);
|
|
40
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
41
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
42
|
+
{ name: "AES-GCM", iv },
|
|
43
|
+
key,
|
|
44
|
+
encoder.encode(plaintext),
|
|
45
|
+
);
|
|
46
|
+
const combined = new Uint8Array(
|
|
47
|
+
iv.length + new Uint8Array(ciphertext).length,
|
|
48
|
+
);
|
|
49
|
+
combined.set(iv);
|
|
50
|
+
combined.set(new Uint8Array(ciphertext), iv.length);
|
|
51
|
+
return btoa(String.fromCharCode(...combined));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Decrypt a string encrypted with encryptSecret.
|
|
56
|
+
*/
|
|
57
|
+
export async function decryptSecret(
|
|
58
|
+
encrypted: string,
|
|
59
|
+
encryptionKey: string,
|
|
60
|
+
): Promise<string> {
|
|
61
|
+
const key = await deriveKey(encryptionKey);
|
|
62
|
+
const combined = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0));
|
|
63
|
+
const iv = combined.slice(0, 12);
|
|
64
|
+
const ciphertext = combined.slice(12);
|
|
65
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
66
|
+
{ name: "AES-GCM", iv },
|
|
67
|
+
key,
|
|
68
|
+
ciphertext,
|
|
69
|
+
);
|
|
70
|
+
return decoder.decode(plaintext);
|
|
71
|
+
}
|
package/src/define.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
AgentConfig,
|
|
9
9
|
AgentDefinition,
|
|
10
10
|
AgentRuntime,
|
|
11
|
+
IntegrationMethods,
|
|
11
12
|
JsonSchema,
|
|
12
13
|
ToolContext,
|
|
13
14
|
ToolDefinition,
|
|
@@ -112,6 +113,12 @@ export interface DefineAgentOptions<
|
|
|
112
113
|
|
|
113
114
|
/** Explicit allowed callers */
|
|
114
115
|
allowedCallers?: string[];
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Integration method callbacks.
|
|
119
|
+
* Implement these when this agent acts as an integration.
|
|
120
|
+
*/
|
|
121
|
+
integrationMethods?: IntegrationMethods;
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
/**
|
|
@@ -149,5 +156,6 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
|
|
|
149
156
|
runtime: options.runtime,
|
|
150
157
|
visibility: options.visibility,
|
|
151
158
|
allowedCallers: options.allowedCallers,
|
|
159
|
+
integrationMethods: options.integrationMethods,
|
|
152
160
|
};
|
|
153
161
|
}
|