@slashfi/agents-sdk 0.8.0 → 0.9.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 +17 -0
- package/dist/agent-definitions/auth.d.ts.map +1 -1
- package/dist/agent-definitions/auth.js +135 -1
- package/dist/agent-definitions/auth.js.map +1 -1
- package/dist/agent-definitions/integrations.d.ts +19 -0
- package/dist/agent-definitions/integrations.d.ts.map +1 -1
- package/dist/agent-definitions/integrations.js +218 -5
- package/dist/agent-definitions/integrations.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/integration-interface.d.ts +37 -0
- package/dist/integration-interface.d.ts.map +1 -0
- package/dist/integration-interface.js +94 -0
- package/dist/integration-interface.js.map +1 -0
- package/dist/integrations-store.d.ts +33 -0
- package/dist/integrations-store.d.ts.map +1 -0
- package/dist/integrations-store.js +50 -0
- package/dist/integrations-store.js.map +1 -0
- package/dist/jwt.d.ts +86 -17
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js +140 -17
- package/dist/jwt.js.map +1 -1
- package/dist/registry.d.ts +7 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +8 -21
- package/dist/registry.js.map +1 -1
- package/dist/secret-collection.d.ts +37 -0
- package/dist/secret-collection.d.ts.map +1 -0
- package/dist/secret-collection.js +37 -0
- package/dist/secret-collection.js.map +1 -0
- package/dist/server.d.ts +41 -44
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +232 -592
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/agent-definitions/auth.ts +187 -1
- package/src/agent-definitions/integrations.ts +260 -5
- package/src/index.ts +18 -4
- package/src/integration-interface.ts +118 -0
- package/src/integrations-store.ts +84 -0
- package/src/jwt.ts +233 -65
- package/src/registry.ts +17 -2
- package/src/secret-collection.ts +66 -0
- package/src/server.ts +268 -681
- package/src/types.ts +8 -1
- package/dist/slack-oauth.d.ts +0 -27
- package/dist/slack-oauth.d.ts.map +0 -1
- package/dist/slack-oauth.js +0 -48
- package/dist/slack-oauth.js.map +0 -1
- package/dist/web-pages.d.ts +0 -8
- package/dist/web-pages.d.ts.map +0 -1
- package/dist/web-pages.js +0 -169
- package/dist/web-pages.js.map +0 -1
- package/src/slack-oauth.ts +0 -66
- package/src/web-pages.ts +0 -178
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { defineAgent, defineTool } from "../define.js";
|
|
27
|
-
import { pendingCollections, generateCollectionToken } from "../
|
|
27
|
+
import { pendingCollections, generateCollectionToken } from "../secret-collection.js";
|
|
28
28
|
import type { AgentDefinition, ToolContext, ToolDefinition } from "../types.js";
|
|
29
29
|
|
|
30
30
|
// ============================================
|
|
@@ -488,6 +488,23 @@ export interface IntegrationsAgentOptions {
|
|
|
488
488
|
/** Integration store backend */
|
|
489
489
|
store: IntegrationStore;
|
|
490
490
|
|
|
491
|
+
/**
|
|
492
|
+
* Callback to list all registered agents.
|
|
493
|
+
* Used by list_integrations to discover agents with integrationMethods.
|
|
494
|
+
* Typically wired to registry.list().
|
|
495
|
+
*/
|
|
496
|
+
getAgents?: () => AgentDefinition[];
|
|
497
|
+
|
|
498
|
+
/** Registry instance for calling other agents' internal tools */
|
|
499
|
+
registry?: {
|
|
500
|
+
call(request: any): Promise<any>;
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
/** Integrations store for tracking installed integrations */
|
|
504
|
+
integrationsStore?: {
|
|
505
|
+
create(input: { agentPath: string; config: Record<string, unknown>; installedBy?: string; tenantId?: string }): Promise<any>;
|
|
506
|
+
};
|
|
507
|
+
|
|
491
508
|
/** Secret store for storing/resolving client credentials and tokens */
|
|
492
509
|
secretStore: {
|
|
493
510
|
store(value: string, ownerId: string): Promise<string>;
|
|
@@ -518,7 +535,7 @@ const SYSTEM_OWNER = "__integrations__";
|
|
|
518
535
|
export function createIntegrationsAgent(
|
|
519
536
|
options: IntegrationsAgentOptions,
|
|
520
537
|
): AgentDefinition {
|
|
521
|
-
const { store, callbackBaseUrl, secretStore } = options;
|
|
538
|
+
const { store, callbackBaseUrl, secretStore, getAgents, integrationsStore } = options;
|
|
522
539
|
|
|
523
540
|
// ---- setup_integration ----
|
|
524
541
|
const setupTool = defineTool({
|
|
@@ -663,11 +680,121 @@ export function createIntegrationsAgent(
|
|
|
663
680
|
}
|
|
664
681
|
|
|
665
682
|
await store.upsertProvider(config);
|
|
683
|
+
|
|
684
|
+
// Also track in integrations table
|
|
685
|
+
if (integrationsStore) {
|
|
686
|
+
try {
|
|
687
|
+
await integrationsStore.create({
|
|
688
|
+
agentPath: config.agentPath ?? `@${config.id}`,
|
|
689
|
+
config: { providerId: config.id, ...input },
|
|
690
|
+
installedBy: _ctx.callerId,
|
|
691
|
+
});
|
|
692
|
+
} catch {}
|
|
693
|
+
}
|
|
694
|
+
|
|
666
695
|
result.provider = config;
|
|
667
696
|
return result;
|
|
668
697
|
},
|
|
669
698
|
});
|
|
670
699
|
|
|
700
|
+
|
|
701
|
+
// ---- discover_integrations ----
|
|
702
|
+
const discoverTool = defineTool({
|
|
703
|
+
name: "discover_integrations",
|
|
704
|
+
description:
|
|
705
|
+
"Discover available integration types that can be set up. " +
|
|
706
|
+
"Returns a catalog of integrations with their setup/connect schemas " +
|
|
707
|
+
"so you know what parameters to pass to setup_integration.",
|
|
708
|
+
visibility: "public" as const,
|
|
709
|
+
inputSchema: {
|
|
710
|
+
type: "object" as const,
|
|
711
|
+
properties: {
|
|
712
|
+
query: {
|
|
713
|
+
type: "string",
|
|
714
|
+
description: "Search query to filter integrations by name or description",
|
|
715
|
+
},
|
|
716
|
+
category: {
|
|
717
|
+
type: "string",
|
|
718
|
+
description: "Filter by category (e.g. 'infrastructure', 'communication')",
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
execute: async (
|
|
723
|
+
input: { query?: string; category?: string },
|
|
724
|
+
_ctx: ToolContext,
|
|
725
|
+
) => {
|
|
726
|
+
const catalog: Array<{
|
|
727
|
+
provider: string;
|
|
728
|
+
agentPath?: string;
|
|
729
|
+
displayName: string;
|
|
730
|
+
icon?: string;
|
|
731
|
+
category?: string;
|
|
732
|
+
description?: string;
|
|
733
|
+
setupSchema?: Record<string, unknown>;
|
|
734
|
+
connectSchema?: Record<string, unknown>;
|
|
735
|
+
hasOAuth?: boolean;
|
|
736
|
+
}> = [];
|
|
737
|
+
|
|
738
|
+
// 1. Agent-backed integrations
|
|
739
|
+
if (getAgents) {
|
|
740
|
+
for (const agent of getAgents()) {
|
|
741
|
+
if (agent.config?.integration) {
|
|
742
|
+
const ic = agent.config.integration;
|
|
743
|
+
catalog.push({
|
|
744
|
+
provider: ic.provider,
|
|
745
|
+
agentPath: agent.path,
|
|
746
|
+
displayName: ic.displayName,
|
|
747
|
+
icon: ic.icon,
|
|
748
|
+
category: ic.category,
|
|
749
|
+
description: ic.description,
|
|
750
|
+
setupSchema: ic.setupSchema,
|
|
751
|
+
connectSchema: ic.connectSchema,
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// 2. DB-stored providers (legacy OAuth)
|
|
758
|
+
const providers = await store.listProviders();
|
|
759
|
+
for (const p of providers) {
|
|
760
|
+
// Skip if already in catalog from agent scan
|
|
761
|
+
if (catalog.some((c) => c.provider === p.id)) continue;
|
|
762
|
+
catalog.push({
|
|
763
|
+
provider: p.id,
|
|
764
|
+
displayName: p.name,
|
|
765
|
+
agentPath: p.agentPath,
|
|
766
|
+
hasOAuth: !!p.auth,
|
|
767
|
+
connectSchema: p.auth
|
|
768
|
+
? {
|
|
769
|
+
type: "object",
|
|
770
|
+
description: "OAuth flow — use connect_integration to start",
|
|
771
|
+
properties: {
|
|
772
|
+
provider: { type: "string", const: p.id },
|
|
773
|
+
},
|
|
774
|
+
}
|
|
775
|
+
: undefined,
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// 3. Filter
|
|
780
|
+
let results = catalog;
|
|
781
|
+
if (input.query) {
|
|
782
|
+
const q = input.query.toLowerCase();
|
|
783
|
+
results = results.filter(
|
|
784
|
+
(r) =>
|
|
785
|
+
r.provider.toLowerCase().includes(q) ||
|
|
786
|
+
r.displayName.toLowerCase().includes(q) ||
|
|
787
|
+
(r.description?.toLowerCase().includes(q) ?? false),
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
if (input.category) {
|
|
791
|
+
results = results.filter((r) => r.category === input.category);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return { integrations: results };
|
|
795
|
+
},
|
|
796
|
+
});
|
|
797
|
+
|
|
671
798
|
// ---- list_integrations ----
|
|
672
799
|
const listTool = defineTool({
|
|
673
800
|
name: "list_integrations",
|
|
@@ -688,15 +815,64 @@ export function createIntegrationsAgent(
|
|
|
688
815
|
const userId = input.userId ?? ctx.callerId;
|
|
689
816
|
const connections = userId ? await store.listConnections(userId) : [];
|
|
690
817
|
|
|
691
|
-
|
|
692
|
-
|
|
818
|
+
// Build unified integrations list
|
|
819
|
+
const integrations: Array<Record<string, unknown>> = [];
|
|
820
|
+
|
|
821
|
+
// 1. DB-stored providers (legacy OAuth integrations)
|
|
822
|
+
for (const p of providers) {
|
|
823
|
+
integrations.push({
|
|
693
824
|
id: p.id,
|
|
694
825
|
name: p.name,
|
|
826
|
+
provider: p.id,
|
|
695
827
|
agentPath: p.agentPath,
|
|
696
828
|
scope: p.scope ?? "user",
|
|
697
829
|
hasOAuth: !!p.auth,
|
|
698
830
|
connected: connections.some((c) => c.providerId === p.id),
|
|
699
|
-
})
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// 2. Agent-backed integrations (agents with config.integration + integrationMethods)
|
|
835
|
+
if (getAgents) {
|
|
836
|
+
const agents = getAgents();
|
|
837
|
+
for (const agent of agents) {
|
|
838
|
+
if (agent.integrationMethods?.list && agent.config?.integration) {
|
|
839
|
+
const meta = {
|
|
840
|
+
provider: agent.config.integration.provider,
|
|
841
|
+
agentPath: agent.path,
|
|
842
|
+
displayName: agent.config.integration.displayName,
|
|
843
|
+
icon: agent.config.integration.icon,
|
|
844
|
+
category: agent.config.integration.category,
|
|
845
|
+
description: agent.config.integration.description,
|
|
846
|
+
};
|
|
847
|
+
try {
|
|
848
|
+
const result = await agent.integrationMethods.list({}, { ...ctx, provider: agent.config.integration.provider });
|
|
849
|
+
if (result.success && result.data) {
|
|
850
|
+
// Flatten: if data has an array field, each item becomes an integration
|
|
851
|
+
const items = Array.isArray(result.data)
|
|
852
|
+
? result.data
|
|
853
|
+
: Object.values(result.data as Record<string, unknown>).find(Array.isArray) as unknown[] ?? [];
|
|
854
|
+
for (const item of items) {
|
|
855
|
+
integrations.push({
|
|
856
|
+
...meta,
|
|
857
|
+
...(typeof item === "object" && item !== null ? item as Record<string, unknown> : { value: item }),
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
// If no items found but agent exists, include it as a provider entry
|
|
861
|
+
if (items.length === 0) {
|
|
862
|
+
integrations.push({ ...meta, id: meta.provider });
|
|
863
|
+
}
|
|
864
|
+
} else {
|
|
865
|
+
integrations.push({ ...meta, id: meta.provider });
|
|
866
|
+
}
|
|
867
|
+
} catch {
|
|
868
|
+
integrations.push({ ...meta, id: meta.provider });
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return {
|
|
875
|
+
integrations,
|
|
700
876
|
connections: connections.map((c) => ({
|
|
701
877
|
providerId: c.providerId,
|
|
702
878
|
connectedAt: c.connectedAt,
|
|
@@ -1036,6 +1212,7 @@ export function createIntegrationsAgent(
|
|
|
1036
1212
|
provider: config.id,
|
|
1037
1213
|
userId,
|
|
1038
1214
|
connectedAt: connection.connectedAt,
|
|
1215
|
+
accessToken: result.accessToken,
|
|
1039
1216
|
};
|
|
1040
1217
|
},
|
|
1041
1218
|
});
|
|
@@ -1162,6 +1339,81 @@ export function createIntegrationsAgent(
|
|
|
1162
1339
|
},
|
|
1163
1340
|
});
|
|
1164
1341
|
|
|
1342
|
+
|
|
1343
|
+
// ---- Facade: discover_integrations (aggregates from all agents) ----
|
|
1344
|
+
const discoverFacadeTool = defineTool({
|
|
1345
|
+
name: "discover_integrations",
|
|
1346
|
+
description: "Discover all available integrations across all registered agents.",
|
|
1347
|
+
visibility: "public" as const,
|
|
1348
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
1349
|
+
execute: async () => {
|
|
1350
|
+
const agents = getAgents?.() ?? [];
|
|
1351
|
+
const results: any[] = [];
|
|
1352
|
+
if (options.registry) {
|
|
1353
|
+
for (const agent of agents) {
|
|
1354
|
+
const hasDiscoverTool = agent.tools?.some((t: any) => t.name === 'discover_integrations');
|
|
1355
|
+
if (hasDiscoverTool) {
|
|
1356
|
+
try {
|
|
1357
|
+
const res = await options.registry.call({
|
|
1358
|
+
action: 'execute_tool',
|
|
1359
|
+
path: agent.path,
|
|
1360
|
+
tool: 'discover_integrations',
|
|
1361
|
+
params: {},
|
|
1362
|
+
callerId: '@integrations',
|
|
1363
|
+
callerType: 'system',
|
|
1364
|
+
});
|
|
1365
|
+
if (res?.result && Array.isArray(res.result)) {
|
|
1366
|
+
results.push(...res.result);
|
|
1367
|
+
}
|
|
1368
|
+
} catch {}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return results;
|
|
1373
|
+
},
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
// ---- Facade: list_integrations (aggregates from all agents) ----
|
|
1377
|
+
const listFacadeTool = defineTool({
|
|
1378
|
+
name: "list_integrations",
|
|
1379
|
+
description: "List all installed integrations across all agents.",
|
|
1380
|
+
visibility: "public" as const,
|
|
1381
|
+
inputSchema: {
|
|
1382
|
+
type: "object" as const,
|
|
1383
|
+
properties: {
|
|
1384
|
+
agent_path: { type: "string", description: "Filter by agent path" },
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
execute: async (input: { agent_path?: string }) => {
|
|
1388
|
+
const agents = getAgents?.() ?? [];
|
|
1389
|
+
const results: any[] = [];
|
|
1390
|
+
if (options.registry) {
|
|
1391
|
+
const targetAgents = input.agent_path
|
|
1392
|
+
? agents.filter((a: any) => a.path === input.agent_path)
|
|
1393
|
+
: agents;
|
|
1394
|
+
for (const agent of targetAgents) {
|
|
1395
|
+
const hasListTool = agent.tools?.some((t: any) => t.name === 'list_integrations');
|
|
1396
|
+
if (hasListTool) {
|
|
1397
|
+
try {
|
|
1398
|
+
const res = await options.registry.call({
|
|
1399
|
+
action: 'execute_tool',
|
|
1400
|
+
path: agent.path,
|
|
1401
|
+
tool: 'list_integrations',
|
|
1402
|
+
params: {},
|
|
1403
|
+
callerId: '@integrations',
|
|
1404
|
+
callerType: 'system',
|
|
1405
|
+
});
|
|
1406
|
+
if (res?.result && Array.isArray(res.result)) {
|
|
1407
|
+
results.push(...res.result);
|
|
1408
|
+
}
|
|
1409
|
+
} catch {}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
return results;
|
|
1414
|
+
},
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1165
1417
|
return defineAgent({
|
|
1166
1418
|
path: "@integrations",
|
|
1167
1419
|
entrypoint:
|
|
@@ -1175,12 +1427,15 @@ export function createIntegrationsAgent(
|
|
|
1175
1427
|
visibility: "public",
|
|
1176
1428
|
tools: [
|
|
1177
1429
|
setupTool,
|
|
1430
|
+
discoverTool,
|
|
1178
1431
|
listTool,
|
|
1179
1432
|
getTool,
|
|
1180
1433
|
connectTool,
|
|
1181
1434
|
callTool,
|
|
1182
1435
|
callbackTool,
|
|
1183
1436
|
collectSecretsTool,
|
|
1437
|
+
discoverFacadeTool,
|
|
1438
|
+
listFacadeTool,
|
|
1184
1439
|
] as ToolDefinition[],
|
|
1185
1440
|
});
|
|
1186
1441
|
}
|
package/src/index.ts
CHANGED
|
@@ -97,8 +97,19 @@ export { createAgentRegistry } from "./registry.js";
|
|
|
97
97
|
export type { AgentRegistry, AgentRegistryOptions } from "./registry.js";
|
|
98
98
|
|
|
99
99
|
// Server
|
|
100
|
-
export { createAgentServer } from "./server.js";
|
|
101
|
-
export type { AgentServer, AgentServerOptions } from "./server.js";
|
|
100
|
+
export { createAgentServer, detectAuth, resolveAuth, canSeeAgent } from "./server.js";
|
|
101
|
+
export type { AgentServer, AgentServerOptions, AuthConfig, ResolvedAuth } from "./server.js";
|
|
102
|
+
|
|
103
|
+
// Secret Collection
|
|
104
|
+
export {
|
|
105
|
+
pendingCollections,
|
|
106
|
+
generateCollectionToken,
|
|
107
|
+
cleanupExpiredCollections,
|
|
108
|
+
} from "./secret-collection.js";
|
|
109
|
+
export type {
|
|
110
|
+
PendingCollection,
|
|
111
|
+
PendingCollectionField,
|
|
112
|
+
} from "./secret-collection.js";
|
|
102
113
|
|
|
103
114
|
// Auth
|
|
104
115
|
export {
|
|
@@ -134,8 +145,8 @@ export type {
|
|
|
134
145
|
export { encryptSecret, decryptSecret } from "./crypto.js";
|
|
135
146
|
|
|
136
147
|
// JWT
|
|
137
|
-
export { signJwt, verifyJwt } from "./jwt.js";
|
|
138
|
-
export type { JwtPayload } from "./jwt.js";
|
|
148
|
+
export { signJwt, verifyJwt, signJwtES256, verifyJwtLocal, verifyJwtFromIssuer, generateSigningKey, exportSigningKey, importSigningKey, buildJwks } from "./jwt.js";
|
|
149
|
+
export type { JwtPayload, AgentJwtPayload, SigningKey, ExportedKeyPair } from "./jwt.js";
|
|
139
150
|
|
|
140
151
|
// Postgres Secret Store
|
|
141
152
|
|
|
@@ -179,3 +190,6 @@ export type {
|
|
|
179
190
|
UserStore,
|
|
180
191
|
UsersAgentOptions,
|
|
181
192
|
} from "./agent-definitions/users.js";
|
|
193
|
+
export * from "./integrations-store.js";
|
|
194
|
+
export * from "./integration-interface.js";
|
|
195
|
+
export type { ContextFactory } from "./registry.js";
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration interface — standard tools that integration agents implement.
|
|
3
|
+
*
|
|
4
|
+
* Any agent that acts as an integration source should implement these tools.
|
|
5
|
+
* They are all internal visibility and only callable by @integrations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { defineTool } from './define.js';
|
|
9
|
+
import type { IntegrationsStore } from './integrations-store.js';
|
|
10
|
+
import type { ToolDefinition, ToolContext } from './types.js';
|
|
11
|
+
|
|
12
|
+
export interface IntegrationDefinition {
|
|
13
|
+
id: string;
|
|
14
|
+
agentPath: string;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
type: 'oauth' | 'credentials' | 'config';
|
|
18
|
+
configSchema?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IntegrationInterfaceConfig {
|
|
22
|
+
agentPath: string;
|
|
23
|
+
store: IntegrationsStore;
|
|
24
|
+
discover: () => Promise<IntegrationDefinition[]>;
|
|
25
|
+
setup: (config: Record<string, unknown>, ctx: ToolContext) => Promise<{ success: boolean; integrationId?: string; oauthUrl?: string; error?: string }>;
|
|
26
|
+
connect?: (integrationId: string, ctx: ToolContext) => Promise<{ success: boolean; error?: string }>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create the standard _integration tools for an agent.
|
|
31
|
+
* Returns an array of ToolDefinitions to include in the agent's tools.
|
|
32
|
+
*/
|
|
33
|
+
export function createIntegrationTools(config: IntegrationInterfaceConfig): ToolDefinition<ToolContext>[] {
|
|
34
|
+
const { agentPath, store, discover, setup, connect } = config;
|
|
35
|
+
|
|
36
|
+
const discoverTool = defineTool({
|
|
37
|
+
name: 'discover_integrations',
|
|
38
|
+
description: `Discover available integrations for ${agentPath}.`,
|
|
39
|
+
visibility: 'internal' as const,
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object' as const,
|
|
42
|
+
properties: {},
|
|
43
|
+
},
|
|
44
|
+
execute: async () => {
|
|
45
|
+
const available = await discover();
|
|
46
|
+
return available;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const setupTool = defineTool({
|
|
51
|
+
name: 'setup_integration',
|
|
52
|
+
description: `Set up a new integration for ${agentPath}.`,
|
|
53
|
+
visibility: 'internal' as const,
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object' as const,
|
|
56
|
+
properties: {
|
|
57
|
+
config: { type: 'object', description: 'Integration configuration' },
|
|
58
|
+
},
|
|
59
|
+
required: ['config'],
|
|
60
|
+
},
|
|
61
|
+
execute: async (input: { config: Record<string, unknown> }, ctx: ToolContext) => {
|
|
62
|
+
const result = await setup(input.config, ctx);
|
|
63
|
+
if (result.success && !result.oauthUrl) {
|
|
64
|
+
// Direct setup (no OAuth needed) — create integration row
|
|
65
|
+
const integration = await store.create({
|
|
66
|
+
agentPath,
|
|
67
|
+
config: input.config,
|
|
68
|
+
installedBy: ctx.callerId,
|
|
69
|
+
});
|
|
70
|
+
return { success: true, integrationId: integration.id };
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const connectTool = defineTool({
|
|
77
|
+
name: 'connect_integration',
|
|
78
|
+
description: `Test or authorize a ${agentPath} integration connection.`,
|
|
79
|
+
visibility: 'internal' as const,
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: 'object' as const,
|
|
82
|
+
properties: {
|
|
83
|
+
integration_id: { type: 'string', description: 'Integration ID to connect' },
|
|
84
|
+
},
|
|
85
|
+
required: ['integration_id'],
|
|
86
|
+
},
|
|
87
|
+
execute: async (input: { integration_id: string }, ctx: ToolContext) => {
|
|
88
|
+
if (connect) {
|
|
89
|
+
const result = await connect(input.integration_id, ctx);
|
|
90
|
+
if (result.success) {
|
|
91
|
+
await store.update(input.integration_id, { status: 'active' });
|
|
92
|
+
} else {
|
|
93
|
+
await store.update(input.integration_id, { status: 'error' });
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
return { success: true };
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const listTool = defineTool({
|
|
102
|
+
name: 'list_integrations',
|
|
103
|
+
description: `List installed integrations for ${agentPath}.`,
|
|
104
|
+
visibility: 'internal' as const,
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object' as const,
|
|
107
|
+
properties: {
|
|
108
|
+
tenant_id: { type: 'string', description: 'Filter by tenant' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
execute: async (input: { tenant_id?: string }) => {
|
|
112
|
+
const integrations = await store.listByAgent(agentPath, input.tenant_id);
|
|
113
|
+
return integrations;
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return [discoverTool, setupTool, connectTool, listTool] as ToolDefinition<ToolContext>[];
|
|
118
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IntegrationsStore — persistence interface for installed integrations.
|
|
3
|
+
*
|
|
4
|
+
* Each integration is an agent that's been configured and connected.
|
|
5
|
+
* The store tracks what's installed, its config, and status.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface Integration {
|
|
9
|
+
id: string;
|
|
10
|
+
agentPath: string;
|
|
11
|
+
tenantId?: string;
|
|
12
|
+
status: 'active' | 'disabled' | 'error';
|
|
13
|
+
config: Record<string, unknown>;
|
|
14
|
+
installedBy?: string;
|
|
15
|
+
installedAt: number;
|
|
16
|
+
updatedAt: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CreateIntegrationInput {
|
|
20
|
+
agentPath: string;
|
|
21
|
+
tenantId?: string;
|
|
22
|
+
config: Record<string, unknown>;
|
|
23
|
+
installedBy?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IntegrationsStore {
|
|
27
|
+
create(input: CreateIntegrationInput): Promise<Integration>;
|
|
28
|
+
get(id: string): Promise<Integration | null>;
|
|
29
|
+
list(tenantId?: string): Promise<Integration[]>;
|
|
30
|
+
listByAgent(agentPath: string, tenantId?: string): Promise<Integration[]>;
|
|
31
|
+
update(id: string, updates: Partial<Pick<Integration, 'status' | 'config' | 'updatedAt'>>): Promise<Integration | null>;
|
|
32
|
+
delete(id: string): Promise<boolean>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** In-memory implementation for testing / lightweight use */
|
|
36
|
+
export function createInMemoryIntegrationsStore(): IntegrationsStore {
|
|
37
|
+
const integrations = new Map<string, Integration>();
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
async create(input) {
|
|
41
|
+
const id = `int_${Math.random().toString(36).slice(2, 14)}`;
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const integration: Integration = {
|
|
44
|
+
id,
|
|
45
|
+
agentPath: input.agentPath,
|
|
46
|
+
tenantId: input.tenantId,
|
|
47
|
+
status: 'active',
|
|
48
|
+
config: input.config,
|
|
49
|
+
installedBy: input.installedBy,
|
|
50
|
+
installedAt: now,
|
|
51
|
+
updatedAt: now,
|
|
52
|
+
};
|
|
53
|
+
integrations.set(id, integration);
|
|
54
|
+
return integration;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
async get(id) {
|
|
58
|
+
return integrations.get(id) ?? null;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async list(tenantId?) {
|
|
62
|
+
const all = Array.from(integrations.values());
|
|
63
|
+
return tenantId ? all.filter(i => i.tenantId === tenantId) : all;
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
async listByAgent(agentPath, tenantId?) {
|
|
67
|
+
return Array.from(integrations.values()).filter(
|
|
68
|
+
i => i.agentPath === agentPath && (!tenantId || i.tenantId === tenantId)
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
async update(id, updates) {
|
|
73
|
+
const existing = integrations.get(id);
|
|
74
|
+
if (!existing) return null;
|
|
75
|
+
const updated = { ...existing, ...updates, updatedAt: Date.now() };
|
|
76
|
+
integrations.set(id, updated);
|
|
77
|
+
return updated;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
async delete(id) {
|
|
81
|
+
return integrations.delete(id);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|