@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.
Files changed (151) hide show
  1. package/dist/adapters/billing/billing-adapter.interface.d.ts +41 -0
  2. package/dist/adapters/billing/billing-adapter.interface.js +5 -0
  3. package/dist/adapters/billing/stripe/stripe.adapter.d.ts +30 -0
  4. package/dist/adapters/billing/stripe/stripe.adapter.js +122 -0
  5. package/dist/adapters/email/email-adapter.interface.d.ts +25 -0
  6. package/dist/adapters/email/email-adapter.interface.js +6 -0
  7. package/dist/adapters/email/noop.adapter.d.ts +10 -0
  8. package/dist/adapters/email/noop.adapter.js +15 -0
  9. package/dist/adapters/email/resend.adapter.d.ts +8 -0
  10. package/dist/adapters/email/resend.adapter.js +39 -0
  11. package/dist/adapters/job-queue/in-memory.d.ts +43 -0
  12. package/dist/adapters/job-queue/in-memory.js +154 -0
  13. package/dist/adapters/job-queue/job-queue.types.d.ts +76 -0
  14. package/dist/adapters/job-queue/job-queue.types.js +5 -0
  15. package/dist/adapters/prepared-stream/prepared-stream.types.d.ts +23 -0
  16. package/dist/adapters/prepared-stream/prepared-stream.types.js +5 -0
  17. package/dist/adapters/rate-limiter/in-memory.d.ts +19 -0
  18. package/dist/adapters/rate-limiter/in-memory.js +63 -0
  19. package/dist/adapters/rate-limiter/rate-limiter.types.d.ts +42 -0
  20. package/dist/adapters/rate-limiter/rate-limiter.types.js +5 -0
  21. package/dist/adapters/rate-limiter/redis.d.ts +31 -0
  22. package/dist/adapters/rate-limiter/redis.js +47 -0
  23. package/dist/adapters/upload/noop.adapter.d.ts +9 -0
  24. package/dist/adapters/upload/noop.adapter.js +14 -0
  25. package/dist/adapters/upload/s3.adapter.d.ts +38 -0
  26. package/dist/adapters/upload/s3.adapter.js +69 -0
  27. package/dist/adapters/upload/upload-adapter.interface.d.ts +37 -0
  28. package/dist/adapters/upload/upload-adapter.interface.js +15 -0
  29. package/dist/ai/index.d.ts +15 -0
  30. package/dist/ai/index.js +43 -0
  31. package/dist/billing/index.d.ts +12 -0
  32. package/dist/billing/index.js +28 -0
  33. package/dist/constants.d.ts +3 -0
  34. package/dist/constants.js +8 -0
  35. package/dist/domain/agent.d.ts +59 -0
  36. package/dist/domain/agent.js +2 -0
  37. package/dist/domain/api-key.d.ts +28 -0
  38. package/dist/domain/api-key.js +2 -0
  39. package/dist/domain/auth-identity.d.ts +10 -0
  40. package/dist/domain/auth-identity.js +2 -0
  41. package/dist/domain/chat-token.d.ts +39 -0
  42. package/dist/domain/chat-token.js +2 -0
  43. package/dist/domain/connector-auth.d.ts +42 -0
  44. package/dist/domain/connector-auth.js +2 -0
  45. package/dist/domain/connector.d.ts +52 -0
  46. package/dist/domain/connector.js +2 -0
  47. package/dist/domain/conversation.d.ts +26 -0
  48. package/dist/domain/conversation.js +2 -0
  49. package/dist/domain/email-token.d.ts +11 -0
  50. package/dist/domain/email-token.js +2 -0
  51. package/dist/domain/external-user.d.ts +23 -0
  52. package/dist/domain/external-user.js +2 -0
  53. package/dist/domain/index.d.ts +5 -0
  54. package/dist/domain/index.js +24 -0
  55. package/dist/domain/mcp-server.d.ts +33 -0
  56. package/dist/domain/mcp-server.js +2 -0
  57. package/dist/domain/plan.d.ts +20 -0
  58. package/dist/domain/plan.js +2 -0
  59. package/dist/domain/platform-secret.d.ts +24 -0
  60. package/dist/domain/platform-secret.js +8 -0
  61. package/dist/domain/refresh-token.d.ts +15 -0
  62. package/dist/domain/refresh-token.js +2 -0
  63. package/dist/domain/subscription.d.ts +21 -0
  64. package/dist/domain/subscription.js +2 -0
  65. package/dist/domain/tenant.d.ts +21 -0
  66. package/dist/domain/tenant.js +2 -0
  67. package/dist/domain/usage-record.d.ts +15 -0
  68. package/dist/domain/usage-record.js +2 -0
  69. package/dist/domain/user.d.ts +43 -0
  70. package/dist/domain/user.js +2 -0
  71. package/dist/factory.d.ts +68 -0
  72. package/dist/factory.js +56 -0
  73. package/dist/index.d.ts +14 -0
  74. package/dist/index.js +59 -0
  75. package/dist/repositories/in-memory.d.ts +30 -0
  76. package/dist/repositories/in-memory.js +82 -0
  77. package/dist/repositories/index.d.ts +67 -0
  78. package/dist/repositories/index.js +16 -0
  79. package/dist/services/agent-config.service.d.ts +45 -0
  80. package/dist/services/agent-config.service.js +114 -0
  81. package/dist/services/agent-job.worker.d.ts +32 -0
  82. package/dist/services/agent-job.worker.js +97 -0
  83. package/dist/services/agent-runner.service.d.ts +35 -0
  84. package/dist/services/agent-runner.service.js +224 -0
  85. package/dist/services/agent.service.d.ts +171 -0
  86. package/dist/services/agent.service.js +329 -0
  87. package/dist/services/api-key.service.d.ts +41 -0
  88. package/dist/services/api-key.service.js +80 -0
  89. package/dist/services/auth.service.d.ts +133 -0
  90. package/dist/services/auth.service.js +411 -0
  91. package/dist/services/billing.service.d.ts +67 -0
  92. package/dist/services/billing.service.js +254 -0
  93. package/dist/services/chat-token.service.d.ts +29 -0
  94. package/dist/services/chat-token.service.js +113 -0
  95. package/dist/services/connector-registry.service.d.ts +156 -0
  96. package/dist/services/connector-registry.service.js +278 -0
  97. package/dist/services/conversation.service.d.ts +47 -0
  98. package/dist/services/conversation.service.js +101 -0
  99. package/dist/services/email-templates.d.ts +18 -0
  100. package/dist/services/email-templates.js +39 -0
  101. package/dist/services/email.service.d.ts +26 -0
  102. package/dist/services/email.service.js +42 -0
  103. package/dist/services/errors.d.ts +7 -0
  104. package/dist/services/errors.js +27 -0
  105. package/dist/services/in-memory-prepared-stream.store.d.ts +13 -0
  106. package/dist/services/in-memory-prepared-stream.store.js +35 -0
  107. package/dist/services/index.d.ts +13 -0
  108. package/dist/services/index.js +40 -0
  109. package/dist/services/mcp-client.service.d.ts +64 -0
  110. package/dist/services/mcp-client.service.js +157 -0
  111. package/dist/services/mcp-server.service.d.ts +44 -0
  112. package/dist/services/mcp-server.service.js +147 -0
  113. package/dist/services/oauth.service.d.ts +73 -0
  114. package/dist/services/oauth.service.js +174 -0
  115. package/dist/services/oauth2.service.d.ts +57 -0
  116. package/dist/services/oauth2.service.js +82 -0
  117. package/dist/services/orchestrator.service.d.ts +45 -0
  118. package/dist/services/orchestrator.service.js +180 -0
  119. package/dist/services/plan.service.d.ts +54 -0
  120. package/dist/services/plan.service.js +120 -0
  121. package/dist/services/prepared-stream.service.d.ts +23 -0
  122. package/dist/services/prepared-stream.service.js +43 -0
  123. package/dist/services/refresh-token.service.d.ts +38 -0
  124. package/dist/services/refresh-token.service.js +73 -0
  125. package/dist/services/secrets/crypto.d.ts +37 -0
  126. package/dist/services/secrets/crypto.js +110 -0
  127. package/dist/services/secrets/known-keys.d.ts +38 -0
  128. package/dist/services/secrets/known-keys.js +50 -0
  129. package/dist/services/secrets.service.d.ts +91 -0
  130. package/dist/services/secrets.service.js +193 -0
  131. package/dist/services/tenant-billing.service.d.ts +121 -0
  132. package/dist/services/tenant-billing.service.js +290 -0
  133. package/dist/services/tenant.service.d.ts +54 -0
  134. package/dist/services/tenant.service.js +96 -0
  135. package/dist/services/tool-registry.service.d.ts +42 -0
  136. package/dist/services/tool-registry.service.js +101 -0
  137. package/dist/services/upload.service.d.ts +37 -0
  138. package/dist/services/upload.service.js +84 -0
  139. package/dist/services/usage.service.d.ts +34 -0
  140. package/dist/services/usage.service.js +108 -0
  141. package/dist/types/agent.types.d.ts +160 -0
  142. package/dist/types/agent.types.js +2 -0
  143. package/dist/types/billing.types.d.ts +82 -0
  144. package/dist/types/billing.types.js +3 -0
  145. package/dist/types/config.types.d.ts +127 -0
  146. package/dist/types/config.types.js +9 -0
  147. package/dist/types/hooks.d.ts +85 -0
  148. package/dist/types/hooks.js +2 -0
  149. package/dist/types/index.d.ts +3 -0
  150. package/dist/types/index.js +19 -0
  151. 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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export * from './conversation';
2
+ export * from './chat-token';
3
+ export * from './mcp-server';
4
+ export * from './connector-auth';
5
+ export * from './connector';
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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;
@@ -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
+ }
@@ -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';