@amodalai/runtime 0.2.0 → 0.2.2
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/src/__fixtures__/README.md +4 -0
- package/dist/src/{agent/user-context-fetcher.test.d.ts → __fixtures__/e2e.test.d.ts} +1 -1
- package/dist/src/__fixtures__/e2e.test.js +211 -0
- package/dist/src/__fixtures__/e2e.test.js.map +1 -0
- package/dist/src/__fixtures__/smoke-agent/automations/delivery-callback-test.json +9 -0
- package/dist/src/__fixtures__/smoke-agent/connections/mock-mcp/spec.json +1 -1
- package/dist/src/__fixtures__/smoke.test.js +715 -29
- package/dist/src/__fixtures__/smoke.test.js.map +1 -1
- package/dist/src/__fixtures__/test-env.d.ts +27 -0
- package/dist/src/__fixtures__/test-env.js +64 -0
- package/dist/src/__fixtures__/test-env.js.map +1 -0
- package/dist/src/__fixtures__/test-helpers.d.ts +30 -0
- package/dist/src/__fixtures__/test-helpers.js +120 -0
- package/dist/src/__fixtures__/test-helpers.js.map +1 -0
- package/dist/src/agent/agent-types.d.ts +22 -0
- package/dist/src/agent/agent-types.js.map +1 -1
- package/dist/src/agent/automation-bridge.d.ts +9 -0
- package/dist/src/agent/automation-bridge.js +26 -0
- package/dist/src/agent/automation-bridge.js.map +1 -1
- package/dist/src/agent/automation-bridge.test.js +63 -0
- package/dist/src/agent/automation-bridge.test.js.map +1 -1
- package/dist/src/agent/local-server.d.ts +0 -7
- package/dist/src/agent/local-server.js +274 -87
- package/dist/src/agent/local-server.js.map +1 -1
- package/dist/src/agent/local-server.test.js +14 -11
- package/dist/src/agent/local-server.test.js.map +1 -1
- package/dist/src/agent/loop-types.d.ts +81 -7
- package/dist/src/agent/loop-types.js +4 -0
- package/dist/src/agent/loop-types.js.map +1 -1
- package/dist/src/agent/loop.js +16 -3
- package/dist/src/agent/loop.js.map +1 -1
- package/dist/src/agent/loop.test.js +572 -10
- package/dist/src/agent/loop.test.js.map +1 -1
- package/dist/src/agent/page-builder.js +20 -17
- package/dist/src/agent/page-builder.js.map +1 -1
- package/dist/src/agent/proactive/delivery-router.d.ts +68 -0
- package/dist/src/agent/proactive/delivery-router.js +337 -0
- package/dist/src/agent/proactive/delivery-router.js.map +1 -0
- package/dist/src/agent/proactive/delivery-router.test.d.ts +6 -0
- package/dist/src/agent/proactive/delivery-router.test.js +455 -0
- package/dist/src/agent/proactive/delivery-router.test.js.map +1 -0
- package/dist/src/agent/proactive/proactive-runner.d.ts +23 -1
- package/dist/src/agent/proactive/proactive-runner.js +42 -10
- package/dist/src/agent/proactive/proactive-runner.js.map +1 -1
- package/dist/src/agent/proactive/proactive-runner.test.js +0 -3
- package/dist/src/agent/proactive/proactive-runner.test.js.map +1 -1
- package/dist/src/agent/routes/admin-chat-abort.test.d.ts +6 -0
- package/dist/src/agent/routes/admin-chat-abort.test.js +206 -0
- package/dist/src/agent/routes/admin-chat-abort.test.js.map +1 -0
- package/dist/src/agent/routes/admin-chat.js +0 -3
- package/dist/src/agent/routes/admin-chat.js.map +1 -1
- package/dist/src/agent/routes/files.js +46 -52
- package/dist/src/agent/routes/files.js.map +1 -1
- package/dist/src/agent/routes/inspect.js +4 -6
- package/dist/src/agent/routes/inspect.js.map +1 -1
- package/dist/src/agent/routes/task.test.js +0 -3
- package/dist/src/agent/routes/task.test.js.map +1 -1
- package/dist/src/agent/snapshot-server.js +37 -3
- package/dist/src/agent/snapshot-server.js.map +1 -1
- package/dist/src/agent/states/compacting.js +5 -3
- package/dist/src/agent/states/compacting.js.map +1 -1
- package/dist/src/agent/states/confirming.js +3 -0
- package/dist/src/agent/states/confirming.js.map +1 -1
- package/dist/src/agent/states/dispatching.js +45 -2
- package/dist/src/agent/states/dispatching.js.map +1 -1
- package/dist/src/agent/states/executing.js +225 -81
- package/dist/src/agent/states/executing.js.map +1 -1
- package/dist/src/agent/states/streaming.js +14 -0
- package/dist/src/agent/states/streaming.js.map +1 -1
- package/dist/src/agent/states/thinking.d.ts +1 -1
- package/dist/src/agent/states/thinking.js +246 -29
- package/dist/src/agent/states/thinking.js.map +1 -1
- package/dist/src/agent/token-estimate.d.ts +20 -6
- package/dist/src/agent/token-estimate.js +24 -3
- package/dist/src/agent/token-estimate.js.map +1 -1
- package/dist/src/agent/token-estimate.test.d.ts +6 -0
- package/dist/src/agent/token-estimate.test.js +44 -0
- package/dist/src/agent/token-estimate.test.js.map +1 -0
- package/dist/src/agent/tool-executor-local.test.js +0 -1
- package/dist/src/agent/tool-executor-local.test.js.map +1 -1
- package/dist/src/agent/tool-harness-template.js +0 -1
- package/dist/src/agent/tool-harness-template.js.map +1 -1
- package/dist/src/api/create-agent.js +1 -5
- package/dist/src/api/create-agent.js.map +1 -1
- package/dist/src/api/types.d.ts +1 -5
- package/dist/src/channels/bootstrap.d.ts +59 -0
- package/dist/src/channels/bootstrap.js +84 -0
- package/dist/src/channels/bootstrap.js.map +1 -0
- package/dist/src/channels/channel-session-mapper.d.ts +42 -0
- package/dist/src/channels/channel-session-mapper.js +91 -0
- package/dist/src/channels/channel-session-mapper.js.map +1 -0
- package/dist/src/channels/dedup-cache.d.ts +17 -0
- package/dist/src/channels/dedup-cache.js +51 -0
- package/dist/src/channels/dedup-cache.js.map +1 -0
- package/dist/src/channels/dedup-cache.test.d.ts +6 -0
- package/dist/src/channels/dedup-cache.test.js +51 -0
- package/dist/src/channels/dedup-cache.test.js.map +1 -0
- package/dist/src/channels/errors.d.ts +28 -0
- package/dist/src/channels/errors.js +38 -0
- package/dist/src/channels/errors.js.map +1 -0
- package/dist/src/channels/in-memory-session-mapper.d.ts +34 -0
- package/dist/src/channels/in-memory-session-mapper.js +50 -0
- package/dist/src/channels/in-memory-session-mapper.js.map +1 -0
- package/dist/src/channels/plugin-loader.d.ts +20 -0
- package/dist/src/channels/plugin-loader.js +136 -0
- package/dist/src/channels/plugin-loader.js.map +1 -0
- package/dist/src/channels/plugin-loader.test.d.ts +6 -0
- package/dist/src/channels/plugin-loader.test.js +113 -0
- package/dist/src/channels/plugin-loader.test.js.map +1 -0
- package/dist/src/channels/routes.d.ts +29 -0
- package/dist/src/channels/routes.js +165 -0
- package/dist/src/channels/routes.js.map +1 -0
- package/dist/src/config.d.ts +0 -2
- package/dist/src/config.js +0 -1
- package/dist/src/config.js.map +1 -1
- package/dist/src/config.test.js +0 -2
- package/dist/src/config.test.js.map +1 -1
- package/dist/src/context/compiler.js +11 -34
- package/dist/src/context/compiler.js.map +1 -1
- package/dist/src/context/compiler.test.js +7 -60
- package/dist/src/context/compiler.test.js.map +1 -1
- package/dist/src/context/types.d.ts +0 -4
- package/dist/src/env-ref.d.ts +13 -0
- package/dist/src/env-ref.js +31 -0
- package/dist/src/env-ref.js.map +1 -0
- package/dist/src/env-ref.test.d.ts +6 -0
- package/dist/src/env-ref.test.js +34 -0
- package/dist/src/env-ref.test.js.map +1 -0
- package/dist/src/errors.d.ts +15 -0
- package/dist/src/errors.js +22 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/errors.test.js +2 -2
- package/dist/src/errors.test.js.map +1 -1
- package/dist/src/events/event-bus.d.ts +54 -0
- package/dist/src/events/event-bus.js +84 -0
- package/dist/src/events/event-bus.js.map +1 -0
- package/dist/src/events/event-bus.test.d.ts +6 -0
- package/dist/src/events/event-bus.test.js +112 -0
- package/dist/src/events/event-bus.test.js.map +1 -0
- package/dist/src/events/events-route.d.ts +36 -0
- package/dist/src/events/events-route.js +80 -0
- package/dist/src/events/events-route.js.map +1 -0
- package/dist/src/events/events-route.test.d.ts +6 -0
- package/dist/src/events/events-route.test.js +134 -0
- package/dist/src/events/events-route.test.js.map +1 -0
- package/dist/src/events/store-event-wrapper.d.ts +19 -0
- package/dist/src/events/store-event-wrapper.js +57 -0
- package/dist/src/events/store-event-wrapper.js.map +1 -0
- package/dist/src/events/store-event-wrapper.test.d.ts +6 -0
- package/dist/src/events/store-event-wrapper.test.js +91 -0
- package/dist/src/events/store-event-wrapper.test.js.map +1 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/middleware/auth.d.ts +0 -2
- package/dist/src/middleware/auth.js.map +1 -1
- package/dist/src/providers/search-provider.d.ts +64 -0
- package/dist/src/providers/search-provider.js +174 -0
- package/dist/src/providers/search-provider.js.map +1 -0
- package/dist/src/providers/types.d.ts +8 -0
- package/dist/src/routes/ai-stream.d.ts +18 -4
- package/dist/src/routes/ai-stream.js +10 -2
- package/dist/src/routes/ai-stream.js.map +1 -1
- package/dist/src/routes/chat-stream.d.ts +9 -1
- package/dist/src/routes/chat-stream.js +3 -1
- package/dist/src/routes/chat-stream.js.map +1 -1
- package/dist/src/routes/chat.d.ts +6 -0
- package/dist/src/routes/chat.js +2 -1
- package/dist/src/routes/chat.js.map +1 -1
- package/dist/src/routes/session-resolver.d.ts +15 -2
- package/dist/src/routes/session-resolver.js +22 -25
- package/dist/src/routes/session-resolver.js.map +1 -1
- package/dist/src/routes/session-resolver.test.js +117 -20
- package/dist/src/routes/session-resolver.test.js.map +1 -1
- package/dist/src/server.d.ts +35 -1
- package/dist/src/server.js +33 -0
- package/dist/src/server.js.map +1 -1
- package/dist/src/session/drizzle-session-store.d.ts +57 -0
- package/dist/src/session/drizzle-session-store.js +204 -0
- package/dist/src/session/drizzle-session-store.js.map +1 -0
- package/dist/src/session/manager.d.ts +6 -3
- package/dist/src/session/manager.js +46 -19
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/manager.test.js +12 -18
- package/dist/src/session/manager.test.js.map +1 -1
- package/dist/src/session/pglite-session-store.d.ts +23 -0
- package/dist/src/session/pglite-session-store.js +86 -0
- package/dist/src/session/pglite-session-store.js.map +1 -0
- package/dist/src/session/postgres-session-store.d.ts +44 -0
- package/dist/src/session/postgres-session-store.js +153 -0
- package/dist/src/session/postgres-session-store.js.map +1 -0
- package/dist/src/session/session-builder.d.ts +0 -5
- package/dist/src/session/session-builder.js +22 -6
- package/dist/src/session/session-builder.js.map +1 -1
- package/dist/src/session/session-builder.test.js +3 -8
- package/dist/src/session/session-builder.test.js.map +1 -1
- package/dist/src/session/session-store-selector.d.ts +49 -0
- package/dist/src/session/session-store-selector.js +60 -0
- package/dist/src/session/session-store-selector.js.map +1 -0
- package/dist/src/session/session-store-selector.test.d.ts +6 -0
- package/dist/src/session/session-store-selector.test.js +79 -0
- package/dist/src/session/session-store-selector.test.js.map +1 -0
- package/dist/src/session/store.d.ts +146 -32
- package/dist/src/session/store.js +126 -138
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/store.test.js +385 -107
- package/dist/src/session/store.test.js.map +1 -1
- package/dist/src/session/tool-context-factory.d.ts +3 -7
- package/dist/src/session/tool-context-factory.js +1 -3
- package/dist/src/session/tool-context-factory.js.map +1 -1
- package/dist/src/session/tool-context-factory.test.js +1 -6
- package/dist/src/session/tool-context-factory.test.js.map +1 -1
- package/dist/src/session/types.d.ts +13 -10
- package/dist/src/stores/schema.d.ts +111 -34
- package/dist/src/stores/schema.js +21 -4
- package/dist/src/stores/schema.js.map +1 -1
- package/dist/src/tools/admin-file-tools.d.ts +29 -0
- package/dist/src/tools/admin-file-tools.js +527 -13
- package/dist/src/tools/admin-file-tools.js.map +1 -1
- package/dist/src/tools/admin-file-tools.test.js +380 -9
- package/dist/src/tools/admin-file-tools.test.js.map +1 -1
- package/dist/src/tools/custom-tool-adapter.js +0 -1
- package/dist/src/tools/custom-tool-adapter.js.map +1 -1
- package/dist/src/tools/custom-tool-adapter.test.js +0 -2
- package/dist/src/tools/custom-tool-adapter.test.js.map +1 -1
- package/dist/src/tools/dispatch-tool.d.ts +4 -4
- package/dist/src/tools/fetch-url-tool.d.ts +23 -0
- package/dist/src/tools/fetch-url-tool.js +333 -0
- package/dist/src/tools/fetch-url-tool.js.map +1 -0
- package/dist/src/tools/fetch-url-tool.test.d.ts +6 -0
- package/dist/src/tools/fetch-url-tool.test.js +227 -0
- package/dist/src/tools/fetch-url-tool.test.js.map +1 -0
- package/dist/src/tools/mcp-tool-adapter.test.js +0 -2
- package/dist/src/tools/mcp-tool-adapter.test.js.map +1 -1
- package/dist/src/tools/registry.test.js +0 -2
- package/dist/src/tools/registry.test.js.map +1 -1
- package/dist/src/tools/request-tool.test.js +0 -2
- package/dist/src/tools/request-tool.test.js.map +1 -1
- package/dist/src/tools/store-tools.test.js +0 -2
- package/dist/src/tools/store-tools.test.js.map +1 -1
- package/dist/src/tools/types.d.ts +20 -7
- package/dist/src/tools/web-search-tool.d.ts +31 -0
- package/dist/src/tools/web-search-tool.js +170 -0
- package/dist/src/tools/web-search-tool.js.map +1 -0
- package/dist/src/tools/web-search-tool.test.d.ts +6 -0
- package/dist/src/tools/web-search-tool.test.js +152 -0
- package/dist/src/tools/web-search-tool.test.js.map +1 -0
- package/dist/src/tools/web-tools-shared.d.ts +21 -0
- package/dist/src/tools/web-tools-shared.js +32 -0
- package/dist/src/tools/web-tools-shared.js.map +1 -0
- package/dist/src/types.d.ts +20 -4
- package/dist/src/types.js +13 -2
- package/dist/src/types.js.map +1 -1
- package/dist/src/types.test.js +0 -3
- package/dist/src/types.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -3
- package/dist/src/agent/session-store.d.ts +0 -71
- package/dist/src/agent/session-store.js +0 -151
- package/dist/src/agent/session-store.js.map +0 -1
- package/dist/src/agent/user-context-fetcher.d.ts +0 -25
- package/dist/src/agent/user-context-fetcher.js +0 -79
- package/dist/src/agent/user-context-fetcher.js.map +0 -1
- package/dist/src/agent/user-context-fetcher.test.js +0 -121
- package/dist/src/agent/user-context-fetcher.test.js.map +0 -1
- package/dist/src/session/admin-file-tools.d.ts +0 -136
- package/dist/src/session/admin-file-tools.js +0 -240
- package/dist/src/session/admin-file-tools.js.map +0 -1
package/dist/src/api/types.d.ts
CHANGED
|
@@ -41,11 +41,7 @@ export interface AgentConfig {
|
|
|
41
41
|
/** A running agent instance. */
|
|
42
42
|
export interface Agent {
|
|
43
43
|
/** Create a new chat session. */
|
|
44
|
-
createSession(
|
|
45
|
-
tenantId?: string;
|
|
46
|
-
userId?: string;
|
|
47
|
-
userRoles?: string[];
|
|
48
|
-
}): AgentSession;
|
|
44
|
+
createSession(): AgentSession;
|
|
49
45
|
/** Resume an existing session by ID. Returns null if not found. */
|
|
50
46
|
resumeSession(sessionId: string): Promise<AgentSession | null>;
|
|
51
47
|
/** Get the agent's compiled system prompt. */
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Shared channel bootstrap sequence.
|
|
8
|
+
*
|
|
9
|
+
* Used by local-server.ts, snapshot-server.ts, and available for
|
|
10
|
+
* hosting layers that call createServer(). Loads channel plugins,
|
|
11
|
+
* wires the session mapper, and creates the Express router.
|
|
12
|
+
*/
|
|
13
|
+
import type { Router } from 'express';
|
|
14
|
+
import type { ChannelAdapter, ChannelSessionMapper } from '@amodalai/types';
|
|
15
|
+
import type { StandaloneSessionManager } from '../session/manager.js';
|
|
16
|
+
import type { SessionComponents } from '../session/session-builder.js';
|
|
17
|
+
import type { RuntimeEventBus } from '../events/event-bus.js';
|
|
18
|
+
import type { Logger } from '../logger.js';
|
|
19
|
+
import type { CreateChannelSession } from './channel-session-mapper.js';
|
|
20
|
+
/** A discovered channel from the bundle (matches AgentBundle['channels'][n]). */
|
|
21
|
+
interface BundleChannel {
|
|
22
|
+
channelType: string;
|
|
23
|
+
packageName: string;
|
|
24
|
+
packageDir: string;
|
|
25
|
+
config: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
export interface BootstrapChannelsOptions {
|
|
28
|
+
/** Discovered channel plugins from the bundle. */
|
|
29
|
+
channels: BundleChannel[];
|
|
30
|
+
/** Repo path for local channel discovery + node_modules resolution. */
|
|
31
|
+
repoPath: string;
|
|
32
|
+
/** The `packages` array from amodal.json. */
|
|
33
|
+
packages?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Pre-wired session mapper. The caller chooses the implementation:
|
|
36
|
+
* - DrizzleChannelSessionMapper (local-server, hosted with DB)
|
|
37
|
+
* - InMemoryChannelSessionMapper (snapshot-server, testing)
|
|
38
|
+
*/
|
|
39
|
+
sessionMapper: ChannelSessionMapper & {
|
|
40
|
+
setSessionFactory(f: CreateChannelSession): void;
|
|
41
|
+
};
|
|
42
|
+
sessionManager: StandaloneSessionManager;
|
|
43
|
+
/** Factory that builds session components for new channel sessions. */
|
|
44
|
+
buildSessionComponents: () => SessionComponents;
|
|
45
|
+
/** App ID for sessions created by channels (e.g. 'local'). */
|
|
46
|
+
appId?: string;
|
|
47
|
+
eventBus: RuntimeEventBus;
|
|
48
|
+
logger: Logger;
|
|
49
|
+
}
|
|
50
|
+
export interface BootstrapChannelsResult {
|
|
51
|
+
adapters: Map<string, ChannelAdapter>;
|
|
52
|
+
router: Router;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Load channel plugins, wire the session factory, and create the
|
|
56
|
+
* Express router. Returns null if loading fails (error is logged).
|
|
57
|
+
*/
|
|
58
|
+
export declare function bootstrapChannels(opts: BootstrapChannelsOptions): Promise<BootstrapChannelsResult | null>;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { loadChannelPlugins } from './plugin-loader.js';
|
|
7
|
+
import { createChannelsRouter } from './routes.js';
|
|
8
|
+
import { MessageDedupCache } from './dedup-cache.js';
|
|
9
|
+
import { resolveEnvRef } from '../env-ref.js';
|
|
10
|
+
/**
|
|
11
|
+
* Load channel plugins, wire the session factory, and create the
|
|
12
|
+
* Express router. Returns null if loading fails (error is logged).
|
|
13
|
+
*/
|
|
14
|
+
export async function bootstrapChannels(opts) {
|
|
15
|
+
const { channels, repoPath, sessionMapper, sessionManager, buildSessionComponents, eventBus, logger } = opts;
|
|
16
|
+
const appId = opts.appId ?? 'local';
|
|
17
|
+
if (channels.length === 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
// Build and resolve env:VAR refs in channel config values
|
|
22
|
+
const channelsConfig = {};
|
|
23
|
+
for (const ch of channels) {
|
|
24
|
+
channelsConfig[ch.channelType] = resolveChannelConfig(ch.config);
|
|
25
|
+
}
|
|
26
|
+
// Load and validate plugins
|
|
27
|
+
const adapters = await loadChannelPlugins({ channelsConfig, repoPath, packages: opts.packages, logger });
|
|
28
|
+
logger.info('channels_loaded', { channels: [...adapters.keys()] });
|
|
29
|
+
// Wire session factory — creates chat sessions for channel users
|
|
30
|
+
sessionMapper.setSessionFactory((origin) => {
|
|
31
|
+
const components = buildSessionComponents();
|
|
32
|
+
const channelNote = `\n\n[Channel context: This user is messaging you via ${origin.channelType}. Keep responses concise and conversational. User: ${origin.channelUserDisplay ?? origin.channelUserId}.]`;
|
|
33
|
+
const session = sessionManager.create({
|
|
34
|
+
provider: components.provider,
|
|
35
|
+
toolRegistry: components.toolRegistry,
|
|
36
|
+
permissionChecker: components.permissionChecker,
|
|
37
|
+
systemPrompt: components.systemPrompt + channelNote,
|
|
38
|
+
toolContextFactory: components.toolContextFactory,
|
|
39
|
+
appId,
|
|
40
|
+
metadata: { channelOrigin: origin },
|
|
41
|
+
});
|
|
42
|
+
return { sessionId: session.id };
|
|
43
|
+
});
|
|
44
|
+
// Build the router
|
|
45
|
+
const router = createChannelsRouter({
|
|
46
|
+
adapters,
|
|
47
|
+
sessionMapper,
|
|
48
|
+
sessionManager,
|
|
49
|
+
dedupCache: new MessageDedupCache(),
|
|
50
|
+
eventBus,
|
|
51
|
+
logger,
|
|
52
|
+
});
|
|
53
|
+
return { adapters, router };
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
logger.warn('channels_load_failed', {
|
|
57
|
+
error: err instanceof Error ? err.message : String(err),
|
|
58
|
+
hint: 'Server will start without messaging channels',
|
|
59
|
+
});
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Helpers
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
/**
|
|
67
|
+
* Resolve `env:VAR_NAME` references in a channel config block.
|
|
68
|
+
*/
|
|
69
|
+
function resolveChannelConfig(config) {
|
|
70
|
+
const resolved = {};
|
|
71
|
+
for (const [k, v] of Object.entries(config)) {
|
|
72
|
+
if (typeof v === 'string') {
|
|
73
|
+
resolved[k] = resolveEnvRef(v) ?? v;
|
|
74
|
+
}
|
|
75
|
+
else if (Array.isArray(v)) {
|
|
76
|
+
resolved[k] = v.map((item) => typeof item === 'string' ? (resolveEnvRef(item) ?? item) : item);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
resolved[k] = v;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return resolved;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../../src/channels/bootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import type { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
|
|
7
|
+
import type { ChannelSessionMapper, ChannelSessionMapResult, ChannelOrigin } from '@amodalai/types';
|
|
8
|
+
import type { Logger } from '../logger.js';
|
|
9
|
+
type AnyPgDatabase = PgDatabase<PgQueryResultHKT, Record<string, unknown>>;
|
|
10
|
+
export interface ChannelSessionMapperOptions {
|
|
11
|
+
db: AnyPgDatabase;
|
|
12
|
+
logger: Logger;
|
|
13
|
+
eventBus?: {
|
|
14
|
+
emit(payload: {
|
|
15
|
+
type: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}): unknown;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a new session for a channel user. The caller provides the
|
|
22
|
+
* session factory so the mapper stays decoupled from session creation
|
|
23
|
+
* details (provider selection, tool registration, etc.).
|
|
24
|
+
*/
|
|
25
|
+
export type CreateChannelSession = (channelOrigin: ChannelOrigin) => {
|
|
26
|
+
sessionId: string;
|
|
27
|
+
};
|
|
28
|
+
export declare class DrizzleChannelSessionMapper implements ChannelSessionMapper {
|
|
29
|
+
private readonly db;
|
|
30
|
+
private readonly logger;
|
|
31
|
+
private readonly eventBus?;
|
|
32
|
+
private createSession;
|
|
33
|
+
constructor(opts: ChannelSessionMapperOptions);
|
|
34
|
+
/**
|
|
35
|
+
* Wire the session factory after construction. Called by the local-server
|
|
36
|
+
* wiring code once all session components are available.
|
|
37
|
+
*/
|
|
38
|
+
setSessionFactory(factory: CreateChannelSession): void;
|
|
39
|
+
findOrCreateSession(channelType: string, channelUserId: string, displayName?: string): Promise<ChannelSessionMapResult>;
|
|
40
|
+
resetSession(channelType: string, channelUserId: string): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Drizzle-based channel session mapper.
|
|
8
|
+
*
|
|
9
|
+
* Maps (channelType, channelUserId) → sessionId using the
|
|
10
|
+
* `channel_sessions` table. Shares the same database connection pool
|
|
11
|
+
* as the session store to avoid opening a second connection.
|
|
12
|
+
*/
|
|
13
|
+
import { eq, and, sql } from 'drizzle-orm';
|
|
14
|
+
import { channelSessions } from '../stores/schema.js';
|
|
15
|
+
export class DrizzleChannelSessionMapper {
|
|
16
|
+
db;
|
|
17
|
+
logger;
|
|
18
|
+
eventBus;
|
|
19
|
+
createSession = null;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
this.db = opts.db;
|
|
22
|
+
this.logger = opts.logger;
|
|
23
|
+
this.eventBus = opts.eventBus;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Wire the session factory after construction. Called by the local-server
|
|
27
|
+
* wiring code once all session components are available.
|
|
28
|
+
*/
|
|
29
|
+
setSessionFactory(factory) {
|
|
30
|
+
this.createSession = factory;
|
|
31
|
+
}
|
|
32
|
+
async findOrCreateSession(channelType, channelUserId, displayName) {
|
|
33
|
+
// Try to find an existing mapping and touch last_active_at in one query
|
|
34
|
+
const rows = await this.db
|
|
35
|
+
.update(channelSessions)
|
|
36
|
+
.set({ lastActiveAt: sql `NOW()` })
|
|
37
|
+
.where(and(eq(channelSessions.channelType, channelType), eq(channelSessions.channelUserId, channelUserId)))
|
|
38
|
+
.returning({ sessionId: channelSessions.sessionId });
|
|
39
|
+
if (rows.length > 0) {
|
|
40
|
+
const { sessionId } = rows[0];
|
|
41
|
+
this.logger.debug('channel_session_found', { channelType, channelUserId, sessionId });
|
|
42
|
+
return { sessionId, isNew: false };
|
|
43
|
+
}
|
|
44
|
+
// Create new session
|
|
45
|
+
if (!this.createSession) {
|
|
46
|
+
throw new Error('Channel session mapper: session factory not set. Call setSessionFactory() first.');
|
|
47
|
+
}
|
|
48
|
+
const channelOrigin = {
|
|
49
|
+
channelType,
|
|
50
|
+
channelUserId,
|
|
51
|
+
channelUserDisplay: displayName,
|
|
52
|
+
};
|
|
53
|
+
const { sessionId } = this.createSession(channelOrigin);
|
|
54
|
+
// Atomic upsert — if a concurrent request inserted first, return the existing row
|
|
55
|
+
const inserted = await this.db
|
|
56
|
+
.insert(channelSessions)
|
|
57
|
+
.values({
|
|
58
|
+
channelType,
|
|
59
|
+
channelUserId,
|
|
60
|
+
sessionId,
|
|
61
|
+
metadata: { channelUserDisplay: displayName },
|
|
62
|
+
})
|
|
63
|
+
.onConflictDoUpdate({
|
|
64
|
+
target: [channelSessions.channelType, channelSessions.channelUserId],
|
|
65
|
+
set: { lastActiveAt: sql `NOW()` },
|
|
66
|
+
})
|
|
67
|
+
.returning({ sessionId: channelSessions.sessionId });
|
|
68
|
+
const finalSessionId = inserted[0].sessionId;
|
|
69
|
+
const isNew = finalSessionId === sessionId;
|
|
70
|
+
if (isNew) {
|
|
71
|
+
this.logger.info('channel_session_created', { channelType, channelUserId, sessionId: finalSessionId });
|
|
72
|
+
this.eventBus?.emit({
|
|
73
|
+
type: 'channel_session_created',
|
|
74
|
+
channelType,
|
|
75
|
+
channelUserId,
|
|
76
|
+
sessionId: finalSessionId,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.logger.debug('channel_session_found', { channelType, channelUserId, sessionId: finalSessionId });
|
|
81
|
+
}
|
|
82
|
+
return { sessionId: finalSessionId, isNew };
|
|
83
|
+
}
|
|
84
|
+
async resetSession(channelType, channelUserId) {
|
|
85
|
+
await this.db
|
|
86
|
+
.delete(channelSessions)
|
|
87
|
+
.where(and(eq(channelSessions.channelType, channelType), eq(channelSessions.channelUserId, channelUserId)));
|
|
88
|
+
this.logger.info('channel_session_reset', { channelType, channelUserId });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=channel-session-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-session-mapper.js","sourceRoot":"","sources":["../../../src/channels/channel-session-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
export declare class MessageDedupCache {
|
|
7
|
+
private readonly seen;
|
|
8
|
+
private readonly ttlMs;
|
|
9
|
+
constructor(ttlMs?: number);
|
|
10
|
+
/**
|
|
11
|
+
* Returns `true` if this message was already seen (duplicate).
|
|
12
|
+
* Returns `false` and records the message if it's new.
|
|
13
|
+
*/
|
|
14
|
+
isDuplicate(channelType: string, messageId: string): boolean;
|
|
15
|
+
get size(): number;
|
|
16
|
+
private evict;
|
|
17
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* In-memory dedup cache for channel webhook retries.
|
|
8
|
+
*
|
|
9
|
+
* Messaging platforms may resend webhook payloads when the server
|
|
10
|
+
* doesn't respond with 200 quickly enough. This cache prevents the same
|
|
11
|
+
* message from being processed twice.
|
|
12
|
+
*
|
|
13
|
+
* Keyed by `${channelType}:${messageId}`, with a configurable TTL.
|
|
14
|
+
* Lazy eviction keeps the fast path allocation-free.
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_TTL_MS = 60_000;
|
|
17
|
+
const EVICTION_THRESHOLD = 1000;
|
|
18
|
+
export class MessageDedupCache {
|
|
19
|
+
seen = new Map();
|
|
20
|
+
ttlMs;
|
|
21
|
+
constructor(ttlMs = DEFAULT_TTL_MS) {
|
|
22
|
+
this.ttlMs = ttlMs;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns `true` if this message was already seen (duplicate).
|
|
26
|
+
* Returns `false` and records the message if it's new.
|
|
27
|
+
*/
|
|
28
|
+
isDuplicate(channelType, messageId) {
|
|
29
|
+
const key = `${channelType}:${messageId}`;
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
if (this.seen.size > EVICTION_THRESHOLD) {
|
|
32
|
+
this.evict(now);
|
|
33
|
+
}
|
|
34
|
+
if (this.seen.has(key)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
this.seen.set(key, now);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
get size() {
|
|
41
|
+
return this.seen.size;
|
|
42
|
+
}
|
|
43
|
+
evict(now) {
|
|
44
|
+
for (const [k, ts] of this.seen) {
|
|
45
|
+
if (now - ts > this.ttlMs) {
|
|
46
|
+
this.seen.delete(k);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=dedup-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup-cache.js","sourceRoot":"","sources":["../../../src/channels/dedup-cache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;GASG;AAEH,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,MAAM,OAAO,iBAAiB;IACX,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjC,KAAK,CAAS;IAE/B,YAAY,KAAK,GAAG,cAAc;QAChC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,WAAmB,EAAE,SAAiB;QAChD,MAAM,GAAG,GAAG,GAAG,WAAW,IAAI,SAAS,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,kBAAkB,EAAE,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,GAAW;QACvB,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,IAAI,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
7
|
+
import { MessageDedupCache } from './dedup-cache.js';
|
|
8
|
+
describe('MessageDedupCache', () => {
|
|
9
|
+
let cache;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
cache = new MessageDedupCache();
|
|
12
|
+
});
|
|
13
|
+
it('returns false for new messages', () => {
|
|
14
|
+
expect(cache.isDuplicate('telegram', '123')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
it('returns true for duplicate messages', () => {
|
|
17
|
+
cache.isDuplicate('telegram', '123');
|
|
18
|
+
expect(cache.isDuplicate('telegram', '123')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it('treats different message IDs as distinct', () => {
|
|
21
|
+
cache.isDuplicate('telegram', '123');
|
|
22
|
+
expect(cache.isDuplicate('telegram', '456')).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
it('treats different channel types as distinct', () => {
|
|
25
|
+
cache.isDuplicate('telegram', '123');
|
|
26
|
+
expect(cache.isDuplicate('slack', '123')).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it('tracks cache size', () => {
|
|
29
|
+
cache.isDuplicate('telegram', '1');
|
|
30
|
+
cache.isDuplicate('telegram', '2');
|
|
31
|
+
expect(cache.size).toBe(2);
|
|
32
|
+
});
|
|
33
|
+
it('evicts stale entries when threshold is exceeded', () => {
|
|
34
|
+
// Use a very short TTL for testing
|
|
35
|
+
const shortCache = new MessageDedupCache(1); // 1ms TTL
|
|
36
|
+
// Fill past eviction threshold
|
|
37
|
+
for (let i = 0; i < 1001; i++) {
|
|
38
|
+
shortCache.isDuplicate('telegram', String(i));
|
|
39
|
+
}
|
|
40
|
+
// Wait a tick for TTL to expire
|
|
41
|
+
const start = Date.now();
|
|
42
|
+
while (Date.now() - start < 5) {
|
|
43
|
+
// busy wait
|
|
44
|
+
}
|
|
45
|
+
// Next isDuplicate should trigger eviction
|
|
46
|
+
shortCache.isDuplicate('telegram', 'trigger');
|
|
47
|
+
// Stale entries should be gone (only 'trigger' remains)
|
|
48
|
+
expect(shortCache.size).toBeLessThanOrEqual(2);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=dedup-cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup-cache.test.js","sourceRoot":"","sources":["../../../src/channels/dedup-cache.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAC,MAAM,QAAQ,CAAC;AACxD,OAAO,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAEnD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,KAAwB,CAAC;IAE7B,UAAU,CAAC,GAAG,EAAE;QACd,KAAK,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACnC,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,mCAAmC;QACnC,MAAM,UAAU,GAAG,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;QAEvD,+BAA+B;QAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9B,UAAU,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;QAED,gCAAgC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC;YAC9B,YAAY;QACd,CAAC;QAED,2CAA2C;QAC3C,UAAU,CAAC,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAC9C,wDAAwD;QACxD,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { AmodalError } from '../errors.js';
|
|
7
|
+
/**
|
|
8
|
+
* Error loading a channel plugin package (missing, invalid export shape).
|
|
9
|
+
*/
|
|
10
|
+
export declare class ChannelPluginError extends AmodalError {
|
|
11
|
+
readonly channelType: string;
|
|
12
|
+
constructor(message: string, options: {
|
|
13
|
+
channelType: string;
|
|
14
|
+
cause?: unknown;
|
|
15
|
+
context?: Record<string, unknown>;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Error validating a channel's config block against its plugin schema.
|
|
20
|
+
*/
|
|
21
|
+
export declare class ChannelConfigError extends AmodalError {
|
|
22
|
+
readonly channelType: string;
|
|
23
|
+
constructor(message: string, options: {
|
|
24
|
+
channelType: string;
|
|
25
|
+
cause?: unknown;
|
|
26
|
+
context?: Record<string, unknown>;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { AmodalError } from '../errors.js';
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Channel plugin errors
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Error loading a channel plugin package (missing, invalid export shape).
|
|
12
|
+
*/
|
|
13
|
+
export class ChannelPluginError extends AmodalError {
|
|
14
|
+
channelType;
|
|
15
|
+
constructor(message, options) {
|
|
16
|
+
super('CHANNEL_PLUGIN_ERROR', message, {
|
|
17
|
+
channelType: options.channelType,
|
|
18
|
+
...options.context,
|
|
19
|
+
}, options.cause);
|
|
20
|
+
this.name = 'ChannelPluginError';
|
|
21
|
+
this.channelType = options.channelType;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Error validating a channel's config block against its plugin schema.
|
|
26
|
+
*/
|
|
27
|
+
export class ChannelConfigError extends AmodalError {
|
|
28
|
+
channelType;
|
|
29
|
+
constructor(message, options) {
|
|
30
|
+
super('CHANNEL_CONFIG_ERROR', message, {
|
|
31
|
+
channelType: options.channelType,
|
|
32
|
+
...options.context,
|
|
33
|
+
}, options.cause);
|
|
34
|
+
this.name = 'ChannelConfigError';
|
|
35
|
+
this.channelType = options.channelType;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/channels/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAEzC,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,WAAW;IACxC,WAAW,CAAS;IAE7B,YACE,OAAe,EACf,OAIC;QAED,KAAK,CAAC,sBAAsB,EAAE,OAAO,EAAE;YACrC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,GAAG,OAAO,CAAC,OAAO;SACnB,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IACzC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,WAAW;IACxC,WAAW,CAAS;IAE7B,YACE,OAAe,EACf,OAIC;QAED,KAAK,CAAC,sBAAsB,EAAE,OAAO,EAAE;YACrC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,GAAG,OAAO,CAAC,OAAO;SACnB,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* In-memory channel session mapper.
|
|
8
|
+
*
|
|
9
|
+
* Simple Map-backed implementation for environments without a database
|
|
10
|
+
* (snapshot preview server, testing). Sessions are not persisted across
|
|
11
|
+
* restarts — that's fine for preview and test use cases.
|
|
12
|
+
*/
|
|
13
|
+
import type { ChannelSessionMapper, ChannelSessionMapResult } from '@amodalai/types';
|
|
14
|
+
import type { CreateChannelSession } from './channel-session-mapper.js';
|
|
15
|
+
import type { Logger } from '../logger.js';
|
|
16
|
+
export interface InMemoryChannelSessionMapperOptions {
|
|
17
|
+
logger: Logger;
|
|
18
|
+
eventBus?: {
|
|
19
|
+
emit(payload: {
|
|
20
|
+
type: string;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}): unknown;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare class InMemoryChannelSessionMapper implements ChannelSessionMapper {
|
|
26
|
+
private readonly sessions;
|
|
27
|
+
private readonly logger;
|
|
28
|
+
private readonly eventBus?;
|
|
29
|
+
private createSession;
|
|
30
|
+
constructor(opts: InMemoryChannelSessionMapperOptions);
|
|
31
|
+
setSessionFactory(factory: CreateChannelSession): void;
|
|
32
|
+
findOrCreateSession(channelType: string, channelUserId: string, displayName?: string): Promise<ChannelSessionMapResult>;
|
|
33
|
+
resetSession(channelType: string, channelUserId: string): Promise<void>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
export class InMemoryChannelSessionMapper {
|
|
7
|
+
sessions = new Map();
|
|
8
|
+
logger;
|
|
9
|
+
eventBus;
|
|
10
|
+
createSession = null;
|
|
11
|
+
constructor(opts) {
|
|
12
|
+
this.logger = opts.logger;
|
|
13
|
+
this.eventBus = opts.eventBus;
|
|
14
|
+
}
|
|
15
|
+
setSessionFactory(factory) {
|
|
16
|
+
this.createSession = factory;
|
|
17
|
+
}
|
|
18
|
+
async findOrCreateSession(channelType, channelUserId, displayName) {
|
|
19
|
+
const key = `${channelType}:${channelUserId}`;
|
|
20
|
+
const existing = this.sessions.get(key);
|
|
21
|
+
if (existing) {
|
|
22
|
+
this.logger.debug('channel_session_found', { channelType, channelUserId, sessionId: existing });
|
|
23
|
+
return { sessionId: existing, isNew: false };
|
|
24
|
+
}
|
|
25
|
+
if (!this.createSession) {
|
|
26
|
+
throw new Error('Channel session mapper: session factory not set. Call setSessionFactory() first.');
|
|
27
|
+
}
|
|
28
|
+
const channelOrigin = {
|
|
29
|
+
channelType,
|
|
30
|
+
channelUserId,
|
|
31
|
+
channelUserDisplay: displayName,
|
|
32
|
+
};
|
|
33
|
+
const { sessionId } = this.createSession(channelOrigin);
|
|
34
|
+
this.sessions.set(key, sessionId);
|
|
35
|
+
this.logger.info('channel_session_created', { channelType, channelUserId, sessionId });
|
|
36
|
+
this.eventBus?.emit({
|
|
37
|
+
type: 'channel_session_created',
|
|
38
|
+
channelType,
|
|
39
|
+
channelUserId,
|
|
40
|
+
sessionId,
|
|
41
|
+
});
|
|
42
|
+
return { sessionId, isNew: true };
|
|
43
|
+
}
|
|
44
|
+
async resetSession(channelType, channelUserId) {
|
|
45
|
+
const key = `${channelType}:${channelUserId}`;
|
|
46
|
+
this.sessions.delete(key);
|
|
47
|
+
this.logger.info('channel_session_reset', { channelType, channelUserId });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=in-memory-session-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory-session-mapper.js","sourceRoot":"","sources":["../../../src/channels/in-memory-session-mapper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAqBH,MAAM,OAAO,4BAA4B;IACtB,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IACrC,MAAM,CAAS;IACf,QAAQ,CAAmD;IACpE,aAAa,GAAgC,IAAI,CAAC;IAE1D,YAAY,IAAyC;QACnD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAChC,CAAC;IAED,iBAAiB,CAAC,OAA6B;QAC7C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,mBAAmB,CACvB,WAAmB,EACnB,aAAqB,EACrB,WAAoB;QAEpB,MAAM,GAAG,GAAG,GAAG,WAAW,IAAI,aAAa,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAExC,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAC,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAC,CAAC,CAAC;YAC9F,OAAO,EAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAC,CAAC;QAC7C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;QACtG,CAAC;QAED,MAAM,aAAa,GAAkB;YACnC,WAAW;YACX,aAAa;YACb,kBAAkB,EAAE,WAAW;SAChC,CAAC;QACF,MAAM,EAAC,SAAS,EAAC,GAAG,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAElC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAC,WAAW,EAAE,aAAa,EAAE,SAAS,EAAC,CAAC,CAAC;QACrF,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC;YAClB,IAAI,EAAE,yBAAyB;YAC/B,WAAW;YACX,aAAa;YACb,SAAS;SACV,CAAC,CAAC;QAEH,OAAO,EAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,WAAmB,EACnB,aAAqB;QAErB,MAAM,GAAG,GAAG,GAAG,WAAW,IAAI,aAAa,EAAE,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAC,WAAW,EAAE,aAAa,EAAC,CAAC,CAAC;IAC1E,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import type { ChannelAdapter } from '@amodalai/types';
|
|
7
|
+
import type { Logger } from '../logger.js';
|
|
8
|
+
export interface LoadChannelPluginsOptions {
|
|
9
|
+
/** The `channels` block from amodal.json (env refs already resolved). */
|
|
10
|
+
channelsConfig: Record<string, unknown>;
|
|
11
|
+
/** Absolute path to the repo root (for local channel discovery + node_modules). */
|
|
12
|
+
repoPath: string;
|
|
13
|
+
/** The `packages` array from amodal.json — used to find channel packages. */
|
|
14
|
+
packages?: string[];
|
|
15
|
+
logger: Logger;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Load and initialize channel adapters for all configured channels.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadChannelPlugins(opts: LoadChannelPluginsOptions): Promise<Map<string, ChannelAdapter>>;
|