@agentforge-io/core 0.2.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/adapters/billing/billing-adapter.interface.d.ts +41 -0
- package/dist/adapters/billing/billing-adapter.interface.js +5 -0
- package/dist/adapters/billing/stripe/stripe.adapter.d.ts +30 -0
- package/dist/adapters/billing/stripe/stripe.adapter.js +122 -0
- package/dist/adapters/email/email-adapter.interface.d.ts +25 -0
- package/dist/adapters/email/email-adapter.interface.js +6 -0
- package/dist/adapters/email/noop.adapter.d.ts +10 -0
- package/dist/adapters/email/noop.adapter.js +15 -0
- package/dist/adapters/email/resend.adapter.d.ts +8 -0
- package/dist/adapters/email/resend.adapter.js +39 -0
- package/dist/adapters/job-queue/in-memory.d.ts +43 -0
- package/dist/adapters/job-queue/in-memory.js +154 -0
- package/dist/adapters/job-queue/job-queue.types.d.ts +76 -0
- package/dist/adapters/job-queue/job-queue.types.js +5 -0
- package/dist/adapters/prepared-stream/prepared-stream.types.d.ts +23 -0
- package/dist/adapters/prepared-stream/prepared-stream.types.js +5 -0
- package/dist/adapters/rate-limiter/in-memory.d.ts +19 -0
- package/dist/adapters/rate-limiter/in-memory.js +63 -0
- package/dist/adapters/rate-limiter/rate-limiter.types.d.ts +42 -0
- package/dist/adapters/rate-limiter/rate-limiter.types.js +5 -0
- package/dist/adapters/rate-limiter/redis.d.ts +31 -0
- package/dist/adapters/rate-limiter/redis.js +47 -0
- package/dist/adapters/upload/noop.adapter.d.ts +9 -0
- package/dist/adapters/upload/noop.adapter.js +14 -0
- package/dist/adapters/upload/s3.adapter.d.ts +38 -0
- package/dist/adapters/upload/s3.adapter.js +69 -0
- package/dist/adapters/upload/upload-adapter.interface.d.ts +37 -0
- package/dist/adapters/upload/upload-adapter.interface.js +15 -0
- package/dist/ai/index.d.ts +15 -0
- package/dist/ai/index.js +43 -0
- package/dist/billing/index.d.ts +12 -0
- package/dist/billing/index.js +28 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +8 -0
- package/dist/domain/agent.d.ts +59 -0
- package/dist/domain/agent.js +2 -0
- package/dist/domain/api-key.d.ts +28 -0
- package/dist/domain/api-key.js +2 -0
- package/dist/domain/auth-identity.d.ts +10 -0
- package/dist/domain/auth-identity.js +2 -0
- package/dist/domain/chat-token.d.ts +39 -0
- package/dist/domain/chat-token.js +2 -0
- package/dist/domain/connector-auth.d.ts +42 -0
- package/dist/domain/connector-auth.js +2 -0
- package/dist/domain/connector.d.ts +52 -0
- package/dist/domain/connector.js +2 -0
- package/dist/domain/conversation.d.ts +26 -0
- package/dist/domain/conversation.js +2 -0
- package/dist/domain/email-token.d.ts +11 -0
- package/dist/domain/email-token.js +2 -0
- package/dist/domain/external-user.d.ts +23 -0
- package/dist/domain/external-user.js +2 -0
- package/dist/domain/index.d.ts +5 -0
- package/dist/domain/index.js +24 -0
- package/dist/domain/mcp-server.d.ts +33 -0
- package/dist/domain/mcp-server.js +2 -0
- package/dist/domain/plan.d.ts +20 -0
- package/dist/domain/plan.js +2 -0
- package/dist/domain/platform-secret.d.ts +24 -0
- package/dist/domain/platform-secret.js +8 -0
- package/dist/domain/refresh-token.d.ts +15 -0
- package/dist/domain/refresh-token.js +2 -0
- package/dist/domain/subscription.d.ts +21 -0
- package/dist/domain/subscription.js +2 -0
- package/dist/domain/tenant.d.ts +21 -0
- package/dist/domain/tenant.js +2 -0
- package/dist/domain/usage-record.d.ts +15 -0
- package/dist/domain/usage-record.js +2 -0
- package/dist/domain/user.d.ts +43 -0
- package/dist/domain/user.js +2 -0
- package/dist/factory.d.ts +68 -0
- package/dist/factory.js +56 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +59 -0
- package/dist/repositories/in-memory.d.ts +30 -0
- package/dist/repositories/in-memory.js +82 -0
- package/dist/repositories/index.d.ts +67 -0
- package/dist/repositories/index.js +16 -0
- package/dist/services/agent-config.service.d.ts +45 -0
- package/dist/services/agent-config.service.js +114 -0
- package/dist/services/agent-job.worker.d.ts +32 -0
- package/dist/services/agent-job.worker.js +97 -0
- package/dist/services/agent-runner.service.d.ts +35 -0
- package/dist/services/agent-runner.service.js +224 -0
- package/dist/services/agent.service.d.ts +171 -0
- package/dist/services/agent.service.js +329 -0
- package/dist/services/api-key.service.d.ts +41 -0
- package/dist/services/api-key.service.js +80 -0
- package/dist/services/auth.service.d.ts +133 -0
- package/dist/services/auth.service.js +411 -0
- package/dist/services/billing.service.d.ts +67 -0
- package/dist/services/billing.service.js +254 -0
- package/dist/services/chat-token.service.d.ts +29 -0
- package/dist/services/chat-token.service.js +113 -0
- package/dist/services/connector-registry.service.d.ts +156 -0
- package/dist/services/connector-registry.service.js +278 -0
- package/dist/services/conversation.service.d.ts +47 -0
- package/dist/services/conversation.service.js +101 -0
- package/dist/services/email-templates.d.ts +18 -0
- package/dist/services/email-templates.js +39 -0
- package/dist/services/email.service.d.ts +26 -0
- package/dist/services/email.service.js +42 -0
- package/dist/services/errors.d.ts +7 -0
- package/dist/services/errors.js +27 -0
- package/dist/services/in-memory-prepared-stream.store.d.ts +13 -0
- package/dist/services/in-memory-prepared-stream.store.js +35 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.js +40 -0
- package/dist/services/mcp-client.service.d.ts +64 -0
- package/dist/services/mcp-client.service.js +157 -0
- package/dist/services/mcp-server.service.d.ts +44 -0
- package/dist/services/mcp-server.service.js +147 -0
- package/dist/services/oauth.service.d.ts +73 -0
- package/dist/services/oauth.service.js +174 -0
- package/dist/services/oauth2.service.d.ts +57 -0
- package/dist/services/oauth2.service.js +82 -0
- package/dist/services/orchestrator.service.d.ts +45 -0
- package/dist/services/orchestrator.service.js +180 -0
- package/dist/services/plan.service.d.ts +54 -0
- package/dist/services/plan.service.js +120 -0
- package/dist/services/prepared-stream.service.d.ts +23 -0
- package/dist/services/prepared-stream.service.js +43 -0
- package/dist/services/refresh-token.service.d.ts +38 -0
- package/dist/services/refresh-token.service.js +73 -0
- package/dist/services/secrets/crypto.d.ts +37 -0
- package/dist/services/secrets/crypto.js +110 -0
- package/dist/services/secrets/known-keys.d.ts +38 -0
- package/dist/services/secrets/known-keys.js +50 -0
- package/dist/services/secrets.service.d.ts +91 -0
- package/dist/services/secrets.service.js +193 -0
- package/dist/services/tenant-billing.service.d.ts +121 -0
- package/dist/services/tenant-billing.service.js +290 -0
- package/dist/services/tenant.service.d.ts +54 -0
- package/dist/services/tenant.service.js +96 -0
- package/dist/services/tool-registry.service.d.ts +42 -0
- package/dist/services/tool-registry.service.js +101 -0
- package/dist/services/upload.service.d.ts +37 -0
- package/dist/services/upload.service.js +84 -0
- package/dist/services/usage.service.d.ts +34 -0
- package/dist/services/usage.service.js +108 -0
- package/dist/types/agent.types.d.ts +160 -0
- package/dist/types/agent.types.js +2 -0
- package/dist/types/billing.types.d.ts +82 -0
- package/dist/types/billing.types.js +3 -0
- package/dist/types/config.types.d.ts +127 -0
- package/dist/types/config.types.js +9 -0
- package/dist/types/hooks.d.ts +85 -0
- package/dist/types/hooks.js +2 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +19 -0
- package/package.json +36 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public chat token. Generated from the admin UI and embedded in a JS widget
|
|
3
|
+
* on the customer's website. Each token is bound to exactly one (tenant,
|
|
4
|
+
* agent) pair, so the browser only ever knows enough to talk to that one
|
|
5
|
+
* agent — the workspace API key never leaves the customer's server.
|
|
6
|
+
*
|
|
7
|
+
* The token string itself is the primary key. We store it in plaintext (not
|
|
8
|
+
* hashed) because the widget needs to send it in the URL path on every call;
|
|
9
|
+
* there's no symmetric secret we could verify a hash against. The protection
|
|
10
|
+
* model is: (1) tokens are easy to rotate from the UI; (2) `allowedOrigins`
|
|
11
|
+
* cap the damage if a token leaks (a stolen token from acme.com can't be
|
|
12
|
+
* used on attacker.com); (3) `isActive=false` revokes immediately.
|
|
13
|
+
*
|
|
14
|
+
* Visual customization (color, position, greeting…) is NOT here — it lives on
|
|
15
|
+
* the agent itself (`AgentRecord.appearance`) so a single edit propagates to
|
|
16
|
+
* every embed of that agent without re-issuing tokens.
|
|
17
|
+
*/
|
|
18
|
+
export interface ChatToken {
|
|
19
|
+
/** Random opaque string. Doubles as the primary key. */
|
|
20
|
+
token: string;
|
|
21
|
+
tenantId: string;
|
|
22
|
+
agentId: string;
|
|
23
|
+
/** Human-friendly label so the admin can tell several tokens apart
|
|
24
|
+
* ("Production homepage", "Marketing landing", etc). */
|
|
25
|
+
name: string;
|
|
26
|
+
/**
|
|
27
|
+
* If empty/null, the token accepts any Origin (handy for dev/staging).
|
|
28
|
+
* Otherwise the server only honors requests whose `Origin` header matches
|
|
29
|
+
* one of these entries. Wildcards are NOT supported on purpose — be explicit.
|
|
30
|
+
*/
|
|
31
|
+
allowedOrigins?: string[];
|
|
32
|
+
isActive: boolean;
|
|
33
|
+
createdByUserId?: string;
|
|
34
|
+
lastUsedAt?: Date;
|
|
35
|
+
createdAt: Date;
|
|
36
|
+
updatedAt: Date;
|
|
37
|
+
}
|
|
38
|
+
export type NewChatToken = Pick<ChatToken, 'token' | 'tenantId' | 'agentId' | 'name'> & Partial<Omit<ChatToken, 'token' | 'tenantId' | 'agentId' | 'name' | 'createdAt' | 'updatedAt'>>;
|
|
39
|
+
export type ChatTokenPatch = Partial<Omit<ChatToken, 'token' | 'tenantId' | 'agentId' | 'createdAt' | 'updatedAt'>>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-user OAuth credentials for a connector (Google, Notion, Slack, etc.).
|
|
3
|
+
*
|
|
4
|
+
* `accessTokenEncrypted` and `refreshTokenEncrypted` are AES-GCM blobs —
|
|
5
|
+
* exactly the same format as `af_platform_secrets`. The service layer
|
|
6
|
+
* encrypts on write and decrypts on read using MASTER_KEY; the repo never
|
|
7
|
+
* sees plaintext. That keeps "leak the DB row" from leaking the token.
|
|
8
|
+
*
|
|
9
|
+
* Scope of identity: the row is unique per (userId, connectorId). Today
|
|
10
|
+
* that's the dashboard user (JWT subject). When we wire chat-session-scoped
|
|
11
|
+
* connectors later, we add a separate row family with a different identity.
|
|
12
|
+
*/
|
|
13
|
+
export interface ConnectorAuth {
|
|
14
|
+
id: string;
|
|
15
|
+
/** Dashboard user (JWT sub). One row per (userId, connectorId). */
|
|
16
|
+
userId: string;
|
|
17
|
+
/** Stable connector id from the registry (e.g. "google", "notion"). */
|
|
18
|
+
connectorId: string;
|
|
19
|
+
/** Account label shown in the UI — e.g. "alice@acme.com". Pulled from
|
|
20
|
+
* the OAuth profile after the first token exchange. Optional. */
|
|
21
|
+
accountLabel?: string;
|
|
22
|
+
/** Encrypted access token. Format: AES-GCM blob produced by
|
|
23
|
+
* `infra/secrets/crypto.encrypt(plaintext, masterKey)`. */
|
|
24
|
+
accessTokenEncrypted: Buffer;
|
|
25
|
+
/** Encrypted refresh token, when the provider returned one. Optional
|
|
26
|
+
* because some providers (e.g. Google with `access_type=online`) don't. */
|
|
27
|
+
refreshTokenEncrypted?: Buffer;
|
|
28
|
+
/** When the access token expires. Refresh runs before this; the service
|
|
29
|
+
* treats anything within `REFRESH_LEEWAY_MS` as already-expired. */
|
|
30
|
+
expiresAt?: Date;
|
|
31
|
+
/** Space-separated scopes the user granted. Stored so the UI can show
|
|
32
|
+
* "you granted: gmail.readonly, drive.readonly" without re-asking the
|
|
33
|
+
* provider. */
|
|
34
|
+
scope?: string;
|
|
35
|
+
/** Soft toggle. UI revoke flips this to false and the runtime stops
|
|
36
|
+
* resolving the token for tool calls. */
|
|
37
|
+
isActive: boolean;
|
|
38
|
+
createdAt: Date;
|
|
39
|
+
updatedAt: Date;
|
|
40
|
+
}
|
|
41
|
+
export type NewConnectorAuth = Pick<ConnectorAuth, 'id' | 'userId' | 'connectorId' | 'accessTokenEncrypted'> & Partial<Omit<ConnectorAuth, 'id' | 'userId' | 'connectorId' | 'accessTokenEncrypted' | 'createdAt' | 'updatedAt'>>;
|
|
42
|
+
export type ConnectorAuthPatch = Partial<Omit<ConnectorAuth, 'id' | 'userId' | 'connectorId' | 'createdAt' | 'updatedAt'>>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AgentToolDefinition } from '../types';
|
|
2
|
+
import type { OAuth2ProviderConfig } from '../services/oauth2.service';
|
|
3
|
+
/**
|
|
4
|
+
* Per-tool context handed to a connector tool's handler. The runtime
|
|
5
|
+
* resolves the user's stored credentials (decrypts the access token, runs
|
|
6
|
+
* refresh if expired) and passes them in here so each tool can call the
|
|
7
|
+
* provider's API directly without knowing about OAuth.
|
|
8
|
+
*
|
|
9
|
+
* `getAccessToken` is a function rather than a plain field so the runtime
|
|
10
|
+
* can lazy-refresh on the first invocation rather than refreshing every
|
|
11
|
+
* tool call up-front.
|
|
12
|
+
*/
|
|
13
|
+
export interface ConnectorToolContext {
|
|
14
|
+
userId: string;
|
|
15
|
+
connectorId: string;
|
|
16
|
+
/** Returns a valid (non-expired) access token. Refreshes transparently
|
|
17
|
+
* if the cached one is within the refresh window. */
|
|
18
|
+
getAccessToken: () => Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A connector tool is an AgentForge tool that needs an authenticated user
|
|
22
|
+
* to call it. The `execute` signature is enriched with `ConnectorToolContext`,
|
|
23
|
+
* but at the registry layer we still expose plain `AgentToolDefinition` to
|
|
24
|
+
* the agent loop — the registry wraps `execute` to inject the context.
|
|
25
|
+
*/
|
|
26
|
+
export interface ConnectorToolFactory {
|
|
27
|
+
/** Definition without `execute`. The factory builds the executor. */
|
|
28
|
+
definition: Omit<AgentToolDefinition, 'execute'>;
|
|
29
|
+
build: (ctx: ConnectorToolContext) => AgentToolDefinition['execute'];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Static connector definition. Lives in code (option 1 from the design
|
|
33
|
+
* discussion): one of these per supported provider, registered into the
|
|
34
|
+
* `ConnectorRegistry` at boot. The host wires `clientId` / `clientSecret`
|
|
35
|
+
* from env into the `oauth` config before registering.
|
|
36
|
+
*/
|
|
37
|
+
export interface ConnectorDefinition {
|
|
38
|
+
/** Stable slug used in URLs and the DB (`google`, `slack`, …). */
|
|
39
|
+
id: string;
|
|
40
|
+
/** Human label shown in the Directory UI. */
|
|
41
|
+
name: string;
|
|
42
|
+
/** Short pitch shown in the Directory card. */
|
|
43
|
+
description: string;
|
|
44
|
+
/** Optional category label (Email, Calendar, Docs, …) for grouping. */
|
|
45
|
+
category?: string;
|
|
46
|
+
/** Optional URL of a logo asset served by the host. */
|
|
47
|
+
iconUrl?: string;
|
|
48
|
+
oauth: OAuth2ProviderConfig;
|
|
49
|
+
/** Tools this connector contributes to the agent's toolbelt once a user
|
|
50
|
+
* has authorized. */
|
|
51
|
+
tools: ConnectorToolFactory[];
|
|
52
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ConversationStatus, MessageRole, ToolCallRecord, TokenUsage } from '../types/agent.types';
|
|
2
|
+
export interface Conversation {
|
|
3
|
+
id: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
agentId: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
status: ConversationStatus;
|
|
8
|
+
totalInputTokens: number;
|
|
9
|
+
totalOutputTokens: number;
|
|
10
|
+
messageCount: number;
|
|
11
|
+
metadata?: Record<string, unknown>;
|
|
12
|
+
createdAt: Date;
|
|
13
|
+
updatedAt: Date;
|
|
14
|
+
}
|
|
15
|
+
export interface Message {
|
|
16
|
+
id: string;
|
|
17
|
+
conversationId: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
role: MessageRole;
|
|
20
|
+
content: string;
|
|
21
|
+
toolCalls?: ToolCallRecord[];
|
|
22
|
+
usage?: TokenUsage;
|
|
23
|
+
createdAt: Date;
|
|
24
|
+
}
|
|
25
|
+
export type NewConversation = Pick<Conversation, 'userId' | 'agentId'> & Partial<Omit<Conversation, 'id' | 'createdAt' | 'updatedAt'>>;
|
|
26
|
+
export type NewMessage = Omit<Message, 'id' | 'createdAt'>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type EmailTokenPurpose = 'verify_email' | 'reset_password';
|
|
2
|
+
export interface EmailToken {
|
|
3
|
+
id: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
tokenHash: string;
|
|
6
|
+
purpose: EmailTokenPurpose;
|
|
7
|
+
expiresAt: Date;
|
|
8
|
+
consumedAt?: Date;
|
|
9
|
+
createdAt: Date;
|
|
10
|
+
}
|
|
11
|
+
export type NewEmailToken = Omit<EmailToken, 'id' | 'createdAt'>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map of (tenantId, externalId) → internal AgentForge userId.
|
|
3
|
+
*
|
|
4
|
+
* In B2B mode, the consumer service has its own user system. When it calls
|
|
5
|
+
* AgentForge with `X-On-Behalf-Of: <theirUserId>`, we look up (or create) an
|
|
6
|
+
* `ExternalUser` row. The internal `id` is what we store on conversations,
|
|
7
|
+
* messages, usage records — so the rest of the system runs unchanged.
|
|
8
|
+
*
|
|
9
|
+
* Two consumers using "user_42" as an externalId get DIFFERENT internal ids,
|
|
10
|
+
* because the (tenantId, externalId) tuple is unique.
|
|
11
|
+
*/
|
|
12
|
+
export interface ExternalUser {
|
|
13
|
+
/** Internal AgentForge UUID (the userId for everything downstream). */
|
|
14
|
+
id: string;
|
|
15
|
+
tenantId: string;
|
|
16
|
+
/** The consumer's own user identifier — opaque to AgentForge. */
|
|
17
|
+
externalId: string;
|
|
18
|
+
email?: string;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
updatedAt: Date;
|
|
22
|
+
}
|
|
23
|
+
export type NewExternalUser = Pick<ExternalUser, 'id' | 'tenantId' | 'externalId'> & Partial<Pick<ExternalUser, 'email' | 'metadata'>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// SDK-owned domain types. The chat-token contract lives here because the
|
|
18
|
+
// public chat surface (mounted by @agentforge-io/nest) authenticates with it.
|
|
19
|
+
// Identity, multi-tenant, billing all stay in the host.
|
|
20
|
+
__exportStar(require("./conversation"), exports);
|
|
21
|
+
__exportStar(require("./chat-token"), exports);
|
|
22
|
+
__exportStar(require("./mcp-server"), exports);
|
|
23
|
+
__exportStar(require("./connector-auth"), exports);
|
|
24
|
+
__exportStar(require("./connector"), exports);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted MCP server, scoped to a tenant. The admin UI manages these; at
|
|
3
|
+
* runtime, AgentForge loads the active ones for each tenant and registers
|
|
4
|
+
* their tools into the global ToolRegistry under a tenant-scoped key.
|
|
5
|
+
*
|
|
6
|
+
* Auth model: tokens live in `headers` as `Authorization: Bearer <plaintext>`.
|
|
7
|
+
* For production secrecy, store the header value as a `secret://<key>` ref
|
|
8
|
+
* and resolve via SecretsService before connecting — the lib doesn't
|
|
9
|
+
* mandate that today because not every deploy has SecretsService wired.
|
|
10
|
+
*/
|
|
11
|
+
export interface McpServerRecord {
|
|
12
|
+
id: string;
|
|
13
|
+
/** Tenant that owns the server. Per-tenant scope keeps customer
|
|
14
|
+
* integrations isolated even when they share an AgentForge deploy. */
|
|
15
|
+
tenantId: string;
|
|
16
|
+
/** Human label + tool-name prefix (e.g. `notion` → `notion__search`). */
|
|
17
|
+
name: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
/** Wire protocol. `http` = Streamable HTTP; `sse` = legacy. */
|
|
20
|
+
transport: 'http' | 'sse';
|
|
21
|
+
url: string;
|
|
22
|
+
/** Static request headers (Authorization, custom API keys, etc). */
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
/** Soft toggle — when false, the runtime skips this server at boot and
|
|
25
|
+
* does NOT register its tools. Lets ops disable a server without losing
|
|
26
|
+
* its config. */
|
|
27
|
+
isActive: boolean;
|
|
28
|
+
createdByUserId?: string;
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
updatedAt: Date;
|
|
31
|
+
}
|
|
32
|
+
export type NewMcpServerRecord = Pick<McpServerRecord, 'id' | 'tenantId' | 'name' | 'transport' | 'url'> & Partial<Omit<McpServerRecord, 'id' | 'tenantId' | 'name' | 'transport' | 'url' | 'createdAt' | 'updatedAt'>>;
|
|
33
|
+
export type McpServerRecordPatch = Partial<Omit<McpServerRecord, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'>>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PlanDefinition, UsageLimits } from '../types/config.types';
|
|
2
|
+
/**
|
|
3
|
+
* A persisted plan row. Plans live in the DB so admins can edit
|
|
4
|
+
* limits/features/prices at runtime. The container seeds the table from
|
|
5
|
+
* `config.billing.plans` on first boot (when the table is empty), so existing
|
|
6
|
+
* deployments don't break; after that, the DB is the source of truth.
|
|
7
|
+
*/
|
|
8
|
+
export interface Plan extends PlanDefinition {
|
|
9
|
+
/** Soft delete flag — keeps the row for historical subscriptions. */
|
|
10
|
+
isActive: boolean;
|
|
11
|
+
createdAt: Date;
|
|
12
|
+
updatedAt: Date;
|
|
13
|
+
}
|
|
14
|
+
export type NewPlan = Pick<PlanDefinition, 'id' | 'name' | 'limits'> & Partial<Omit<PlanDefinition, 'id' | 'name' | 'limits'>> & {
|
|
15
|
+
isActive?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type PlanPatch = Partial<Omit<PlanDefinition, 'id'> & {
|
|
18
|
+
isActive: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
export type { UsageLimits };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface PlatformSecret {
|
|
2
|
+
id: string;
|
|
3
|
+
/** Stable identifier matching the canonical env-var name. */
|
|
4
|
+
key: string;
|
|
5
|
+
/** Encrypted blob: AES-GCM `iv | ciphertext | tag`. Persisted as bytea. */
|
|
6
|
+
valueEncrypted: Buffer;
|
|
7
|
+
/** Last 4 chars of the plaintext (or "••••" when too short). UI display. */
|
|
8
|
+
valueHint: string;
|
|
9
|
+
/** Free-form description shown next to the field in the admin UI. */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** User who last wrote this row. Optional because the first import from
|
|
12
|
+
* .env may have no user attached. */
|
|
13
|
+
updatedByUserId?: string;
|
|
14
|
+
createdAt: Date;
|
|
15
|
+
updatedAt: Date;
|
|
16
|
+
}
|
|
17
|
+
export interface NewPlatformSecret {
|
|
18
|
+
key: string;
|
|
19
|
+
valueEncrypted: Buffer;
|
|
20
|
+
valueHint: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
updatedByUserId?: string;
|
|
23
|
+
}
|
|
24
|
+
export type PlatformSecretPatch = Partial<Pick<PlatformSecret, 'valueEncrypted' | 'valueHint' | 'description' | 'updatedByUserId'>>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Domain type for platform-wide secrets (Tavily/Brave/Stripe/Anthropic).
|
|
3
|
+
//
|
|
4
|
+
// Note what's MISSING: the plaintext value. The plaintext only exists at
|
|
5
|
+
// two moments in the system's life — when the operator types it into the
|
|
6
|
+
// UI, and during the boot-time decrypt inside SecretsService. Anything
|
|
7
|
+
// else (repos, API responses, logs) must work off the hint + ciphertext.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface RefreshToken {
|
|
2
|
+
id: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
tokenHash: string;
|
|
5
|
+
family: string;
|
|
6
|
+
expiresAt: Date;
|
|
7
|
+
revokedAt?: Date;
|
|
8
|
+
replacedById?: string;
|
|
9
|
+
userAgent?: string;
|
|
10
|
+
ip?: string;
|
|
11
|
+
createdAt: Date;
|
|
12
|
+
lastUsedAt?: Date;
|
|
13
|
+
}
|
|
14
|
+
export type NewRefreshToken = Omit<RefreshToken, 'id' | 'createdAt'>;
|
|
15
|
+
export type RefreshTokenPatch = Partial<Omit<RefreshToken, 'id' | 'createdAt'>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SubscriptionStatus } from '../types/billing.types';
|
|
2
|
+
export interface Subscription {
|
|
3
|
+
id: string;
|
|
4
|
+
/** End-user subscription. Set for B2C; may be null in B2B mode where the
|
|
5
|
+
* payer is the tenant, not an individual user. */
|
|
6
|
+
userId?: string;
|
|
7
|
+
/** Tenant subscription. Set in B2B mode; null for B2C/end-user subs. */
|
|
8
|
+
tenantId?: string;
|
|
9
|
+
planId: string;
|
|
10
|
+
status: SubscriptionStatus;
|
|
11
|
+
providerSubscriptionId?: string;
|
|
12
|
+
providerCustomerId?: string;
|
|
13
|
+
currentPeriodStart?: Date;
|
|
14
|
+
currentPeriodEnd?: Date;
|
|
15
|
+
trialEnd?: Date;
|
|
16
|
+
cancelAtPeriodEnd: boolean;
|
|
17
|
+
createdAt: Date;
|
|
18
|
+
updatedAt: Date;
|
|
19
|
+
}
|
|
20
|
+
export type NewSubscription = Omit<Subscription, 'id' | 'createdAt' | 'updatedAt'>;
|
|
21
|
+
export type SubscriptionPatch = Partial<Omit<Subscription, 'id' | 'createdAt' | 'updatedAt'>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Tenant (a.k.a. "client") is a consumer service that signed up to use
|
|
3
|
+
* AgentForge as a backend. Every B2B request carries an API key that resolves
|
|
4
|
+
* to one Tenant, and all data is scoped under it. The Tenant's `ownerUserId`
|
|
5
|
+
* points at a User row (the human admin who manages the tenant).
|
|
6
|
+
*/
|
|
7
|
+
export interface Tenant {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
/** UserId of the human admin who manages this tenant. */
|
|
11
|
+
ownerUserId: string;
|
|
12
|
+
planId: string;
|
|
13
|
+
/** Stripe (or other provider) customer id — lazy-created on first checkout. */
|
|
14
|
+
providerCustomerId?: string;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
isActive: boolean;
|
|
17
|
+
createdAt: Date;
|
|
18
|
+
updatedAt: Date;
|
|
19
|
+
}
|
|
20
|
+
export type NewTenant = Pick<Tenant, 'id' | 'name' | 'ownerUserId'> & Partial<Omit<Tenant, 'id' | 'name' | 'ownerUserId' | 'createdAt' | 'updatedAt'>>;
|
|
21
|
+
export type TenantPatch = Partial<Omit<Tenant, 'id' | 'createdAt' | 'updatedAt'>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface UsageRecord {
|
|
2
|
+
id: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
conversationId: string;
|
|
5
|
+
messageId: string;
|
|
6
|
+
agentId: string;
|
|
7
|
+
inputTokens: number;
|
|
8
|
+
outputTokens: number;
|
|
9
|
+
totalTokens: number;
|
|
10
|
+
requestCount: number;
|
|
11
|
+
/** YYYY-MM bucket used by quota counters. */
|
|
12
|
+
billingPeriod: string;
|
|
13
|
+
recordedAt: Date;
|
|
14
|
+
}
|
|
15
|
+
export type NewUsageRecord = Omit<UsageRecord, 'id'>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roles in AgentForge's RBAC.
|
|
3
|
+
*
|
|
4
|
+
* - `user` — default. Can own tenants, manage their own API keys,
|
|
5
|
+
* view their billing. Bulk of signups land here.
|
|
6
|
+
* - `platform_admin` — operator of the AgentForge instance. Can edit the
|
|
7
|
+
* plan catalog, manage users, and any other endpoint
|
|
8
|
+
* that operates at instance scope.
|
|
9
|
+
*
|
|
10
|
+
* Bootstrap: the very first user registered on a fresh install is auto-
|
|
11
|
+
* promoted to `platform_admin` (since there's nobody else to grant it).
|
|
12
|
+
* Subsequent users default to `user`; existing platform admins can promote
|
|
13
|
+
* them via the admin UI.
|
|
14
|
+
*/
|
|
15
|
+
export type UserRole = 'user' | 'platform_admin';
|
|
16
|
+
/**
|
|
17
|
+
* Plain domain shape for a User. Mirrors the columns of `af_users` but is
|
|
18
|
+
* deliberately decoupled from TypeORM (no decorators, no `@Entity`). Each
|
|
19
|
+
* adapter is free to back this with TypeORM, Prisma, Drizzle, raw SQL, etc.
|
|
20
|
+
*/
|
|
21
|
+
export interface User {
|
|
22
|
+
id: string;
|
|
23
|
+
email?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
passwordHash?: string;
|
|
26
|
+
authProvider?: string;
|
|
27
|
+
providerCustomerId?: string;
|
|
28
|
+
currentPlanId: string;
|
|
29
|
+
creditsBalance: number;
|
|
30
|
+
/** RBAC role. See {@link UserRole} for semantics. */
|
|
31
|
+
role: UserRole;
|
|
32
|
+
externalId?: string;
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
isActive: boolean;
|
|
35
|
+
failedLoginCount: number;
|
|
36
|
+
lockedUntil?: Date;
|
|
37
|
+
lastLoginAt?: Date;
|
|
38
|
+
emailVerifiedAt?: Date;
|
|
39
|
+
createdAt: Date;
|
|
40
|
+
updatedAt: Date;
|
|
41
|
+
}
|
|
42
|
+
export type NewUser = Pick<User, 'id'> & Partial<Omit<User, 'id' | 'createdAt' | 'updatedAt'>>;
|
|
43
|
+
export type UserPatch = Partial<Omit<User, 'id' | 'createdAt' | 'updatedAt'>>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AgentForgeConfig } from './types/config.types';
|
|
2
|
+
import type { AgentToolDefinition } from './types/agent.types';
|
|
3
|
+
import type { SdkHooks } from './types/hooks';
|
|
4
|
+
import type { PreparedStreamStore } from './adapters/prepared-stream/prepared-stream.types';
|
|
5
|
+
import type { RateLimiter } from './adapters/rate-limiter/rate-limiter.types';
|
|
6
|
+
import type { JobQueue } from './adapters/job-queue/job-queue.types';
|
|
7
|
+
import type { AgentJobPayload, AgentJobResult } from './types/agent.types';
|
|
8
|
+
import { AgentJobWorker } from './services/agent-job.worker';
|
|
9
|
+
import type { ConversationRepository, MessageRepository } from './repositories';
|
|
10
|
+
import { ToolRegistryService, type Logger } from './services/tool-registry.service';
|
|
11
|
+
import { AgentRunnerService } from './services/agent-runner.service';
|
|
12
|
+
import { PreparedStreamService } from './services/prepared-stream.service';
|
|
13
|
+
import { ConversationService } from './services/conversation.service';
|
|
14
|
+
import { AgentService, type AgentResolver } from './services/agent.service';
|
|
15
|
+
import { OrchestratorService } from './services/orchestrator.service';
|
|
16
|
+
/**
|
|
17
|
+
* Repositories the SDK needs. After Phase 3 this is just conversation +
|
|
18
|
+
* message — the host owns everything else (users, tenants, plans, etc).
|
|
19
|
+
*/
|
|
20
|
+
export interface AgentForgeRepositories {
|
|
21
|
+
conversations: ConversationRepository;
|
|
22
|
+
messages: MessageRepository;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Optional adapter overrides for runtime infrastructure. Identity / email
|
|
26
|
+
* / uploads / secrets are now the host's job and not declared here.
|
|
27
|
+
*/
|
|
28
|
+
export interface AgentForgeAdapters {
|
|
29
|
+
preparedStreamStore?: PreparedStreamStore;
|
|
30
|
+
/** Rate limiter for sensitive endpoints. Defaults to in-memory. */
|
|
31
|
+
rateLimiter?: RateLimiter;
|
|
32
|
+
/** Background queue for agent-turn jobs. Defaults to in-memory. */
|
|
33
|
+
jobQueue?: JobQueue<AgentJobPayload, AgentJobResult>;
|
|
34
|
+
logger?: Logger;
|
|
35
|
+
/** Host-supplied agent resolver — lookup `AgentRecord` by `(tenantId, slug)`
|
|
36
|
+
* or by id. When omitted, the SDK falls back to the static
|
|
37
|
+
* `config.agents` array (UUID lookups only). */
|
|
38
|
+
agentResolver?: AgentResolver;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fully-wired AgentForge runtime. Drastically smaller post-Phase 3: the SDK
|
|
42
|
+
* only owns the AI loop, conversations, prepared streams and the job queue.
|
|
43
|
+
*/
|
|
44
|
+
export interface AgentForgeContainer {
|
|
45
|
+
config: AgentForgeConfig;
|
|
46
|
+
toolRegistry: ToolRegistryService;
|
|
47
|
+
runner: AgentRunnerService;
|
|
48
|
+
orchestrator: OrchestratorService;
|
|
49
|
+
preparedStream: PreparedStreamService;
|
|
50
|
+
conversations: ConversationService;
|
|
51
|
+
agents: AgentService;
|
|
52
|
+
rateLimiter: RateLimiter;
|
|
53
|
+
jobQueue: JobQueue<AgentJobPayload, AgentJobResult>;
|
|
54
|
+
agentJobWorker: AgentJobWorker;
|
|
55
|
+
}
|
|
56
|
+
export interface CreateAgentForgeOptions {
|
|
57
|
+
config: AgentForgeConfig;
|
|
58
|
+
repositories: AgentForgeRepositories;
|
|
59
|
+
adapters?: AgentForgeAdapters;
|
|
60
|
+
tools?: AgentToolDefinition[];
|
|
61
|
+
hooks?: SdkHooks;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build a fully-wired AgentForge container from plain inputs. Both
|
|
65
|
+
* @agentforge-io/nest and external bindings call this and expose the services
|
|
66
|
+
* through their transport.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createAgentForge(opts: CreateAgentForgeOptions): AgentForgeContainer;
|
package/dist/factory.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAgentForge = createAgentForge;
|
|
4
|
+
const in_memory_1 = require("./adapters/rate-limiter/in-memory");
|
|
5
|
+
const in_memory_2 = require("./adapters/job-queue/in-memory");
|
|
6
|
+
const agent_job_worker_1 = require("./services/agent-job.worker");
|
|
7
|
+
const tool_registry_service_1 = require("./services/tool-registry.service");
|
|
8
|
+
const agent_runner_service_1 = require("./services/agent-runner.service");
|
|
9
|
+
const prepared_stream_service_1 = require("./services/prepared-stream.service");
|
|
10
|
+
const in_memory_prepared_stream_store_1 = require("./services/in-memory-prepared-stream.store");
|
|
11
|
+
const conversation_service_1 = require("./services/conversation.service");
|
|
12
|
+
const agent_service_1 = require("./services/agent.service");
|
|
13
|
+
const orchestrator_service_1 = require("./services/orchestrator.service");
|
|
14
|
+
/**
|
|
15
|
+
* Build a fully-wired AgentForge container from plain inputs. Both
|
|
16
|
+
* @agentforge-io/nest and external bindings call this and expose the services
|
|
17
|
+
* through their transport.
|
|
18
|
+
*/
|
|
19
|
+
function createAgentForge(opts) {
|
|
20
|
+
const { config, repositories, adapters = {}, tools = [], hooks } = opts;
|
|
21
|
+
const logger = adapters.logger;
|
|
22
|
+
// ─── Tool registry + runner + orchestrator ───────────────────────────────
|
|
23
|
+
const toolRegistry = new tool_registry_service_1.ToolRegistryService({ logger, initialTools: tools });
|
|
24
|
+
const runner = new agent_runner_service_1.AgentRunnerService(config.anthropic, toolRegistry, { logger });
|
|
25
|
+
const orchestrator = new orchestrator_service_1.OrchestratorService(config.anthropic, runner, {
|
|
26
|
+
agents: config.agents ?? [],
|
|
27
|
+
logger,
|
|
28
|
+
});
|
|
29
|
+
// ─── Prepared-stream store + service ─────────────────────────────────────
|
|
30
|
+
const preparedStreamStore = adapters.preparedStreamStore ?? new in_memory_prepared_stream_store_1.InMemoryPreparedStreamStore();
|
|
31
|
+
const preparedStream = new prepared_stream_service_1.PreparedStreamService(preparedStreamStore);
|
|
32
|
+
// ─── Rate limiter (defaults to in-memory) ────────────────────────────────
|
|
33
|
+
const rateLimiter = adapters.rateLimiter ?? new in_memory_1.InMemoryRateLimiter();
|
|
34
|
+
// ─── Conversations + agents ──────────────────────────────────────────────
|
|
35
|
+
const conversations = new conversation_service_1.ConversationService(repositories.conversations, repositories.messages);
|
|
36
|
+
const agents = new agent_service_1.AgentService(config.agents ?? [], runner, conversations, adapters.agentResolver, hooks);
|
|
37
|
+
// ─── Background-job worker + queue (in-memory default) ───────────────────
|
|
38
|
+
const agentJobWorker = new agent_job_worker_1.AgentJobWorker(orchestrator, conversations, {
|
|
39
|
+
logger,
|
|
40
|
+
hooks,
|
|
41
|
+
});
|
|
42
|
+
const jobQueue = adapters.jobQueue ??
|
|
43
|
+
new in_memory_2.InMemoryJobQueue((payload, ctx) => agentJobWorker.process(payload, ctx));
|
|
44
|
+
return {
|
|
45
|
+
config,
|
|
46
|
+
toolRegistry,
|
|
47
|
+
runner,
|
|
48
|
+
orchestrator,
|
|
49
|
+
preparedStream,
|
|
50
|
+
conversations,
|
|
51
|
+
agents,
|
|
52
|
+
rateLimiter,
|
|
53
|
+
jobQueue,
|
|
54
|
+
agentJobWorker,
|
|
55
|
+
};
|
|
56
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { AGENT_FORGE_CONFIG, AGENT_QUEUE_NAME, CURRENT_USER, } from './constants';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export * from './domain';
|
|
4
|
+
export * from './repositories';
|
|
5
|
+
export * from './repositories/in-memory';
|
|
6
|
+
export { PREPARED_STREAM_STORE, type PreparedStreamStore, type PreparedStreamPayload, } from './adapters/prepared-stream/prepared-stream.types';
|
|
7
|
+
export { RATE_LIMITER, type RateLimiter, type RateLimitOptions, type RateLimitResult, } from './adapters/rate-limiter/rate-limiter.types';
|
|
8
|
+
export { InMemoryRateLimiter } from './adapters/rate-limiter/in-memory';
|
|
9
|
+
export { RedisRateLimiter, type RedisLike } from './adapters/rate-limiter/redis';
|
|
10
|
+
export { JOB_QUEUE, type JobQueue, type JobStatus, type JobState, type JobContext, type JobProcessor, type EnqueueOptions, type QueueMetrics, } from './adapters/job-queue/job-queue.types';
|
|
11
|
+
export { InMemoryJobQueue, type InMemoryJobQueueOptions, } from './adapters/job-queue/in-memory';
|
|
12
|
+
export * from './services';
|
|
13
|
+
export type { AgentResolver, AgentRecord, AgentResolveParams, } from './services/agent.service';
|
|
14
|
+
export { createAgentForge, type CreateAgentForgeOptions, type AgentForgeContainer, type AgentForgeRepositories, type AgentForgeAdapters, } from './factory';
|