@slashfi/agents-sdk 0.5.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/agent-definitions/auth.d.ts.map +1 -1
- package/dist/agent-definitions/auth.js +16 -3
- package/dist/agent-definitions/auth.js.map +1 -1
- 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.map +1 -1
- package/dist/agent-definitions/secrets.js +8 -25
- package/dist/agent-definitions/secrets.js.map +1 -1
- 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.map +1 -1
- package/dist/crypto.js.map +1 -1
- 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 +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- 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 +477 -26
- 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/agent-definitions/auth.ts +37 -14
- package/src/agent-definitions/integrations.ts +1209 -0
- package/src/agent-definitions/secrets.ts +24 -30
- package/src/agent-definitions/users.ts +533 -0
- package/src/crypto.ts +3 -1
- package/src/define.ts +8 -0
- package/src/index.ts +56 -3
- package/src/jwt.ts +7 -5
- package/src/server.ts +565 -33
- package/src/slack-oauth.ts +66 -0
- package/src/types.ts +83 -0
- package/src/web-pages.ts +178 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - AES-256-GCM encryption via crypto.ts
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { decryptSecret, encryptSecret } from "../crypto.js";
|
|
12
12
|
import { defineAgent, defineTool } from "../define.js";
|
|
13
13
|
import type { AgentDefinition, ToolContext, ToolDefinition } from "../types.js";
|
|
14
14
|
|
|
@@ -50,7 +50,8 @@ export function makeSecretRef(id: string): string {
|
|
|
50
50
|
function randomSecretId(): string {
|
|
51
51
|
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
52
52
|
let id = "";
|
|
53
|
-
for (let i = 0; i < 24; i++)
|
|
53
|
+
for (let i = 0; i < 24; i++)
|
|
54
|
+
id += chars[Math.floor(Math.random() * chars.length)];
|
|
54
55
|
return id;
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -104,7 +105,8 @@ export function createSecretsAgent(
|
|
|
104
105
|
|
|
105
106
|
const storeSecretTool = defineTool({
|
|
106
107
|
name: "store",
|
|
107
|
-
description:
|
|
108
|
+
description:
|
|
109
|
+
"Store secret values. Returns secret:<id> refs for each value.",
|
|
108
110
|
visibility: "internal" as const,
|
|
109
111
|
inputSchema: {
|
|
110
112
|
type: "object" as const,
|
|
@@ -117,7 +119,10 @@ export function createSecretsAgent(
|
|
|
117
119
|
},
|
|
118
120
|
required: ["secrets"],
|
|
119
121
|
},
|
|
120
|
-
execute: async (
|
|
122
|
+
execute: async (
|
|
123
|
+
input: { secrets: Record<string, string> },
|
|
124
|
+
ctx: ToolContext,
|
|
125
|
+
) => {
|
|
121
126
|
const ownerId = ctx.callerId ?? "anonymous";
|
|
122
127
|
const refs: Record<string, string> = {};
|
|
123
128
|
for (const [key, value] of Object.entries(input.secrets)) {
|
|
@@ -130,27 +135,6 @@ export function createSecretsAgent(
|
|
|
130
135
|
},
|
|
131
136
|
});
|
|
132
137
|
|
|
133
|
-
const resolveSecretTool = defineTool({
|
|
134
|
-
name: "resolve",
|
|
135
|
-
description: "Resolve a secret ref to its value. Only accessible to the owner.",
|
|
136
|
-
visibility: "internal" as const,
|
|
137
|
-
inputSchema: {
|
|
138
|
-
type: "object" as const,
|
|
139
|
-
properties: {
|
|
140
|
-
ref: { type: "string" as const, description: "Secret ref (secret:xxx)" },
|
|
141
|
-
},
|
|
142
|
-
required: ["ref"],
|
|
143
|
-
},
|
|
144
|
-
execute: async (input: { ref: string }, ctx: ToolContext) => {
|
|
145
|
-
const ownerId = ctx.callerId ?? "anonymous";
|
|
146
|
-
const id = getSecretId(input.ref);
|
|
147
|
-
const value = await store.resolve(id, ownerId);
|
|
148
|
-
if (!value) throw new Error("Secret not found or unauthorized");
|
|
149
|
-
// Return wrapped so actor can handle it
|
|
150
|
-
return { value: { $agent_type: "secret", value } };
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
|
|
154
138
|
const revokeSecretTool = defineTool({
|
|
155
139
|
name: "revoke",
|
|
156
140
|
description: "Delete a stored secret.",
|
|
@@ -172,13 +156,17 @@ export function createSecretsAgent(
|
|
|
172
156
|
|
|
173
157
|
return defineAgent({
|
|
174
158
|
path: "@secrets",
|
|
175
|
-
entrypoint:
|
|
159
|
+
entrypoint:
|
|
160
|
+
"Secret storage agent. Stores, resolves, and manages encrypted secrets.",
|
|
176
161
|
config: {
|
|
177
162
|
name: "Secrets",
|
|
178
163
|
description: "Encrypted secret storage and management",
|
|
179
164
|
visibility: "internal",
|
|
180
165
|
},
|
|
181
|
-
tools: [
|
|
166
|
+
tools: [
|
|
167
|
+
storeSecretTool,
|
|
168
|
+
revokeSecretTool,
|
|
169
|
+
] as ToolDefinition<ToolContext>[],
|
|
182
170
|
});
|
|
183
171
|
}
|
|
184
172
|
|
|
@@ -200,7 +188,10 @@ export async function processSecretParams(
|
|
|
200
188
|
schema: { properties?: Record<string, SchemaProperty> } | undefined,
|
|
201
189
|
secretStore: SecretStore,
|
|
202
190
|
ownerId: string,
|
|
203
|
-
): Promise<{
|
|
191
|
+
): Promise<{
|
|
192
|
+
resolved: Record<string, unknown>;
|
|
193
|
+
redacted: Record<string, unknown>;
|
|
194
|
+
}> {
|
|
204
195
|
const resolved: Record<string, unknown> = { ...params };
|
|
205
196
|
const redacted: Record<string, unknown> = { ...params };
|
|
206
197
|
|
|
@@ -210,7 +201,11 @@ export async function processSecretParams(
|
|
|
210
201
|
const value = params[key];
|
|
211
202
|
if (value === undefined || value === null) continue;
|
|
212
203
|
|
|
213
|
-
if (
|
|
204
|
+
if (
|
|
205
|
+
schemaProp.type === "object" &&
|
|
206
|
+
typeof value === "object" &&
|
|
207
|
+
!Array.isArray(value)
|
|
208
|
+
) {
|
|
214
209
|
const nested = await processSecretParams(
|
|
215
210
|
value as Record<string, unknown>,
|
|
216
211
|
schemaProp,
|
|
@@ -239,7 +234,6 @@ export async function processSecretParams(
|
|
|
239
234
|
const id = await secretStore.store(value, ownerId);
|
|
240
235
|
resolved[key] = value;
|
|
241
236
|
redacted[key] = makeSecretRef(id);
|
|
242
|
-
continue;
|
|
243
237
|
}
|
|
244
238
|
}
|
|
245
239
|
|
|
@@ -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
CHANGED
|
@@ -43,7 +43,9 @@ export async function encryptSecret(
|
|
|
43
43
|
key,
|
|
44
44
|
encoder.encode(plaintext),
|
|
45
45
|
);
|
|
46
|
-
const combined = new Uint8Array(
|
|
46
|
+
const combined = new Uint8Array(
|
|
47
|
+
iv.length + new Uint8Array(ciphertext).length,
|
|
48
|
+
);
|
|
47
49
|
combined.set(iv);
|
|
48
50
|
combined.set(new Uint8Array(ciphertext), iv.length);
|
|
49
51
|
return btoa(String.fromCharCode(...combined));
|
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
|
}
|