@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
package/dist/index.js ADDED
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ // ─── Public API of @agentforge-io/core ─────────────────────────────────────────
3
+ //
4
+ // AI-runtime SDK. After Phase 3 the lib is responsible only for:
5
+ // - Agent loop (Anthropic / future LLM adapters)
6
+ // - Conversation + message persistence contract
7
+ // - Tool registry + execution
8
+ // - Prepared streaming
9
+ // - Background agent-job queue
10
+ // - SdkHooks for host observability
11
+ //
12
+ // Identity, multi-tenancy, billing, infra (email/upload/secrets) all live
13
+ // in the host's own modules. The SDK exposes opaque `userId`, `tenantId`
14
+ // strings; the host validates and enriches them.
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
27
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.createAgentForge = exports.InMemoryJobQueue = exports.JOB_QUEUE = exports.RedisRateLimiter = exports.InMemoryRateLimiter = exports.RATE_LIMITER = exports.PREPARED_STREAM_STORE = exports.CURRENT_USER = exports.AGENT_QUEUE_NAME = exports.AGENT_FORGE_CONFIG = void 0;
31
+ // ─── Constants ──────────────────────────────────────────────────────────────
32
+ var constants_1 = require("./constants");
33
+ Object.defineProperty(exports, "AGENT_FORGE_CONFIG", { enumerable: true, get: function () { return constants_1.AGENT_FORGE_CONFIG; } });
34
+ Object.defineProperty(exports, "AGENT_QUEUE_NAME", { enumerable: true, get: function () { return constants_1.AGENT_QUEUE_NAME; } });
35
+ Object.defineProperty(exports, "CURRENT_USER", { enumerable: true, get: function () { return constants_1.CURRENT_USER; } });
36
+ // ─── Types ──────────────────────────────────────────────────────────────────
37
+ __exportStar(require("./types"), exports);
38
+ __exportStar(require("./domain"), exports);
39
+ // ─── Repository contracts ───────────────────────────────────────────────────
40
+ __exportStar(require("./repositories"), exports);
41
+ __exportStar(require("./repositories/in-memory"), exports);
42
+ // ─── Adapter contracts (runtime infra) ──────────────────────────────────────
43
+ var prepared_stream_types_1 = require("./adapters/prepared-stream/prepared-stream.types");
44
+ Object.defineProperty(exports, "PREPARED_STREAM_STORE", { enumerable: true, get: function () { return prepared_stream_types_1.PREPARED_STREAM_STORE; } });
45
+ var rate_limiter_types_1 = require("./adapters/rate-limiter/rate-limiter.types");
46
+ Object.defineProperty(exports, "RATE_LIMITER", { enumerable: true, get: function () { return rate_limiter_types_1.RATE_LIMITER; } });
47
+ var in_memory_1 = require("./adapters/rate-limiter/in-memory");
48
+ Object.defineProperty(exports, "InMemoryRateLimiter", { enumerable: true, get: function () { return in_memory_1.InMemoryRateLimiter; } });
49
+ var redis_1 = require("./adapters/rate-limiter/redis");
50
+ Object.defineProperty(exports, "RedisRateLimiter", { enumerable: true, get: function () { return redis_1.RedisRateLimiter; } });
51
+ var job_queue_types_1 = require("./adapters/job-queue/job-queue.types");
52
+ Object.defineProperty(exports, "JOB_QUEUE", { enumerable: true, get: function () { return job_queue_types_1.JOB_QUEUE; } });
53
+ var in_memory_2 = require("./adapters/job-queue/in-memory");
54
+ Object.defineProperty(exports, "InMemoryJobQueue", { enumerable: true, get: function () { return in_memory_2.InMemoryJobQueue; } });
55
+ // ─── Services (framework-free) ──────────────────────────────────────────────
56
+ __exportStar(require("./services"), exports);
57
+ // ─── Container factory ──────────────────────────────────────────────────────
58
+ var factory_1 = require("./factory");
59
+ Object.defineProperty(exports, "createAgentForge", { enumerable: true, get: function () { return factory_1.createAgentForge; } });
@@ -0,0 +1,30 @@
1
+ import type { ConversationRepository, MessageRepository } from './index';
2
+ import type { Conversation, NewConversation, Message, NewMessage } from '../domain/conversation';
3
+ import type { ConversationStatus } from '../types/agent.types';
4
+ export declare class InMemoryConversationRepository implements ConversationRepository {
5
+ private byId;
6
+ create(c: NewConversation): Promise<Conversation>;
7
+ findById(id: string): Promise<Conversation | null>;
8
+ findByIdForUser(id: string, userId: string): Promise<Conversation | null>;
9
+ listForUser(userId: string, opts?: {
10
+ status?: ConversationStatus;
11
+ limit?: number;
12
+ offset?: number;
13
+ }): Promise<Conversation[]>;
14
+ updateStats(id: string, patch: {
15
+ status?: ConversationStatus;
16
+ addInputTokens?: number;
17
+ addOutputTokens?: number;
18
+ addMessages?: number;
19
+ }): Promise<void>;
20
+ }
21
+ export declare class InMemoryMessageRepository implements MessageRepository {
22
+ private items;
23
+ create(m: NewMessage): Promise<Message>;
24
+ listForConversation(conversationId: string, _userId: string): Promise<Message[]>;
25
+ }
26
+ /** Build a minimal in-memory repo bundle for tests/demos. */
27
+ export declare function createInMemoryRepositories(): {
28
+ conversations: InMemoryConversationRepository;
29
+ messages: InMemoryMessageRepository;
30
+ };
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ // In-memory repositories for tests + demos. Production should use TypeORM
3
+ // adapters (@agentforge-io/typeorm). After Phase 3, only conversation + message
4
+ // remain — identity / multi-tenant / billing etc. all moved to the host.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InMemoryMessageRepository = exports.InMemoryConversationRepository = void 0;
7
+ exports.createInMemoryRepositories = createInMemoryRepositories;
8
+ const crypto_1 = require("crypto");
9
+ class InMemoryConversationRepository {
10
+ constructor() {
11
+ this.byId = new Map();
12
+ }
13
+ async create(c) {
14
+ const now = new Date();
15
+ const full = {
16
+ id: (0, crypto_1.randomUUID)(),
17
+ status: 'active',
18
+ totalInputTokens: 0,
19
+ totalOutputTokens: 0,
20
+ messageCount: 0,
21
+ createdAt: now,
22
+ updatedAt: now,
23
+ ...c,
24
+ };
25
+ this.byId.set(full.id, full);
26
+ return full;
27
+ }
28
+ async findById(id) {
29
+ return this.byId.get(id) ?? null;
30
+ }
31
+ async findByIdForUser(id, userId) {
32
+ const c = this.byId.get(id);
33
+ return c && c.userId === userId ? c : null;
34
+ }
35
+ async listForUser(userId, opts = {}) {
36
+ let items = Array.from(this.byId.values()).filter((c) => c.userId === userId);
37
+ if (opts.status)
38
+ items = items.filter((c) => c.status === opts.status);
39
+ items.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
40
+ const start = opts.offset ?? 0;
41
+ const end = opts.limit ? start + opts.limit : undefined;
42
+ return items.slice(start, end);
43
+ }
44
+ async updateStats(id, patch) {
45
+ const c = this.byId.get(id);
46
+ if (!c)
47
+ return;
48
+ if (patch.status !== undefined)
49
+ c.status = patch.status;
50
+ if (patch.addInputTokens)
51
+ c.totalInputTokens += patch.addInputTokens;
52
+ if (patch.addOutputTokens)
53
+ c.totalOutputTokens += patch.addOutputTokens;
54
+ if (patch.addMessages)
55
+ c.messageCount += patch.addMessages;
56
+ c.updatedAt = new Date();
57
+ }
58
+ }
59
+ exports.InMemoryConversationRepository = InMemoryConversationRepository;
60
+ class InMemoryMessageRepository {
61
+ constructor() {
62
+ this.items = [];
63
+ }
64
+ async create(m) {
65
+ const full = { id: (0, crypto_1.randomUUID)(), createdAt: new Date(), ...m };
66
+ this.items.push(full);
67
+ return full;
68
+ }
69
+ async listForConversation(conversationId, _userId) {
70
+ return this.items
71
+ .filter((m) => m.conversationId === conversationId)
72
+ .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
73
+ }
74
+ }
75
+ exports.InMemoryMessageRepository = InMemoryMessageRepository;
76
+ /** Build a minimal in-memory repo bundle for tests/demos. */
77
+ function createInMemoryRepositories() {
78
+ return {
79
+ conversations: new InMemoryConversationRepository(),
80
+ messages: new InMemoryMessageRepository(),
81
+ };
82
+ }
@@ -0,0 +1,67 @@
1
+ import type { Conversation, NewConversation, Message, NewMessage } from '../domain/conversation';
2
+ import type { ChatToken, NewChatToken, ChatTokenPatch } from '../domain/chat-token';
3
+ import type { McpServerRecord, NewMcpServerRecord, McpServerRecordPatch } from '../domain/mcp-server';
4
+ import type { ConnectorAuth, NewConnectorAuth, ConnectorAuthPatch } from '../domain/connector-auth';
5
+ import type { ConversationStatus } from '../types/agent.types';
6
+ export declare const CONVERSATION_REPOSITORY = "AGENTFORGE_CONVERSATION_REPOSITORY";
7
+ export declare const MESSAGE_REPOSITORY = "AGENTFORGE_MESSAGE_REPOSITORY";
8
+ export declare const CHAT_TOKEN_REPOSITORY = "AGENTFORGE_CHAT_TOKEN_REPOSITORY";
9
+ export declare const MCP_SERVER_REPOSITORY = "AGENTFORGE_MCP_SERVER_REPOSITORY";
10
+ export declare const CONNECTOR_AUTH_REPOSITORY = "AGENTFORGE_CONNECTOR_AUTH_REPOSITORY";
11
+ export interface ConversationRepository {
12
+ create(conv: NewConversation): Promise<Conversation>;
13
+ findById(id: string): Promise<Conversation | null>;
14
+ /** Same as findById but also enforces ownership — used in user-scoped routes. */
15
+ findByIdForUser(id: string, userId: string): Promise<Conversation | null>;
16
+ /**
17
+ * List the user's conversations, newest first. `userId` is opaque — the
18
+ * SDK never validates it; the host's JWT guard does.
19
+ */
20
+ listForUser(userId: string, opts?: {
21
+ status?: ConversationStatus;
22
+ limit?: number;
23
+ offset?: number;
24
+ }): Promise<Conversation[]>;
25
+ updateStats(id: string, patch: {
26
+ status?: ConversationStatus;
27
+ addInputTokens?: number;
28
+ addOutputTokens?: number;
29
+ addMessages?: number;
30
+ }): Promise<void>;
31
+ }
32
+ export interface MessageRepository {
33
+ create(message: NewMessage): Promise<Message>;
34
+ listForConversation(conversationId: string, userId: string): Promise<Message[]>;
35
+ }
36
+ export interface ChatTokenRepository {
37
+ create(token: NewChatToken): Promise<ChatToken>;
38
+ /** Hot path — every public widget request looks the token up by string. */
39
+ findByToken(token: string): Promise<ChatToken | null>;
40
+ listForAgent(tenantId: string, agentId: string): Promise<ChatToken[]>;
41
+ update(token: string, patch: ChatTokenPatch): Promise<void>;
42
+ /** Soft-revoke. */
43
+ deactivate(token: string): Promise<void>;
44
+ /** Updates last_used_at — called on every public hit. */
45
+ touch(token: string, at?: Date): Promise<void>;
46
+ }
47
+ export interface McpServerRepository {
48
+ create(record: NewMcpServerRecord): Promise<McpServerRecord>;
49
+ findById(id: string): Promise<McpServerRecord | null>;
50
+ /** All servers for a tenant, including inactive ones (admin UI shows both). */
51
+ listForTenant(tenantId: string): Promise<McpServerRecord[]>;
52
+ /** All active servers across all tenants — used at boot to register
53
+ * connections. The host can pass `tenantId` to scope to a single tenant. */
54
+ listActive(tenantId?: string): Promise<McpServerRecord[]>;
55
+ update(id: string, patch: McpServerRecordPatch): Promise<void>;
56
+ delete(id: string): Promise<void>;
57
+ }
58
+ export interface ConnectorAuthRepository {
59
+ create(record: NewConnectorAuth): Promise<ConnectorAuth>;
60
+ /** Hot path — every tool call resolves the user's token here. */
61
+ findByUserAndConnector(userId: string, connectorId: string): Promise<ConnectorAuth | null>;
62
+ /** All connectors a user has authorized — for the Directory UI. */
63
+ listForUser(userId: string): Promise<ConnectorAuth[]>;
64
+ update(id: string, patch: ConnectorAuthPatch): Promise<void>;
65
+ /** Hard delete on revoke. The user can re-auth fresh if they want back in. */
66
+ delete(id: string): Promise<void>;
67
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ // ─── Repository contracts (AI runtime only) ───────────────────────────────
3
+ // Plain interfaces over conversation + message persistence. Implementations
4
+ // live in framework adapters (TypeORM in @agentforge-io/typeorm, but consumers
5
+ // can plug in Drizzle/Prisma/anything).
6
+ //
7
+ // Identity, multi-tenancy, billing, infra all moved out of the SDK. The host
8
+ // provides those concerns as its own modules.
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.CONNECTOR_AUTH_REPOSITORY = exports.MCP_SERVER_REPOSITORY = exports.CHAT_TOKEN_REPOSITORY = exports.MESSAGE_REPOSITORY = exports.CONVERSATION_REPOSITORY = void 0;
11
+ // ─── Tokens ────────────────────────────────────────────────────────────────
12
+ exports.CONVERSATION_REPOSITORY = 'AGENTFORGE_CONVERSATION_REPOSITORY';
13
+ exports.MESSAGE_REPOSITORY = 'AGENTFORGE_MESSAGE_REPOSITORY';
14
+ exports.CHAT_TOKEN_REPOSITORY = 'AGENTFORGE_CHAT_TOKEN_REPOSITORY';
15
+ exports.MCP_SERVER_REPOSITORY = 'AGENTFORGE_MCP_SERVER_REPOSITORY';
16
+ exports.CONNECTOR_AUTH_REPOSITORY = 'AGENTFORGE_CONNECTOR_AUTH_REPOSITORY';
@@ -0,0 +1,45 @@
1
+ import type { AgentConfigRepository, AgentConfigListOptions, AgentConfigListResult } from '../repositories';
2
+ import type { AgentRecord, AgentRecordPatch } from '../domain/agent';
3
+ export declare class AgentConfigError extends Error {
4
+ status: number;
5
+ code: 'not_found' | 'duplicate_slug' | 'invalid' | 'forbidden';
6
+ constructor(code: AgentConfigError['code'], message: string);
7
+ }
8
+ export interface AgentConfigCreateInput {
9
+ tenantId: string;
10
+ slug: string;
11
+ name: string;
12
+ systemPrompt: string;
13
+ description?: string;
14
+ model?: string;
15
+ context?: string;
16
+ temperature?: number;
17
+ topP?: number;
18
+ maxTokens?: number;
19
+ tools?: string[];
20
+ isActive?: boolean;
21
+ createdByUserId?: string;
22
+ metadata?: Record<string, unknown>;
23
+ }
24
+ /**
25
+ * Application-level CRUD for per-tenant agent configurations. Controllers
26
+ * call this — they do NOT touch the repository directly so we can keep
27
+ * validation (slug format, unique-per-tenant) in one place.
28
+ */
29
+ export declare class AgentConfigService {
30
+ private readonly repo;
31
+ constructor(repo: AgentConfigRepository);
32
+ create(input: AgentConfigCreateInput): Promise<AgentRecord>;
33
+ getById(id: string): Promise<AgentRecord>;
34
+ /**
35
+ * Confirms the agent belongs to `tenantId`. Used by admin endpoints so a
36
+ * tenant can't fetch another tenant's agent by guessing the id.
37
+ */
38
+ getByIdForTenant(id: string, tenantId: string): Promise<AgentRecord>;
39
+ /** SDK hot path. Returns null on miss — callers decide whether to 404 or
40
+ * fall back to the legacy hardcoded array. */
41
+ findBySlug(tenantId: string, slug: string): Promise<AgentRecord | null>;
42
+ listForTenant(tenantId: string, opts?: AgentConfigListOptions): Promise<AgentConfigListResult>;
43
+ update(id: string, tenantId: string, patch: AgentRecordPatch): Promise<AgentRecord>;
44
+ deactivate(id: string, tenantId: string): Promise<void>;
45
+ }
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentConfigService = exports.AgentConfigError = void 0;
4
+ const crypto_1 = require("crypto");
5
+ class AgentConfigError extends Error {
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.code = code;
9
+ this.status =
10
+ code === 'forbidden'
11
+ ? 403
12
+ : code === 'not_found'
13
+ ? 404
14
+ : code === 'duplicate_slug'
15
+ ? 409
16
+ : 400;
17
+ }
18
+ }
19
+ exports.AgentConfigError = AgentConfigError;
20
+ // Slug rules are deliberately strict so they're URL-safe and predictable for
21
+ // SDK callers: lowercase letters, digits, hyphen, underscore. Length 2..64.
22
+ const SLUG_RE = /^[a-z0-9][a-z0-9_-]{1,62}[a-z0-9]$/;
23
+ /**
24
+ * Application-level CRUD for per-tenant agent configurations. Controllers
25
+ * call this — they do NOT touch the repository directly so we can keep
26
+ * validation (slug format, unique-per-tenant) in one place.
27
+ */
28
+ class AgentConfigService {
29
+ constructor(repo) {
30
+ this.repo = repo;
31
+ }
32
+ async create(input) {
33
+ if (!input.tenantId)
34
+ throw new AgentConfigError('invalid', 'tenantId is required');
35
+ if (!input.name?.trim())
36
+ throw new AgentConfigError('invalid', 'name is required');
37
+ if (!input.systemPrompt?.trim())
38
+ throw new AgentConfigError('invalid', 'systemPrompt is required');
39
+ const slug = input.slug.trim().toLowerCase();
40
+ if (!SLUG_RE.test(slug)) {
41
+ throw new AgentConfigError('invalid', 'slug must be 3–64 chars, lowercase letters/digits/hyphen/underscore, start and end alphanumeric');
42
+ }
43
+ const existing = await this.repo.findBySlug(input.tenantId, slug);
44
+ if (existing) {
45
+ throw new AgentConfigError('duplicate_slug', `An agent with slug "${slug}" already exists for this tenant.`);
46
+ }
47
+ const record = {
48
+ id: (0, crypto_1.randomUUID)(),
49
+ tenantId: input.tenantId,
50
+ slug,
51
+ name: input.name.trim(),
52
+ systemPrompt: input.systemPrompt,
53
+ description: input.description?.trim(),
54
+ model: input.model,
55
+ context: input.context,
56
+ temperature: input.temperature,
57
+ topP: input.topP,
58
+ maxTokens: input.maxTokens,
59
+ tools: input.tools,
60
+ isActive: input.isActive ?? true,
61
+ createdByUserId: input.createdByUserId,
62
+ metadata: input.metadata,
63
+ };
64
+ return this.repo.create(record);
65
+ }
66
+ async getById(id) {
67
+ const a = await this.repo.findById(id);
68
+ if (!a)
69
+ throw new AgentConfigError('not_found', 'Agent not found');
70
+ return a;
71
+ }
72
+ /**
73
+ * Confirms the agent belongs to `tenantId`. Used by admin endpoints so a
74
+ * tenant can't fetch another tenant's agent by guessing the id.
75
+ */
76
+ async getByIdForTenant(id, tenantId) {
77
+ const a = await this.getById(id);
78
+ if (a.tenantId !== tenantId) {
79
+ // Don't reveal existence — return not_found.
80
+ throw new AgentConfigError('not_found', 'Agent not found');
81
+ }
82
+ return a;
83
+ }
84
+ /** SDK hot path. Returns null on miss — callers decide whether to 404 or
85
+ * fall back to the legacy hardcoded array. */
86
+ async findBySlug(tenantId, slug) {
87
+ return this.repo.findBySlug(tenantId, slug.toLowerCase());
88
+ }
89
+ async listForTenant(tenantId, opts = {}) {
90
+ return this.repo.listForTenant(tenantId, opts);
91
+ }
92
+ async update(id, tenantId, patch) {
93
+ const current = await this.getByIdForTenant(id, tenantId);
94
+ // Slug changes need re-validation + uniqueness check.
95
+ if (patch.slug !== undefined && patch.slug !== current.slug) {
96
+ const nextSlug = patch.slug.trim().toLowerCase();
97
+ if (!SLUG_RE.test(nextSlug)) {
98
+ throw new AgentConfigError('invalid', 'slug must be 3–64 chars, lowercase letters/digits/hyphen/underscore');
99
+ }
100
+ const dup = await this.repo.findBySlug(tenantId, nextSlug);
101
+ if (dup && dup.id !== id) {
102
+ throw new AgentConfigError('duplicate_slug', `An agent with slug "${nextSlug}" already exists for this tenant.`);
103
+ }
104
+ patch.slug = nextSlug;
105
+ }
106
+ await this.repo.update(id, patch);
107
+ return this.getById(id);
108
+ }
109
+ async deactivate(id, tenantId) {
110
+ await this.getByIdForTenant(id, tenantId);
111
+ await this.repo.deactivate(id);
112
+ }
113
+ }
114
+ exports.AgentConfigService = AgentConfigService;
@@ -0,0 +1,32 @@
1
+ import type { AgentJobPayload, AgentJobResult } from '../types/agent.types';
2
+ import type { JobContext } from '../adapters/job-queue/job-queue.types';
3
+ import type { SdkHooks } from '../types/hooks';
4
+ import type { OrchestratorService } from './orchestrator.service';
5
+ import type { ConversationService } from './conversation.service';
6
+ import type { Logger } from './tool-registry.service';
7
+ /**
8
+ * Framework-free worker for the `agent-turn` job kind. Both the Nest BullMQ
9
+ * processor and any in-memory JobQueue (Express adapter, custom integrations)
10
+ * delegate to `process(payload)` here — the transport decides how a payload
11
+ * is delivered, this class owns what happens with one.
12
+ *
13
+ * Side effects, in order:
14
+ * 1. Run the orchestrator for `agentId` with the supplied messages.
15
+ * 2. Persist the assistant message via ConversationService.
16
+ * 3. Emit `onUsage` + `onTurnComplete` hooks for the host to handle.
17
+ *
18
+ * Progress is reported through the JobContext when provided (10% before the
19
+ * run, 70% after persistence, 100% on return). Callers without a queue can
20
+ * call `process(payload)` with no context.
21
+ */
22
+ export declare class AgentJobWorker {
23
+ private readonly orchestrator;
24
+ private readonly conversations;
25
+ private readonly logger;
26
+ private readonly hooks?;
27
+ constructor(orchestrator: OrchestratorService, conversations: ConversationService, opts?: {
28
+ logger?: Logger;
29
+ hooks?: SdkHooks;
30
+ });
31
+ process(payload: AgentJobPayload, ctx?: JobContext): Promise<AgentJobResult>;
32
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentJobWorker = void 0;
4
+ const noopLogger = {
5
+ log: () => { }, warn: () => { }, debug: () => { }, error: () => { },
6
+ };
7
+ /**
8
+ * Framework-free worker for the `agent-turn` job kind. Both the Nest BullMQ
9
+ * processor and any in-memory JobQueue (Express adapter, custom integrations)
10
+ * delegate to `process(payload)` here — the transport decides how a payload
11
+ * is delivered, this class owns what happens with one.
12
+ *
13
+ * Side effects, in order:
14
+ * 1. Run the orchestrator for `agentId` with the supplied messages.
15
+ * 2. Persist the assistant message via ConversationService.
16
+ * 3. Emit `onUsage` + `onTurnComplete` hooks for the host to handle.
17
+ *
18
+ * Progress is reported through the JobContext when provided (10% before the
19
+ * run, 70% after persistence, 100% on return). Callers without a queue can
20
+ * call `process(payload)` with no context.
21
+ */
22
+ class AgentJobWorker {
23
+ constructor(orchestrator, conversations, opts = {}) {
24
+ this.orchestrator = orchestrator;
25
+ this.conversations = conversations;
26
+ this.logger = opts.logger ?? noopLogger;
27
+ this.hooks = opts.hooks;
28
+ }
29
+ async process(payload, ctx) {
30
+ const { jobId, userId, conversationId, agentId, messages } = payload;
31
+ this.logger.log(`Processing agent job ${jobId} for user ${userId}`);
32
+ await ctx?.updateProgress(10);
33
+ const response = await this.orchestrator.run(agentId, messages, {
34
+ userId,
35
+ conversationId,
36
+ agentId,
37
+ messageId: jobId,
38
+ });
39
+ await ctx?.updateProgress(70);
40
+ await this.conversations.addMessage({
41
+ conversationId,
42
+ userId,
43
+ role: 'assistant',
44
+ content: response.content,
45
+ toolCalls: response.toolCalls,
46
+ usage: response.usage,
47
+ });
48
+ if (this.hooks?.onUsage || this.hooks?.onTurnComplete) {
49
+ const now = new Date();
50
+ if (this.hooks.onUsage) {
51
+ try {
52
+ await this.hooks.onUsage({
53
+ version: 1,
54
+ turnId: response.messageId,
55
+ conversationId,
56
+ agentId,
57
+ userId,
58
+ provider: 'anthropic',
59
+ modelId: 'unknown',
60
+ inputTokens: response.usage.inputTokens,
61
+ outputTokens: response.usage.outputTokens,
62
+ durationMs: 0,
63
+ at: now,
64
+ });
65
+ }
66
+ catch (err) {
67
+ this.logger.warn(`[agentforge] onUsage hook threw: ${String(err)}`);
68
+ }
69
+ }
70
+ if (this.hooks.onTurnComplete) {
71
+ try {
72
+ await this.hooks.onTurnComplete({
73
+ version: 1,
74
+ turnId: response.messageId,
75
+ conversationId,
76
+ agentId,
77
+ userId,
78
+ status: 'ok',
79
+ toolCallCount: response.toolCalls?.length ?? 0,
80
+ durationMs: 0,
81
+ at: now,
82
+ });
83
+ }
84
+ catch (err) {
85
+ this.logger.warn(`[agentforge] onTurnComplete hook threw: ${String(err)}`);
86
+ }
87
+ }
88
+ }
89
+ await ctx?.updateProgress(100);
90
+ return {
91
+ jobId,
92
+ response,
93
+ completedAt: new Date(),
94
+ };
95
+ }
96
+ }
97
+ exports.AgentJobWorker = AgentJobWorker;
@@ -0,0 +1,35 @@
1
+ import type { AgentOverrides, AgentResponse, AnthropicMessage, StreamChunk, ToolExecutionContext } from '../types/agent.types';
2
+ import type { AgentDefinition, AnthropicConfig } from '../types/config.types';
3
+ import type { ToolRegistryService } from './tool-registry.service';
4
+ import type { Logger } from './tool-registry.service';
5
+ /**
6
+ * Framework-free runner for Claude. Handles the agentic loop (tool calls) for
7
+ * sync runs and exposes streaming as an `AsyncGenerator<StreamChunk>` so any
8
+ * transport (SSE, fetch+ReadableStream, WebSocket, etc.) can consume it.
9
+ */
10
+ export declare class AgentRunnerService {
11
+ private readonly anthropicConfig;
12
+ private readonly toolRegistry;
13
+ private readonly client;
14
+ private readonly logger;
15
+ constructor(anthropicConfig: AnthropicConfig, toolRegistry: ToolRegistryService, opts?: {
16
+ logger?: Logger;
17
+ });
18
+ run(agent: AgentDefinition, messages: AnthropicMessage[], context: ToolExecutionContext, overrides?: AgentOverrides): Promise<AgentResponse>;
19
+ stream(agent: AgentDefinition, messages: AnthropicMessage[], context: ToolExecutionContext, overrides?: AgentOverrides): AsyncGenerator<StreamChunk>;
20
+ /**
21
+ * Merge `agent.tools[]` (resolved via the global registry) with any
22
+ * per-call extras (e.g. the user's connector tools).
23
+ *
24
+ * Returns:
25
+ * - `tools`: the Anthropic-shaped array sent to the LLM
26
+ * - `extras`: a name→definition map so `dispatchTool` can route execution
27
+ * for tools that aren't in the global registry
28
+ *
29
+ * Extras with the same name as a global tool shadow the global one — useful
30
+ * when a connector wants to override the built-in `http_request`, for
31
+ * example. The shadowing only lasts for this call.
32
+ */
33
+ private buildToolList;
34
+ private dispatchTool;
35
+ }