@recombine-ai/platform 0.2.5 → 0.2.6-test.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.
@@ -0,0 +1,39 @@
1
+ import { z } from 'zod';
2
+ export type AnalyticsValueType = 'boolean' | 'number' | 'string' | 'json';
3
+ export type AnalyticsJsonValue = boolean | number | string | null | AnalyticsJsonValue[] | {
4
+ [key: string]: AnalyticsJsonValue;
5
+ };
6
+ export interface AnalyticsKey<T> {
7
+ readonly key: string;
8
+ readonly valueType: AnalyticsValueType;
9
+ readonly schema: z.ZodType<T>;
10
+ }
11
+ export type AnalyticsValue<TKey extends AnalyticsKey<unknown>> = TKey extends AnalyticsKey<infer TValue> ? TValue : never;
12
+ export interface ConversationAnalyticsSummary {
13
+ conversationId: string;
14
+ agentId?: string;
15
+ channel: string;
16
+ region?: string;
17
+ startedAt: Date;
18
+ endedAt?: Date;
19
+ durationSeconds?: number;
20
+ }
21
+ export interface ConversationAnalytics {
22
+ set<TKey extends AnalyticsKey<unknown>>(key: TKey, value: AnalyticsValue<TKey>): Promise<void>;
23
+ clear(key: AnalyticsKey<unknown>): Promise<void>;
24
+ get<TKey extends AnalyticsKey<unknown>>(key: TKey): Promise<AnalyticsValue<TKey> | undefined>;
25
+ getSummary(): Promise<ConversationAnalyticsSummary | undefined>;
26
+ }
27
+ export interface Analytics {
28
+ getConversationAnalytics(conversationId: string): ConversationAnalytics;
29
+ }
30
+ export declare const analyticsKey: {
31
+ boolean(key: string): AnalyticsKey<boolean>;
32
+ number(key: string): AnalyticsKey<number>;
33
+ string(key: string): AnalyticsKey<string>;
34
+ enum<const TValues extends readonly [string, ...string[]]>(key: string, values: TValues): AnalyticsKey<TValues[number]>;
35
+ json<T extends AnalyticsJsonValue>(key: string, schema: z.ZodType<T>): AnalyticsKey<T>;
36
+ };
37
+ export declare function createInMemoryAnalytics(summaries?: ConversationAnalyticsSummary[]): Analytics;
38
+ export declare function assertAnalyticsJsonValue(value: unknown): asserts value is AnalyticsJsonValue;
39
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;AACzE,MAAM,MAAM,kBAAkB,GACxB,OAAO,GACP,MAAM,GACN,MAAM,GACN,IAAI,GACJ,kBAAkB,EAAE,GACpB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAAA;CAAE,CAAA;AAE3C,MAAM,WAAW,YAAY,CAAC,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;CAChC;AAED,MAAM,MAAM,cAAc,CAAC,IAAI,SAAS,YAAY,CAAC,OAAO,CAAC,IACzD,IAAI,SAAS,YAAY,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,GAAG,KAAK,CAAA;AAE5D,MAAM,WAAW,4BAA4B;IACzC,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;IACf,OAAO,CAAC,EAAE,IAAI,CAAA;IACd,eAAe,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,qBAAqB;IAClC,GAAG,CAAC,IAAI,SAAS,YAAY,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9F,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD,GAAG,CAAC,IAAI,SAAS,YAAY,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAA;IAC7F,UAAU,IAAI,OAAO,CAAC,4BAA4B,GAAG,SAAS,CAAC,CAAA;CAClE;AAED,MAAM,WAAW,SAAS;IACtB,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB,CAAA;CAC1E;AAcD,eAAO,MAAM,YAAY;iBACR,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC;gBAG/B,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC;gBAG7B,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC;eAG9B,OAAO,SAAS,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,OAChD,MAAM,UACH,OAAO,GAChB,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;SAG3B,CAAC,SAAS,kBAAkB,OAAO,MAAM,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;CAGzF,CAAA;AAED,wBAAgB,uBAAuB,CAAC,SAAS,GAAE,4BAA4B,EAAO,GAAG,SAAS,CAsCjG;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,kBAAkB,CAkB5F"}
@@ -0,0 +1,77 @@
1
+ import { z } from 'zod';
2
+ function defineKey(key, valueType, schema) {
3
+ if (!/^[A-Za-z0-9_.:-]{1,128}$/.test(key)) {
4
+ throw new Error('Analytics key contains unsupported characters');
5
+ }
6
+ return Object.freeze({ key, valueType, schema });
7
+ }
8
+ export const analyticsKey = {
9
+ boolean(key) {
10
+ return defineKey(key, 'boolean', z.boolean());
11
+ },
12
+ number(key) {
13
+ return defineKey(key, 'number', z.number().finite());
14
+ },
15
+ string(key) {
16
+ return defineKey(key, 'string', z.string());
17
+ },
18
+ enum(key, values) {
19
+ return defineKey(key, 'string', z.enum(values));
20
+ },
21
+ json(key, schema) {
22
+ return defineKey(key, 'json', schema);
23
+ },
24
+ };
25
+ export function createInMemoryAnalytics(summaries = []) {
26
+ const conversations = new Map();
27
+ const summariesById = new Map(summaries.map((summary) => [summary.conversationId, structuredClone(summary)]));
28
+ return {
29
+ getConversationAnalytics(conversationId) {
30
+ return {
31
+ async set(key, value) {
32
+ const parsed = key.schema.parse(value);
33
+ assertAnalyticsJsonValue(parsed);
34
+ const values = conversations.get(conversationId) ?? new Map();
35
+ values.set(key.key, structuredClone(parsed));
36
+ conversations.set(conversationId, values);
37
+ },
38
+ async clear(key) {
39
+ const values = conversations.get(conversationId);
40
+ values?.delete(key.key);
41
+ if (values?.size === 0) {
42
+ conversations.delete(conversationId);
43
+ }
44
+ },
45
+ async get(key) {
46
+ const values = conversations.get(conversationId);
47
+ if (!values?.has(key.key)) {
48
+ return undefined;
49
+ }
50
+ const value = values.get(key.key);
51
+ return key.schema.parse(structuredClone(value));
52
+ },
53
+ async getSummary() {
54
+ const summary = summariesById.get(conversationId);
55
+ return summary ? structuredClone(summary) : undefined;
56
+ },
57
+ };
58
+ },
59
+ };
60
+ }
61
+ export function assertAnalyticsJsonValue(value) {
62
+ if (value === null ||
63
+ typeof value === 'boolean' ||
64
+ typeof value === 'string' ||
65
+ (typeof value === 'number' && Number.isFinite(value))) {
66
+ return;
67
+ }
68
+ if (Array.isArray(value)) {
69
+ value.forEach(assertAnalyticsJsonValue);
70
+ return;
71
+ }
72
+ if (typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {
73
+ Object.values(value).forEach(assertAnalyticsJsonValue);
74
+ return;
75
+ }
76
+ throw new Error('Analytics value must be valid JSON');
77
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=analytics.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.spec.d.ts","sourceRoot":"","sources":["../src/analytics.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { describe, expect, expectTypeOf, it } from 'vitest';
2
+ import { z } from 'zod';
3
+ import { analyticsKey, createInMemoryAnalytics } from './analytics.js';
4
+ describe('Analytics', () => {
5
+ it('rejects unsupported key characters', () => {
6
+ expect(() => analyticsKey.string('not allowed')).toThrow('Analytics key contains unsupported characters');
7
+ });
8
+ it('keeps key and value types paired', async () => {
9
+ const analytics = createInMemoryAnalytics();
10
+ const escalated = analyticsKey.boolean('common.escalated');
11
+ const conversation = analytics.getConversationAnalytics('conversation-1');
12
+ await conversation.set(escalated, true);
13
+ const value = await conversation.get(escalated);
14
+ expectTypeOf(value).toEqualTypeOf();
15
+ expect(value).toBe(true);
16
+ });
17
+ it('clears a value idempotently', async () => {
18
+ const analytics = createInMemoryAnalytics();
19
+ const score = analyticsKey.number('project.score');
20
+ const conversation = analytics.getConversationAnalytics('conversation-1');
21
+ await conversation.set(score, 3);
22
+ await conversation.clear(score);
23
+ await conversation.clear(score);
24
+ await expect(conversation.get(score)).resolves.toBeUndefined();
25
+ });
26
+ it('validates JSON values at runtime', async () => {
27
+ const analytics = createInMemoryAnalytics();
28
+ const result = analyticsKey.json('project.result', z.object({ reason: z.string(), accepted: z.boolean() }));
29
+ const conversation = analytics.getConversationAnalytics('conversation-1');
30
+ await expect(conversation.set(result, {
31
+ reason: 'done',
32
+ accepted: true,
33
+ })).resolves.toBeUndefined();
34
+ await expect(conversation.set(result, {
35
+ reason: 'done',
36
+ accepted: 'yes',
37
+ })).rejects.toThrow();
38
+ });
39
+ it('distinguishes JSON null from a missing key', async () => {
40
+ const analytics = createInMemoryAnalytics();
41
+ const result = analyticsKey.json('project.result', z.null());
42
+ await analytics.getConversationAnalytics('conversation-1').set(result, null);
43
+ await expect(analytics.getConversationAnalytics('conversation-1').get(result)).resolves.toBeNull();
44
+ await expect(analytics.getConversationAnalytics('conversation-2').get(result)).resolves.toBeUndefined();
45
+ });
46
+ it('returns common conversation data from getSummary', async () => {
47
+ const summary = {
48
+ conversationId: 'conversation-1',
49
+ agentId: 'support',
50
+ channel: 'phone',
51
+ startedAt: new Date('2026-08-22T10:00:00Z'),
52
+ durationSeconds: 60,
53
+ };
54
+ const analytics = createInMemoryAnalytics([summary]);
55
+ await expect(analytics.getConversationAnalytics('conversation-1').getSummary()).resolves.toEqual(summary);
56
+ });
57
+ });
@@ -1,8 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createInMemoryCache = createInMemoryCache;
4
- exports.createRedisCache = createRedisCache;
5
- function createInMemoryCache() {
1
+ export function createInMemoryCache() {
6
2
  const cache = new Map();
7
3
  async function getContext(callId, defaultContext) {
8
4
  if (cache.has(callId)) {
@@ -25,7 +21,7 @@ function createInMemoryCache() {
25
21
  },
26
22
  };
27
23
  }
28
- function createRedisCache(client, options = DEFAULT_REDIS_CACHE_OPTIONS) {
24
+ export function createRedisCache(client, options = DEFAULT_REDIS_CACHE_OPTIONS) {
29
25
  function createKey(callId) {
30
26
  return options.keyPrefix + callId;
31
27
  }
package/build/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- export * from './interfaces/platform';
2
- export * from './interfaces/rc-voice';
3
- export * from './context-cache';
4
- export * from './timeouts-manager';
1
+ export * from './interfaces/platform.js';
2
+ export * from './interfaces/rc-voice.js';
3
+ export * from './context-cache.js';
4
+ export * from './analytics.js';
5
+ export * from './postgres-analytics.js';
6
+ export * from './timeouts-manager.js';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,oBAAoB,CAAA;AAClC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA"}
package/build/index.js CHANGED
@@ -1,20 +1,6 @@
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
- __exportStar(require("./interfaces/platform"), exports);
18
- __exportStar(require("./interfaces/rc-voice"), exports);
19
- __exportStar(require("./context-cache"), exports);
20
- __exportStar(require("./timeouts-manager"), exports);
1
+ export * from './interfaces/platform.js';
2
+ export * from './interfaces/rc-voice.js';
3
+ export * from './context-cache.js';
4
+ export * from './analytics.js';
5
+ export * from './postgres-analytics.js';
6
+ export * from './timeouts-manager.js';
@@ -1,22 +1,24 @@
1
1
  import { ReadableStream } from 'node:stream/web';
2
2
  import { AIEngine, AIStreamEngine, LlmAdapter, LlmStreamAdapter, Logger, Message, PromptFS, ResponseChunk } from '@recombine-ai/engine';
3
- import { ContextCache } from '../context-cache';
4
- import { TimeoutManager } from '../timeouts-manager';
5
- import { IRcVoice } from './rc-voice';
3
+ import { Analytics } from '../analytics.js';
4
+ import { ContextCache } from '../context-cache.js';
5
+ import { TimeoutManager } from '../timeouts-manager.js';
6
+ import { IRcVoice } from './rc-voice.js';
6
7
  export interface IModels {
7
8
  models: Record<string, LlmAdapter>;
8
9
  streamingModels: Record<string, LlmStreamAdapter>;
9
10
  }
10
- export interface Platform<TContext extends {}, TModels extends IModels> {
11
+ export interface Platform<TContext extends {}, TModels extends IModels, TRcVoice = IRcVoice, TTimeoutManager = TimeoutManager, TContextCache = ContextCache<TContext>> {
11
12
  streamAi: AIStreamEngine<TContext>;
12
13
  ai: AIEngine<TContext>;
13
14
  fs: PromptFS;
14
- rcVoice: IRcVoice;
15
+ rcVoice: TRcVoice;
15
16
  logger: Logger;
16
- timeoutManager: TimeoutManager;
17
+ timeoutManager: TTimeoutManager;
17
18
  models: TModels['models'];
18
19
  streamingModels: TModels['streamingModels'];
19
- contextCache: ContextCache<TContext>;
20
+ contextCache: TContextCache;
21
+ analytics: Analytics;
20
22
  }
21
23
  export interface CallInfo {
22
24
  callId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/interfaces/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EACH,QAAQ,EACR,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,OAAO,EACP,QAAQ,EACR,aAAa,EAChB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,MAAM,WAAW,OAAO;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAClC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CACpD;AAED,MAAM,WAAW,QAAQ,CAAC,QAAQ,SAAS,EAAE,EAAE,OAAO,SAAS,OAAO;IAElE,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;IAElC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAEtB,EAAE,EAAE,QAAQ,CAAA;IAEZ,OAAO,EAAE,QAAQ,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,cAAc,CAAA;IAE9B,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IACzB,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAE3C,YAAY,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,UAAU;IACvB,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC;QACrC,cAAc,EAAE,MAAM,CAAA;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;KACpD,CAAC,CAAA;IACF,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,cAAc,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAA;IAC/F,SAAS,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzE,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5D;AAED,wBAAgB,WAAW,CAAC,QAAQ,SAAS,EAAE,EAAE,SAAS,EAAE,OAAO,SAAS,OAAO,EAC/E,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,SAAS,KAAK,UAAU,cAA/D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,SAAS,KAAK,UAAU,CAGtF"}
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/interfaces/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EACH,QAAQ,EACR,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,OAAO,EACP,QAAQ,EACR,aAAa,EAChB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,MAAM,WAAW,OAAO;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAClC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CACpD;AAED,MAAM,WAAW,QAAQ,CACrB,QAAQ,SAAS,EAAE,EACnB,OAAO,SAAS,OAAO,EACvB,QAAQ,GAAG,QAAQ,EACnB,eAAe,GAAG,cAAc,EAChC,aAAa,GAAG,YAAY,CAAC,QAAQ,CAAC;IAGtC,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;IAElC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAEtB,EAAE,EAAE,QAAQ,CAAA;IAEZ,OAAO,EAAE,QAAQ,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,eAAe,CAAA;IAE/B,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IACzB,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAE3C,YAAY,EAAE,aAAa,CAAA;IAE3B,SAAS,EAAE,SAAS,CAAA;CACvB;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,UAAU;IACvB,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC;QACrC,cAAc,EAAE,MAAM,CAAA;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;KACpD,CAAC,CAAA;IACF,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,cAAc,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAA;IAC/F,SAAS,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzE,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5D;AAED,wBAAgB,WAAW,CAAC,QAAQ,SAAS,EAAE,EAAE,SAAS,EAAE,OAAO,SAAS,OAAO,EAC/E,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,SAAS,KAAK,UAAU,cAA/D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,SAAS,KAAK,UAAU,CAGtF"}
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createAgent = createAgent;
4
- function createAgent(creator) {
1
+ export function createAgent(creator) {
5
2
  return creator;
6
3
  }
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
@@ -0,0 +1,41 @@
1
+ import { Pool, PoolConfig, QueryResult } from 'pg';
2
+ import { Analytics, ConversationAnalytics } from './analytics.js';
3
+ export interface ConversationRecord {
4
+ conversationId: string;
5
+ agentId?: string;
6
+ channel: string;
7
+ region?: string;
8
+ startedAt: Date;
9
+ endedAt?: Date;
10
+ durationSeconds?: number;
11
+ }
12
+ export interface PhoneCallRecord {
13
+ conversationId: string;
14
+ phoneNumber?: string;
15
+ recordingUrl?: string;
16
+ endedReason?: string;
17
+ }
18
+ interface Queryable {
19
+ query<TResult extends Record<string, unknown> = Record<string, unknown>>(text: string, values?: unknown[]): Promise<QueryResult<TResult>>;
20
+ }
21
+ export declare class PostgresAnalytics implements Analytics {
22
+ private readonly database;
23
+ constructor(database: Queryable);
24
+ getConversationAnalytics(conversationId: string): ConversationAnalytics;
25
+ }
26
+ export declare class PostgresConversationRepository {
27
+ private readonly pool;
28
+ constructor(pool: Pool);
29
+ saveConversation(conversation: ConversationRecord, phoneCall?: PhoneCallRecord): Promise<void>;
30
+ private upsertConversation;
31
+ private upsertPhoneCall;
32
+ }
33
+ export declare function migrateAnalyticsDatabase(pool: Pool): Promise<void>;
34
+ export interface PostgresAnalyticsServices {
35
+ analytics: PostgresAnalytics;
36
+ conversations: PostgresConversationRepository;
37
+ close(): Promise<void>;
38
+ }
39
+ export declare function createPostgresAnalyticsServices(config: PoolConfig): Promise<PostgresAnalyticsServices>;
40
+ export {};
41
+ //# sourceMappingURL=postgres-analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-analytics.d.ts","sourceRoot":"","sources":["../src/postgres-analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAc,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAA;AAE9D,OAAO,EACH,SAAS,EAGT,qBAAqB,EAGxB,MAAM,gBAAgB,CAAA;AAEvB,MAAM,WAAW,kBAAkB;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;IACf,OAAO,CAAC,EAAE,IAAI,CAAA;IACd,eAAe,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,UAAU,SAAS;IACf,KAAK,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;CACnC;AAYD,qBAAa,iBAAkB,YAAW,SAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,SAAS;IAEhD,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;CA6E1E;AAQD,qBAAa,8BAA8B;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAEjC,gBAAgB,CAClB,YAAY,EAAE,kBAAkB,EAChC,SAAS,CAAC,EAAE,eAAe,GAC5B,OAAO,CAAC,IAAI,CAAC;YAiBF,kBAAkB;YAkClB,eAAe;CAoBhC;AA+CD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCxE;AAED,MAAM,WAAW,yBAAyB;IACtC,SAAS,EAAE,iBAAiB,CAAA;IAC5B,aAAa,EAAE,8BAA8B,CAAA;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,wBAAsB,+BAA+B,CACjD,MAAM,EAAE,UAAU,GACnB,OAAO,CAAC,yBAAyB,CAAC,CAapC"}
@@ -0,0 +1,233 @@
1
+ import { Pool } from 'pg';
2
+ import { assertAnalyticsJsonValue, } from './analytics.js';
3
+ export class PostgresAnalytics {
4
+ database;
5
+ constructor(database) {
6
+ this.database = database;
7
+ }
8
+ getConversationAnalytics(conversationId) {
9
+ const database = this.database;
10
+ return {
11
+ async set(key, value) {
12
+ const parsed = key.schema.parse(value);
13
+ assertAnalyticsJsonValue(parsed);
14
+ const serialized = JSON.stringify(parsed);
15
+ if (serialized === undefined) {
16
+ throw new Error(`Analytics value for '${key.key}' is not valid JSON`);
17
+ }
18
+ const result = await database.query(`UPDATE conversations
19
+ SET analytics = jsonb_set(analytics, ARRAY[$2::text], $3::jsonb, true),
20
+ updated_at = now()
21
+ WHERE conversation_id = $1`, [conversationId, key.key, serialized]);
22
+ assertConversationExists(result.rowCount, conversationId);
23
+ },
24
+ async clear(key) {
25
+ const result = await database.query(`UPDATE conversations
26
+ SET analytics = analytics - $2::text,
27
+ updated_at = now()
28
+ WHERE conversation_id = $1`, [conversationId, key.key]);
29
+ assertConversationExists(result.rowCount, conversationId);
30
+ },
31
+ async get(key) {
32
+ const result = await database.query(`SELECT analytics ? $2::text AS present,
33
+ analytics -> $2::text AS value
34
+ FROM conversations
35
+ WHERE conversation_id = $1`, [conversationId, key.key]);
36
+ if (result.rows.length === 0 || !result.rows[0].present) {
37
+ return undefined;
38
+ }
39
+ return key.schema.parse(result.rows[0].value);
40
+ },
41
+ async getSummary() {
42
+ const result = await database.query(`SELECT conversation_id,
43
+ agent_id,
44
+ channel,
45
+ region,
46
+ started_at,
47
+ ended_at,
48
+ duration_seconds
49
+ FROM conversations
50
+ WHERE conversation_id = $1`, [conversationId]);
51
+ const row = result.rows[0];
52
+ if (!row) {
53
+ return undefined;
54
+ }
55
+ return {
56
+ conversationId: row.conversation_id,
57
+ agentId: row.agent_id ?? undefined,
58
+ channel: row.channel,
59
+ region: row.region ?? undefined,
60
+ startedAt: row.started_at,
61
+ endedAt: row.ended_at ?? undefined,
62
+ durationSeconds: row.duration_seconds ?? undefined,
63
+ };
64
+ },
65
+ };
66
+ }
67
+ }
68
+ function assertConversationExists(rowCount, conversationId) {
69
+ if (rowCount !== 1) {
70
+ throw new Error(`Conversation '${conversationId}' does not exist`);
71
+ }
72
+ }
73
+ export class PostgresConversationRepository {
74
+ pool;
75
+ constructor(pool) {
76
+ this.pool = pool;
77
+ }
78
+ async saveConversation(conversation, phoneCall) {
79
+ const client = await this.pool.connect();
80
+ try {
81
+ await client.query('BEGIN');
82
+ await this.upsertConversation(client, conversation);
83
+ if (phoneCall) {
84
+ await this.upsertPhoneCall(client, phoneCall);
85
+ }
86
+ await client.query('COMMIT');
87
+ }
88
+ catch (error) {
89
+ await client.query('ROLLBACK');
90
+ throw error;
91
+ }
92
+ finally {
93
+ client.release();
94
+ }
95
+ }
96
+ async upsertConversation(client, conversation) {
97
+ await client.query(`INSERT INTO conversations (
98
+ conversation_id,
99
+ agent_id,
100
+ channel,
101
+ region,
102
+ started_at,
103
+ ended_at,
104
+ duration_seconds
105
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7)
106
+ ON CONFLICT (conversation_id) DO UPDATE SET
107
+ agent_id = EXCLUDED.agent_id,
108
+ channel = EXCLUDED.channel,
109
+ region = EXCLUDED.region,
110
+ started_at = EXCLUDED.started_at,
111
+ ended_at = EXCLUDED.ended_at,
112
+ duration_seconds = EXCLUDED.duration_seconds,
113
+ updated_at = now()`, [
114
+ conversation.conversationId,
115
+ conversation.agentId ?? null,
116
+ conversation.channel,
117
+ conversation.region ?? null,
118
+ conversation.startedAt,
119
+ conversation.endedAt ?? null,
120
+ conversation.durationSeconds ?? null,
121
+ ]);
122
+ }
123
+ async upsertPhoneCall(client, phoneCall) {
124
+ await client.query(`INSERT INTO phone_calls (
125
+ conversation_id,
126
+ phone_number,
127
+ recording_url,
128
+ ended_reason
129
+ ) VALUES ($1, $2, $3, $4)
130
+ ON CONFLICT (conversation_id) DO UPDATE SET
131
+ phone_number = EXCLUDED.phone_number,
132
+ recording_url = EXCLUDED.recording_url,
133
+ ended_reason = EXCLUDED.ended_reason`, [
134
+ phoneCall.conversationId,
135
+ phoneCall.phoneNumber ?? null,
136
+ phoneCall.recordingUrl ?? null,
137
+ phoneCall.endedReason ?? null,
138
+ ]);
139
+ }
140
+ }
141
+ const migrations = [
142
+ {
143
+ version: 1,
144
+ sql: `
145
+ CREATE TABLE conversations (
146
+ conversation_id text PRIMARY KEY,
147
+ agent_id text,
148
+ channel text NOT NULL,
149
+ region text,
150
+ started_at timestamptz NOT NULL,
151
+ ended_at timestamptz,
152
+ duration_seconds double precision CHECK (
153
+ duration_seconds IS NULL OR duration_seconds >= 0
154
+ ),
155
+ analytics jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (
156
+ jsonb_typeof(analytics) = 'object'
157
+ ),
158
+ created_at timestamptz NOT NULL DEFAULT now(),
159
+ updated_at timestamptz NOT NULL DEFAULT now()
160
+ );
161
+
162
+ CREATE TABLE phone_calls (
163
+ conversation_id text PRIMARY KEY REFERENCES conversations(conversation_id)
164
+ ON DELETE CASCADE,
165
+ phone_number text,
166
+ recording_url text,
167
+ ended_reason text
168
+ );
169
+
170
+ CREATE INDEX conversations_started_at_idx
171
+ ON conversations (started_at DESC, conversation_id DESC);
172
+ CREATE INDEX conversations_agent_started_at_idx
173
+ ON conversations (agent_id, started_at DESC, conversation_id DESC);
174
+ CREATE INDEX conversations_region_started_at_idx
175
+ ON conversations (region, started_at DESC, conversation_id DESC);
176
+ CREATE INDEX conversations_channel_started_at_idx
177
+ ON conversations (channel, started_at DESC, conversation_id DESC);
178
+ CREATE INDEX conversations_analytics_idx
179
+ ON conversations USING gin (analytics);
180
+ CREATE INDEX phone_calls_phone_number_idx
181
+ ON phone_calls (phone_number, conversation_id);
182
+ `,
183
+ },
184
+ ];
185
+ export async function migrateAnalyticsDatabase(pool) {
186
+ const client = await pool.connect();
187
+ try {
188
+ await client.query("SELECT pg_advisory_lock(hashtext('recombine_analytics_migrations'))");
189
+ await client.query(`
190
+ CREATE TABLE IF NOT EXISTS analytics_schema_migrations (
191
+ version integer PRIMARY KEY,
192
+ applied_at timestamptz NOT NULL DEFAULT now()
193
+ )
194
+ `);
195
+ for (const migration of migrations) {
196
+ const applied = await client.query(`SELECT EXISTS (
197
+ SELECT 1 FROM analytics_schema_migrations WHERE version = $1
198
+ ) AS exists`, [migration.version]);
199
+ if (applied.rows[0].exists) {
200
+ continue;
201
+ }
202
+ await client.query('BEGIN');
203
+ try {
204
+ await client.query(migration.sql);
205
+ await client.query('INSERT INTO analytics_schema_migrations (version) VALUES ($1)', [migration.version]);
206
+ await client.query('COMMIT');
207
+ }
208
+ catch (error) {
209
+ await client.query('ROLLBACK');
210
+ throw error;
211
+ }
212
+ }
213
+ }
214
+ finally {
215
+ await client.query("SELECT pg_advisory_unlock(hashtext('recombine_analytics_migrations'))");
216
+ client.release();
217
+ }
218
+ }
219
+ export async function createPostgresAnalyticsServices(config) {
220
+ const pool = new Pool(config);
221
+ try {
222
+ await migrateAnalyticsDatabase(pool);
223
+ }
224
+ catch (error) {
225
+ await pool.end();
226
+ throw error;
227
+ }
228
+ return {
229
+ analytics: new PostgresAnalytics(pool),
230
+ conversations: new PostgresConversationRepository(pool),
231
+ close: () => pool.end(),
232
+ };
233
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=postgres-analytics.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-analytics.spec.d.ts","sourceRoot":"","sources":["../src/postgres-analytics.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { z } from 'zod';
3
+ import { analyticsKey } from './analytics.js';
4
+ import { PostgresAnalytics } from './postgres-analytics.js';
5
+ function result(rows = [], rowCount = rows.length) {
6
+ return { rows, rowCount };
7
+ }
8
+ describe('PostgresAnalytics', () => {
9
+ it('stores a typed value under one literal JSON key', async () => {
10
+ const query = vi.fn().mockResolvedValue(result([], 1));
11
+ const analytics = new PostgresAnalytics({ query });
12
+ await analytics
13
+ .getConversationAnalytics('conversation-1')
14
+ .set(analyticsKey.boolean('common.escalated'), true);
15
+ expect(query).toHaveBeenCalledWith(expect.stringContaining('jsonb_set'), [
16
+ 'conversation-1',
17
+ 'common.escalated',
18
+ 'true',
19
+ ]);
20
+ });
21
+ it('validates values before writing', async () => {
22
+ const query = vi.fn();
23
+ const analytics = new PostgresAnalytics({ query });
24
+ await expect(analytics
25
+ .getConversationAnalytics('conversation-1')
26
+ .set(analyticsKey.number('score'), 'wrong')).rejects.toThrow();
27
+ expect(query).not.toHaveBeenCalled();
28
+ });
29
+ it('reads JSON null as a present value', async () => {
30
+ const query = vi.fn().mockResolvedValue(result([{ present: true, value: null }]));
31
+ const analytics = new PostgresAnalytics({ query });
32
+ await expect(analytics
33
+ .getConversationAnalytics('conversation-1')
34
+ .get(analyticsKey.json('result', z.null()))).resolves.toBeNull();
35
+ });
36
+ it('returns a common conversation summary', async () => {
37
+ const startedAt = new Date('2026-08-01T10:00:00Z');
38
+ const endedAt = new Date('2026-08-01T10:02:00Z');
39
+ const query = vi.fn().mockResolvedValue(result([
40
+ {
41
+ conversation_id: 'conversation-1',
42
+ agent_id: 'support-agent',
43
+ channel: 'phone',
44
+ region: 'GB',
45
+ started_at: startedAt,
46
+ ended_at: endedAt,
47
+ duration_seconds: 120,
48
+ },
49
+ ]));
50
+ const analytics = new PostgresAnalytics({ query });
51
+ await expect(analytics.getConversationAnalytics('conversation-1').getSummary()).resolves.toEqual({
52
+ conversationId: 'conversation-1',
53
+ agentId: 'support-agent',
54
+ channel: 'phone',
55
+ region: 'GB',
56
+ startedAt,
57
+ endedAt,
58
+ durationSeconds: 120,
59
+ });
60
+ });
61
+ });
@@ -1,5 +1,5 @@
1
1
  import { Logger } from '@recombine-ai/engine';
2
- import { ContextCache } from './context-cache';
2
+ import { ContextCache } from './context-cache.js';
3
3
  export type TimeoutsState = {
4
4
  timeouts: Record<string, number>;
5
5
  };
@@ -1 +1 @@
1
- {"version":3,"file":"timeouts-manager.d.ts","sourceRoot":"","sources":["../src/timeouts-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAK9C,MAAM,MAAM,aAAa,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC,CAAA;AAKD,KAAK,kBAAkB,GAAG;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAC,OAAO,CAAA;CACxB,CAAA;AAED,qBAAa,cAAc;IAOnB,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAC,aAAa,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM;IAEvB,OAAO,CAAC,SAAS;IANrB,QAAQ,CAAC,mBAAmB,kDAAwD;gBAGvE,aAAa,EAAE,YAAY,CAAC,aAAa,CAAC,EAC1C,MAAM,EAAE,MAAM,EAEf,SAAS,UAAQ;IAQ7B,gBAAgB,CAAC,MAAM,EAAE,MAAM;IAYzB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAWlE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IA4B7D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM;IAmEvF,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAYxD"}
1
+ {"version":3,"file":"timeouts-manager.d.ts","sourceRoot":"","sources":["../src/timeouts-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAKjD,MAAM,MAAM,aAAa,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC,CAAA;AAKD,KAAK,kBAAkB,GAAG;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAC,OAAO,CAAA;CACxB,CAAA;AAED,qBAAa,cAAc;IAOnB,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAC,aAAa,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM;IAEvB,OAAO,CAAC,SAAS;IANrB,QAAQ,CAAC,mBAAmB,kDAAwD;gBAGvE,aAAa,EAAE,YAAY,CAAC,aAAa,CAAC,EAC1C,MAAM,EAAE,MAAM,EAEf,SAAS,UAAQ;IAQ7B,gBAAgB,CAAC,MAAM,EAAE,MAAM;IAYzB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAWlE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IA4B7D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM;IAmEvF,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAYxD"}
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TimeoutManager = void 0;
4
- class TimeoutManager {
1
+ export class TimeoutManager {
5
2
  timeoutsCache;
6
3
  logger;
7
4
  shallSkip;
@@ -100,4 +97,3 @@ class TimeoutManager {
100
97
  this.logger.debug(`TimeoutManager: cleared all timeouts`, { callId });
101
98
  }
102
99
  }
103
- exports.TimeoutManager = TimeoutManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recombine-ai/platform",
3
- "version": "0.2.5",
3
+ "version": "0.2.6-test.2",
4
4
  "description": "Recombine AI: voice agent platform",
5
5
  "license": "EULA",
6
6
  "author": "recombine.ai",
@@ -12,18 +12,27 @@
12
12
  "files": [
13
13
  "build"
14
14
  ],
15
- "main": "./build/index.js",
15
+ "type": "module",
16
16
  "types": "./build/index.d.ts",
17
17
  "dependencies": {
18
18
  "@recombine-ai/engine": "1.0.0",
19
- "@recombine-ai/telescope": "^0.3.0",
19
+ "@recombine-ai/telescope": "0.2.0",
20
+ "pg": "8.16.3",
20
21
  "redis": "5.11.0",
21
22
  "zod": "^4.4.3"
22
23
  },
24
+ "exports": {
25
+ ".": {
26
+ "types": "./build/index.d.ts",
27
+ "import": "./build/index.js",
28
+ "default": "./build/index.js"
29
+ }
30
+ },
23
31
  "publishConfig": {
24
32
  "registry": "https://registry.npmjs.org"
25
33
  },
26
34
  "devDependencies": {
27
- "@types/node": "^25.2.3"
35
+ "@types/node": "^25.2.3",
36
+ "@types/pg": "8.15.5"
28
37
  }
29
38
  }