@chatcops/core 0.3.2 → 0.5.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 (162) hide show
  1. package/dist/agent/chat.d.ts +13 -0
  2. package/dist/agent/chat.d.ts.map +1 -0
  3. package/dist/agent/chat.js +42 -0
  4. package/dist/agent/chat.js.map +1 -0
  5. package/dist/agent/errors.d.ts +14 -0
  6. package/dist/agent/errors.d.ts.map +1 -0
  7. package/dist/agent/errors.js +24 -0
  8. package/dist/agent/errors.js.map +1 -0
  9. package/dist/agent/index.d.ts +5 -0
  10. package/dist/agent/index.d.ts.map +1 -0
  11. package/dist/agent/index.js +5 -0
  12. package/dist/agent/index.js.map +1 -0
  13. package/dist/agent/run-turn.d.ts +68 -0
  14. package/dist/agent/run-turn.d.ts.map +1 -0
  15. package/dist/agent/run-turn.js +344 -0
  16. package/dist/agent/run-turn.js.map +1 -0
  17. package/dist/agent/tool-registry.d.ts +28 -0
  18. package/dist/agent/tool-registry.d.ts.map +1 -0
  19. package/dist/agent/tool-registry.js +69 -0
  20. package/dist/agent/tool-registry.js.map +1 -0
  21. package/dist/analytics/index.d.ts +1 -0
  22. package/dist/analytics/index.d.ts.map +1 -1
  23. package/dist/analytics/index.js +1 -0
  24. package/dist/analytics/index.js.map +1 -1
  25. package/dist/analytics/sink.d.ts +24 -0
  26. package/dist/analytics/sink.d.ts.map +1 -0
  27. package/dist/analytics/sink.js +106 -0
  28. package/dist/analytics/sink.js.map +1 -0
  29. package/dist/conversation/index.d.ts +1 -0
  30. package/dist/conversation/index.d.ts.map +1 -1
  31. package/dist/conversation/index.js.map +1 -1
  32. package/dist/conversation/manager.d.ts +62 -6
  33. package/dist/conversation/manager.d.ts.map +1 -1
  34. package/dist/conversation/manager.js +109 -30
  35. package/dist/conversation/manager.js.map +1 -1
  36. package/dist/conversation/store.d.ts +6 -5
  37. package/dist/conversation/store.d.ts.map +1 -1
  38. package/dist/conversation/store.js +4 -0
  39. package/dist/conversation/store.js.map +1 -1
  40. package/dist/index.d.ts +18 -4
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +14 -2
  43. package/dist/index.js.map +1 -1
  44. package/dist/limits/budget.d.ts +84 -0
  45. package/dist/limits/budget.d.ts.map +1 -0
  46. package/dist/limits/budget.js +114 -0
  47. package/dist/limits/budget.js.map +1 -0
  48. package/dist/limits/rate-limit.d.ts +53 -0
  49. package/dist/limits/rate-limit.d.ts.map +1 -0
  50. package/dist/limits/rate-limit.js +68 -0
  51. package/dist/limits/rate-limit.js.map +1 -0
  52. package/dist/parts.d.ts +9 -0
  53. package/dist/parts.d.ts.map +1 -0
  54. package/dist/parts.js +17 -0
  55. package/dist/parts.js.map +1 -0
  56. package/dist/protocol/ai-sdk.d.ts +27 -0
  57. package/dist/protocol/ai-sdk.d.ts.map +1 -0
  58. package/dist/protocol/ai-sdk.js +127 -0
  59. package/dist/protocol/ai-sdk.js.map +1 -0
  60. package/dist/protocol/errors.d.ts +10 -0
  61. package/dist/protocol/errors.d.ts.map +1 -0
  62. package/dist/protocol/errors.js +42 -0
  63. package/dist/protocol/errors.js.map +1 -0
  64. package/dist/protocol/index.d.ts +9 -0
  65. package/dist/protocol/index.d.ts.map +1 -0
  66. package/dist/protocol/index.js +6 -0
  67. package/dist/protocol/index.js.map +1 -0
  68. package/dist/protocol/legacy.d.ts +18 -0
  69. package/dist/protocol/legacy.d.ts.map +1 -0
  70. package/dist/protocol/legacy.js +41 -0
  71. package/dist/protocol/legacy.js.map +1 -0
  72. package/dist/protocol/sse.d.ts +10 -0
  73. package/dist/protocol/sse.d.ts.map +1 -0
  74. package/dist/protocol/sse.js +15 -0
  75. package/dist/protocol/sse.js.map +1 -0
  76. package/dist/protocol/v1.d.ts +140 -0
  77. package/dist/protocol/v1.d.ts.map +1 -0
  78. package/dist/protocol/v1.js +15 -0
  79. package/dist/protocol/v1.js.map +1 -0
  80. package/dist/providers/base.d.ts +17 -15
  81. package/dist/providers/base.d.ts.map +1 -1
  82. package/dist/providers/base.js +43 -31
  83. package/dist/providers/base.js.map +1 -1
  84. package/dist/providers/claude.d.ts +40 -2
  85. package/dist/providers/claude.d.ts.map +1 -1
  86. package/dist/providers/claude.js +250 -113
  87. package/dist/providers/claude.js.map +1 -1
  88. package/dist/providers/gemini.d.ts +23 -3
  89. package/dist/providers/gemini.d.ts.map +1 -1
  90. package/dist/providers/gemini.js +245 -102
  91. package/dist/providers/gemini.js.map +1 -1
  92. package/dist/providers/history.d.ts +52 -0
  93. package/dist/providers/history.d.ts.map +1 -0
  94. package/dist/providers/history.js +212 -0
  95. package/dist/providers/history.js.map +1 -0
  96. package/dist/providers/index.d.ts +5 -5
  97. package/dist/providers/index.d.ts.map +1 -1
  98. package/dist/providers/index.js +5 -4
  99. package/dist/providers/index.js.map +1 -1
  100. package/dist/providers/openai.d.ts +37 -3
  101. package/dist/providers/openai.d.ts.map +1 -1
  102. package/dist/providers/openai.js +246 -140
  103. package/dist/providers/openai.js.map +1 -1
  104. package/dist/providers/registry.d.ts +7 -0
  105. package/dist/providers/registry.d.ts.map +1 -0
  106. package/dist/providers/registry.js +12 -0
  107. package/dist/providers/registry.js.map +1 -0
  108. package/dist/providers/schema.d.ts +16 -0
  109. package/dist/providers/schema.d.ts.map +1 -0
  110. package/dist/providers/schema.js +80 -0
  111. package/dist/providers/schema.js.map +1 -0
  112. package/dist/providers/tool-execution.d.ts +10 -0
  113. package/dist/providers/tool-execution.d.ts.map +1 -1
  114. package/dist/providers/tool-execution.js +38 -12
  115. package/dist/providers/tool-execution.js.map +1 -1
  116. package/dist/providers/types.d.ts +71 -0
  117. package/dist/providers/types.d.ts.map +1 -0
  118. package/dist/providers/types.js +2 -0
  119. package/dist/providers/types.js.map +1 -0
  120. package/dist/storage/adapt.d.ts +15 -0
  121. package/dist/storage/adapt.d.ts.map +1 -0
  122. package/dist/storage/adapt.js +83 -0
  123. package/dist/storage/adapt.js.map +1 -0
  124. package/dist/storage/errors.d.ts +18 -0
  125. package/dist/storage/errors.d.ts.map +1 -0
  126. package/dist/storage/errors.js +28 -0
  127. package/dist/storage/errors.js.map +1 -0
  128. package/dist/storage/index.d.ts +5 -0
  129. package/dist/storage/index.d.ts.map +1 -0
  130. package/dist/storage/index.js +4 -0
  131. package/dist/storage/index.js.map +1 -0
  132. package/dist/storage/memory.d.ts +24 -0
  133. package/dist/storage/memory.d.ts.map +1 -0
  134. package/dist/storage/memory.js +265 -0
  135. package/dist/storage/memory.js.map +1 -0
  136. package/dist/storage/types.d.ts +118 -0
  137. package/dist/storage/types.d.ts.map +1 -0
  138. package/dist/storage/types.js +2 -0
  139. package/dist/storage/types.js.map +1 -0
  140. package/dist/testing/conformance.d.ts +29 -0
  141. package/dist/testing/conformance.d.ts.map +1 -0
  142. package/dist/testing/conformance.js +335 -0
  143. package/dist/testing/conformance.js.map +1 -0
  144. package/dist/testing/index.d.ts +3 -0
  145. package/dist/testing/index.d.ts.map +1 -0
  146. package/dist/testing/index.js +5 -0
  147. package/dist/testing/index.js.map +1 -0
  148. package/dist/testing/provider.d.ts +45 -0
  149. package/dist/testing/provider.d.ts.map +1 -0
  150. package/dist/testing/provider.js +82 -0
  151. package/dist/testing/provider.js.map +1 -0
  152. package/dist/tools/base.d.ts +14 -4
  153. package/dist/tools/base.d.ts.map +1 -1
  154. package/dist/tools/base.js +4 -2
  155. package/dist/tools/base.js.map +1 -1
  156. package/dist/tools/lead-capture.d.ts +3 -1
  157. package/dist/tools/lead-capture.d.ts.map +1 -1
  158. package/dist/tools/lead-capture.js +6 -1
  159. package/dist/tools/lead-capture.js.map +1 -1
  160. package/dist/types.d.ts +168 -4
  161. package/dist/types.d.ts.map +1 -1
  162. package/package.json +53 -5
@@ -28,23 +28,37 @@ export function toToolFailure(error) {
28
28
  message: error instanceof Error ? error.message : 'Tool execution failed.',
29
29
  };
30
30
  }
31
- export async function executeToolCall(params, call) {
32
- if (!params.toolExecutor) {
33
- return {
34
- success: false,
35
- message: `No tool executor is configured for "${call.name}".`,
36
- };
37
- }
38
- const timeoutMs = params.toolTimeoutMs ?? DEFAULT_TOOL_TIMEOUT_MS;
31
+ /**
32
+ * Runs a tool with a timeout. The tool gets a signal that aborts when the
33
+ * timeout passes or when `signal` aborts (the visitor left), and the result
34
+ * comes back as a failure right away in both cases, so a tool that ignores
35
+ * its signal can't hold up the reply.
36
+ */
37
+ export async function runToolWithTimeout(run, options = {}) {
38
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TOOL_TIMEOUT_MS;
39
39
  const timers = globalThis;
40
+ const controller = new AbortController();
40
41
  let timeoutId;
42
+ let onAbort;
41
43
  try {
42
44
  return await Promise.race([
43
- params.toolExecutor(call),
45
+ run(controller.signal),
44
46
  new Promise((_, reject) => {
45
47
  timeoutId = timers.setTimeout(() => {
46
- reject(new Error(`Tool execution timed out after ${timeoutMs}ms.`));
48
+ const error = new Error(`Tool execution timed out after ${timeoutMs}ms.`);
49
+ controller.abort(error);
50
+ reject(error);
47
51
  }, timeoutMs);
52
+ if (options.signal) {
53
+ onAbort = () => {
54
+ controller.abort(options.signal?.reason);
55
+ reject(new Error('The reply was cancelled before the tool finished.'));
56
+ };
57
+ if (options.signal.aborted)
58
+ onAbort();
59
+ else
60
+ options.signal.addEventListener('abort', onAbort, { once: true });
61
+ }
48
62
  }),
49
63
  ]);
50
64
  }
@@ -52,9 +66,21 @@ export async function executeToolCall(params, call) {
52
66
  return toToolFailure(error);
53
67
  }
54
68
  finally {
55
- if (timeoutId !== undefined) {
69
+ if (timeoutId !== undefined)
56
70
  timers.clearTimeout(timeoutId);
57
- }
71
+ if (onAbort)
72
+ options.signal?.removeEventListener('abort', onAbort);
73
+ }
74
+ }
75
+ export async function executeToolCall(params, call) {
76
+ const executor = params.toolExecutor;
77
+ if (!executor) {
78
+ return {
79
+ success: false,
80
+ message: `No tool executor is configured for "${call.name}".`,
81
+ };
58
82
  }
83
+ // The executor gets the call alone, as in 0.4.
84
+ return runToolWithTimeout(() => executor(call), { timeoutMs: params.toolTimeoutMs });
59
85
  }
60
86
  //# sourceMappingURL=tool-execution.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-execution.js","sourceRoot":"","sources":["../../src/providers/tool-execution.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAE9C,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAEhC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAkB;IACpD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO;QACL,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;KAC3E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAA0B,EAC1B,IAAsB;IAEtB,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,uCAAuC,IAAI,CAAC,IAAI,IAAI;SAC9D,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,IAAI,uBAAuB,CAAC;IAClE,MAAM,MAAM,GAAG,UAGd,CAAC;IACF,IAAI,SAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;YACxB,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC;YACzB,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;oBACjC,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,SAAS,KAAK,CAAC,CAAC,CAAC;gBACtE,CAAC,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;YAAS,CAAC;QACT,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"tool-execution.js","sourceRoot":"","sources":["../../src/providers/tool-execution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import type { ChatMessage, MessagePart, ProviderChatParams, ProviderMetadata, ProviderToolCall, TokenUsage, ToolDefinition } from '../types.js';
2
+ /**
3
+ * Why a model step ended.
4
+ * - `stop`: the model finished its reply
5
+ * - `tool-calls`: the model wants tool results before it goes on
6
+ * - `length`: the reply hit the output token limit
7
+ * - `content-filter`: the provider's safety filter stopped the reply
8
+ * - `refusal`: the model declined to answer
9
+ * - `error`: the request failed partway
10
+ */
11
+ export type FinishReason = 'stop' | 'tool-calls' | 'length' | 'content-filter' | 'refusal' | 'error';
12
+ /** What a provider step yields, in order. Always ends with one `finish`. */
13
+ export type ProviderEvent = {
14
+ type: 'text-delta';
15
+ delta: string;
16
+ } | {
17
+ type: 'reasoning-delta';
18
+ delta: string;
19
+ }
20
+ /**
21
+ * Sent once the call's input is complete. `inputError` is set when the
22
+ * model sent input that isn't a JSON object; the call then can't run, and
23
+ * the error goes back to the model as the tool's result.
24
+ */
25
+ | {
26
+ type: 'tool-call';
27
+ call: ProviderToolCall;
28
+ providerMetadata?: ProviderMetadata;
29
+ inputError?: string;
30
+ } | {
31
+ type: 'finish';
32
+ reason: FinishReason;
33
+ usage: TokenUsage;
34
+ /**
35
+ * Everything the model produced in this step, as it should be stored
36
+ * and sent back on the next step: reasoning (with any signatures), text
37
+ * and tool calls, in order.
38
+ */
39
+ parts: MessagePart[];
40
+ };
41
+ export interface ProviderStepParams {
42
+ /** Stored history. Messages with `parts` include earlier tool calls and results. */
43
+ messages: ChatMessage[];
44
+ /** The operator's instructions. The same on every request, so providers can cache it. */
45
+ systemPrompt: string;
46
+ /**
47
+ * Instructions that change per request (knowledge base excerpts, the
48
+ * visitor's language), sent after `systemPrompt` and outside the cached
49
+ * prefix.
50
+ */
51
+ systemContext?: string;
52
+ tools?: ToolDefinition[];
53
+ maxTokens?: number;
54
+ /** Dropped, with a warning once, for models that don't accept it. */
55
+ temperature?: number;
56
+ signal?: AbortSignal;
57
+ }
58
+ export interface AIProvider {
59
+ name: string;
60
+ /** The model this provider sends requests to. */
61
+ model?: string;
62
+ /**
63
+ * Runs one model request and yields what comes back, without running any
64
+ * tools. Optional, so custom providers written for 0.4 that only have
65
+ * `chat()` keep working.
66
+ */
67
+ step?(params: ProviderStepParams): AsyncGenerator<ProviderEvent>;
68
+ chat(params: ProviderChatParams): AsyncGenerator<string>;
69
+ chatSync(params: ProviderChatParams): Promise<string>;
70
+ }
71
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,cAAc,EACf,MAAM,aAAa,CAAC;AAErB;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,GAAG,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC;AAErG,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC5C;;;;GAIG;GACD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACvG;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,UAAU,CAAC;IAClB;;;;OAIG;IACH,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,yFAAyF;IACzF,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,IAAI,CAAC,CAAC,MAAM,EAAE,kBAAkB,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;IACjE,IAAI,CAAC,MAAM,EAAE,kBAAkB,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACzD,QAAQ,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACvD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import type { ConversationStore } from './types.js';
2
+ /** A store with the message-level writes the server uses. */
3
+ export type ConversationStoreWithAppend = ConversationStore & Required<Pick<ConversationStore, 'append' | 'updateMessage' | 'removeMessage'>>;
4
+ /** True when the store only has the 0.4 methods (`get`, `save`, `delete`). */
5
+ export declare function isLegacyConversationStore(store: ConversationStore): boolean;
6
+ /**
7
+ * Gives any store `append`, `updateMessage` and `removeMessage`. Methods the
8
+ * store has are used as they are. The missing ones read the conversation,
9
+ * change it and save it back, one write at a time per conversation within
10
+ * this process. That's safe for a single server, but two instances writing
11
+ * the same conversation can still overwrite each other, which is why stores
12
+ * should implement `append` themselves.
13
+ */
14
+ export declare function withAppend(store: ConversationStore): ConversationStoreWithAppend;
15
+ //# sourceMappingURL=adapt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapt.d.ts","sourceRoot":"","sources":["../../src/storage/adapt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE,6DAA6D;AAC7D,MAAM,MAAM,2BAA2B,GAAG,iBAAiB,GACzD,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,eAAe,GAAG,eAAe,CAAC,CAAC,CAAC;AAElF,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAE3E;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,2BAA2B,CAsFhF"}
@@ -0,0 +1,83 @@
1
+ import { ConflictError } from './errors.js';
2
+ /** True when the store only has the 0.4 methods (`get`, `save`, `delete`). */
3
+ export function isLegacyConversationStore(store) {
4
+ return !store.append;
5
+ }
6
+ /**
7
+ * Gives any store `append`, `updateMessage` and `removeMessage`. Methods the
8
+ * store has are used as they are. The missing ones read the conversation,
9
+ * change it and save it back, one write at a time per conversation within
10
+ * this process. That's safe for a single server, but two instances writing
11
+ * the same conversation can still overwrite each other, which is why stores
12
+ * should implement `append` themselves.
13
+ */
14
+ export function withAppend(store) {
15
+ if (store.append && store.updateMessage && store.removeMessage) {
16
+ return store;
17
+ }
18
+ const queues = new Map();
19
+ function serialized(id, fn) {
20
+ const previous = queues.get(id) ?? Promise.resolve();
21
+ const run = previous.then(fn, fn);
22
+ const settled = run.then(() => undefined, () => undefined);
23
+ queues.set(id, settled);
24
+ void settled.then(() => {
25
+ if (queues.get(id) === settled)
26
+ queues.delete(id);
27
+ });
28
+ return run;
29
+ }
30
+ async function rewrite(id, change) {
31
+ const current = await store.get(id);
32
+ if (!current)
33
+ return false;
34
+ const next = change(current);
35
+ if (!next)
36
+ return false;
37
+ await store.save({ ...next, version: (current.version ?? 0) + 1, updatedAt: Date.now() });
38
+ return true;
39
+ }
40
+ return {
41
+ get: (id) => store.get(id),
42
+ save: (conversation) => store.save(conversation),
43
+ delete: (id) => store.delete(id),
44
+ ...(store.list ? { list: store.list.bind(store) } : {}),
45
+ ...(store.purge ? { purge: store.purge.bind(store) } : {}),
46
+ append: store.append
47
+ ? store.append.bind(store)
48
+ : (id, messages, options = {}) => serialized(id, async () => {
49
+ const current = await store.get(id);
50
+ const version = current?.version ?? 0;
51
+ if (options.expectedVersion !== undefined && options.expectedVersion !== version) {
52
+ throw new ConflictError(id, options.expectedVersion, version);
53
+ }
54
+ const time = Date.now();
55
+ const metadata = options.metadata || current?.metadata ? { ...current?.metadata, ...options.metadata } : undefined;
56
+ await store.save({
57
+ id,
58
+ messages: [...(current?.messages ?? []), ...messages],
59
+ ...(metadata ? { metadata } : {}),
60
+ version: version + 1,
61
+ createdAt: current?.createdAt ?? time,
62
+ updatedAt: time,
63
+ });
64
+ return { version: version + 1 };
65
+ }),
66
+ updateMessage: store.updateMessage
67
+ ? store.updateMessage.bind(store)
68
+ : (id, message) => serialized(id, () => rewrite(id, (current) => {
69
+ const index = current.messages.findIndex((m) => m.id === message.id);
70
+ if (index === -1)
71
+ return null;
72
+ const messages = [...current.messages];
73
+ messages[index] = message;
74
+ return { ...current, messages };
75
+ })),
76
+ removeMessage: store.removeMessage
77
+ ? store.removeMessage.bind(store)
78
+ : (id, messageId) => serialized(id, () => rewrite(id, (current) => current.messages.some((m) => m.id === messageId)
79
+ ? { ...current, messages: current.messages.filter((m) => m.id !== messageId) }
80
+ : null)),
81
+ };
82
+ }
83
+ //# sourceMappingURL=adapt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapt.js","sourceRoot":"","sources":["../../src/storage/adapt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export declare const CONFLICT_ERROR_CODE = "chatcops_conversation_conflict";
2
+ /**
3
+ * Thrown by `ConversationStore.append` when `expectedVersion` doesn't match.
4
+ * Another request wrote to the conversation since it was read.
5
+ */
6
+ export declare class ConflictError extends Error {
7
+ readonly conversationId: string;
8
+ readonly expectedVersion: number;
9
+ readonly actualVersion: number;
10
+ readonly code = "chatcops_conversation_conflict";
11
+ constructor(conversationId: string, expectedVersion: number, actualVersion: number);
12
+ }
13
+ /**
14
+ * Checks by `code` rather than `instanceof`, so it also works when a store
15
+ * package ends up with its own copy of `@chatcops/core`.
16
+ */
17
+ export declare function isConflictError(error: unknown): error is ConflictError;
18
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/storage/errors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,mCAAmC,CAAC;AAEpE;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAIpC,QAAQ,CAAC,cAAc,EAAE,MAAM;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM;IALhC,QAAQ,CAAC,IAAI,oCAAuB;gBAGzB,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM;CAOjC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAMtE"}
@@ -0,0 +1,28 @@
1
+ export const CONFLICT_ERROR_CODE = 'chatcops_conversation_conflict';
2
+ /**
3
+ * Thrown by `ConversationStore.append` when `expectedVersion` doesn't match.
4
+ * Another request wrote to the conversation since it was read.
5
+ */
6
+ export class ConflictError extends Error {
7
+ conversationId;
8
+ expectedVersion;
9
+ actualVersion;
10
+ code = CONFLICT_ERROR_CODE;
11
+ constructor(conversationId, expectedVersion, actualVersion) {
12
+ super(`Conversation ${conversationId} is at version ${actualVersion}, expected ${expectedVersion}. Another request wrote to it first.`);
13
+ this.conversationId = conversationId;
14
+ this.expectedVersion = expectedVersion;
15
+ this.actualVersion = actualVersion;
16
+ this.name = 'ConflictError';
17
+ }
18
+ }
19
+ /**
20
+ * Checks by `code` rather than `instanceof`, so it also works when a store
21
+ * package ends up with its own copy of `@chatcops/core`.
22
+ */
23
+ export function isConflictError(error) {
24
+ return (typeof error === 'object' &&
25
+ error !== null &&
26
+ error.code === CONFLICT_ERROR_CODE);
27
+ }
28
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/storage/errors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,gCAAgC,CAAC;AAEpE;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAI3B;IACA;IACA;IALF,IAAI,GAAG,mBAAmB,CAAC;IAEpC,YACW,cAAsB,EACtB,eAAuB,EACvB,aAAqB;QAE9B,KAAK,CACH,gBAAgB,cAAc,kBAAkB,aAAa,cAAc,eAAe,sCAAsC,CACjI,CAAC;QANO,mBAAc,GAAd,cAAc,CAAQ;QACtB,oBAAe,GAAf,eAAe,CAAQ;QACvB,kBAAa,GAAb,aAAa,CAAQ;QAK9B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAA4B,CAAC,IAAI,KAAK,mBAAmB,CAC3D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { AppendOptions, ChatCopsStorage, ConversationStore, ConversationSummary, CounterStore, CounterValue, EventCount, EventQuery, EventSink, KeyValueStore, ListQuery, } from './types.js';
2
+ export { ConflictError, isConflictError, CONFLICT_ERROR_CODE } from './errors.js';
3
+ export { memoryStorage, type MemoryStorageOptions } from './memory.js';
4
+ export { withAppend, isLegacyConversationStore, type ConversationStoreWithAppend } from './adapt.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACb,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAE,KAAK,2BAA2B,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { ConflictError, isConflictError, CONFLICT_ERROR_CODE } from './errors.js';
2
+ export { memoryStorage } from './memory.js';
3
+ export { withAppend, isLegacyConversationStore } from './adapt.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,aAAa,EAA6B,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAoC,MAAM,YAAY,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ChatCopsStorage } from './types.js';
2
+ export interface MemoryStorageOptions {
3
+ /** Least recently used conversations go first past this. Default 1000. */
4
+ maxConversations?: number;
5
+ /** Conversations not touched for this long are dropped. Default 7 days. */
6
+ conversationTtlMs?: number;
7
+ /** Counters and keys each, past which expired ones are swept and then the oldest dropped. Default 10,000. */
8
+ maxKeys?: number;
9
+ /** Oldest events go first past this. Default 10,000. */
10
+ maxEvents?: number;
11
+ /** Clock, for tests. */
12
+ now?: () => number;
13
+ }
14
+ /**
15
+ * Keeps everything in this process. Fine for local development and a single
16
+ * long-running server; on serverless each instance has its own copy, so
17
+ * conversations and limits don't carry over between requests.
18
+ *
19
+ * Every capability is bounded, and nothing runs on a timer: expired entries
20
+ * are dropped when they're next read or when a map reaches its cap. Objects
21
+ * are copied in and out, so callers can't change stored data by accident.
22
+ */
23
+ export declare function memoryStorage(options?: MemoryStorageOptions): ChatCopsStorage;
24
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,eAAe,EAUhB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6GAA6G;IAC7G,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAKD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,CAYjF"}
@@ -0,0 +1,265 @@
1
+ import { ConflictError } from './errors.js';
2
+ const DAY_MS = 24 * 60 * 60 * 1000;
3
+ const HOUR_MS = 60 * 60 * 1000;
4
+ /**
5
+ * Keeps everything in this process. Fine for local development and a single
6
+ * long-running server; on serverless each instance has its own copy, so
7
+ * conversations and limits don't carry over between requests.
8
+ *
9
+ * Every capability is bounded, and nothing runs on a timer: expired entries
10
+ * are dropped when they're next read or when a map reaches its cap. Objects
11
+ * are copied in and out, so callers can't change stored data by accident.
12
+ */
13
+ export function memoryStorage(options = {}) {
14
+ const now = options.now ?? Date.now;
15
+ return {
16
+ conversations: new MemoryConversationStore(options.maxConversations ?? 1000, options.conversationTtlMs ?? 7 * DAY_MS, now),
17
+ counters: new MemoryCounterStore(options.maxKeys ?? 10_000, now),
18
+ kv: new MemoryKeyValueStore(options.maxKeys ?? 10_000, now),
19
+ events: new MemoryEventSink(options.maxEvents ?? 10_000),
20
+ };
21
+ }
22
+ class MemoryConversationStore {
23
+ maxConversations;
24
+ ttlMs;
25
+ now;
26
+ records = new Map();
27
+ constructor(maxConversations, ttlMs, now) {
28
+ this.maxConversations = maxConversations;
29
+ this.ttlMs = ttlMs;
30
+ this.now = now;
31
+ }
32
+ async get(id) {
33
+ const record = this.read(id);
34
+ return record ? structuredClone(record) : null;
35
+ }
36
+ async save(conversation) {
37
+ const current = this.read(conversation.id);
38
+ this.write({
39
+ ...structuredClone(conversation),
40
+ version: (current?.version ?? 0) + 1,
41
+ updatedAt: this.now(),
42
+ });
43
+ }
44
+ async delete(id) {
45
+ this.records.delete(id);
46
+ }
47
+ async append(id, messages, options = {}) {
48
+ const current = this.read(id);
49
+ const version = current?.version ?? 0;
50
+ if (options.expectedVersion !== undefined && options.expectedVersion !== version) {
51
+ throw new ConflictError(id, options.expectedVersion, version);
52
+ }
53
+ const time = this.now();
54
+ const metadata = options.metadata || current?.metadata ? { ...current?.metadata, ...structuredClone(options.metadata) } : undefined;
55
+ this.write({
56
+ id,
57
+ messages: [...(current?.messages ?? []), ...structuredClone(messages)],
58
+ ...(metadata ? { metadata } : {}),
59
+ version: version + 1,
60
+ createdAt: current?.createdAt ?? time,
61
+ updatedAt: time,
62
+ });
63
+ return { version: version + 1 };
64
+ }
65
+ async updateMessage(id, message) {
66
+ const current = this.read(id);
67
+ const index = current?.messages.findIndex((m) => m.id === message.id) ?? -1;
68
+ if (!current || index === -1)
69
+ return false;
70
+ const messages = [...current.messages];
71
+ messages[index] = structuredClone(message);
72
+ this.write({ ...current, messages, version: (current.version ?? 0) + 1, updatedAt: this.now() });
73
+ return true;
74
+ }
75
+ async removeMessage(id, messageId) {
76
+ const current = this.read(id);
77
+ if (!current?.messages.some((m) => m.id === messageId))
78
+ return false;
79
+ this.write({
80
+ ...current,
81
+ messages: current.messages.filter((m) => m.id !== messageId),
82
+ version: (current.version ?? 0) + 1,
83
+ updatedAt: this.now(),
84
+ });
85
+ return true;
86
+ }
87
+ async list(query) {
88
+ const all = [...this.records.values()]
89
+ .filter((c) => !this.expired(c))
90
+ .filter((c) => query.updatedAfter === undefined || c.updatedAt > query.updatedAfter)
91
+ .sort((a, b) => b.updatedAt - a.updatedAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
92
+ let start = 0;
93
+ if (query.cursor) {
94
+ const index = all.findIndex((c) => cursorOf(c) === query.cursor);
95
+ start = index === -1 ? all.length : index + 1;
96
+ }
97
+ const page = all.slice(start, start + query.limit);
98
+ const items = page.map((c) => ({
99
+ id: c.id,
100
+ createdAt: c.createdAt,
101
+ updatedAt: c.updatedAt,
102
+ messageCount: c.messages.length,
103
+ ...(c.metadata ? { metadata: structuredClone(c.metadata) } : {}),
104
+ }));
105
+ const last = page.at(-1);
106
+ return start + page.length < all.length && last ? { items, cursor: cursorOf(last) } : { items };
107
+ }
108
+ async purge(before) {
109
+ let removed = 0;
110
+ for (const [id, conversation] of this.records) {
111
+ if (conversation.updatedAt < before) {
112
+ this.records.delete(id);
113
+ removed++;
114
+ }
115
+ }
116
+ return removed;
117
+ }
118
+ /** Returns the live record (not a copy) and marks it as recently used. */
119
+ read(id) {
120
+ const record = this.records.get(id);
121
+ if (!record)
122
+ return undefined;
123
+ if (this.expired(record)) {
124
+ this.records.delete(id);
125
+ return undefined;
126
+ }
127
+ this.records.delete(id);
128
+ this.records.set(id, record);
129
+ return record;
130
+ }
131
+ write(conversation) {
132
+ this.records.delete(conversation.id);
133
+ this.records.set(conversation.id, conversation);
134
+ while (this.records.size > this.maxConversations) {
135
+ const oldest = this.records.keys().next().value;
136
+ if (oldest === undefined)
137
+ break;
138
+ this.records.delete(oldest);
139
+ }
140
+ }
141
+ expired(conversation) {
142
+ return this.now() - conversation.updatedAt > this.ttlMs;
143
+ }
144
+ }
145
+ function cursorOf(conversation) {
146
+ return `${conversation.updatedAt}:${conversation.id}`;
147
+ }
148
+ /** A map whose entries expire, swept when it reaches its cap. */
149
+ class ExpiringMap {
150
+ maxKeys;
151
+ now;
152
+ entries = new Map();
153
+ constructor(maxKeys, now) {
154
+ this.maxKeys = maxKeys;
155
+ this.now = now;
156
+ }
157
+ get(key) {
158
+ const entry = this.entries.get(key);
159
+ if (entry && entry.expiresAt <= this.now()) {
160
+ this.entries.delete(key);
161
+ return undefined;
162
+ }
163
+ return entry;
164
+ }
165
+ set(key, entry) {
166
+ this.entries.delete(key);
167
+ this.entries.set(key, entry);
168
+ if (this.entries.size <= this.maxKeys)
169
+ return;
170
+ const time = this.now();
171
+ for (const [k, e] of this.entries) {
172
+ if (e.expiresAt <= time)
173
+ this.entries.delete(k);
174
+ }
175
+ while (this.entries.size > this.maxKeys) {
176
+ const oldest = this.entries.keys().next().value;
177
+ if (oldest === undefined)
178
+ break;
179
+ this.entries.delete(oldest);
180
+ }
181
+ }
182
+ delete(key) {
183
+ this.entries.delete(key);
184
+ }
185
+ }
186
+ class MemoryCounterStore {
187
+ now;
188
+ counters;
189
+ constructor(maxKeys, now) {
190
+ this.now = now;
191
+ this.counters = new ExpiringMap(maxKeys, now);
192
+ }
193
+ async increment(key, by, ttlMs) {
194
+ const current = this.counters.get(key);
195
+ const entry = current
196
+ ? { value: current.value + by, expiresAt: current.expiresAt }
197
+ : { value: by, expiresAt: this.now() + ttlMs };
198
+ this.counters.set(key, entry);
199
+ return { ...entry };
200
+ }
201
+ async get(key) {
202
+ const entry = this.counters.get(key);
203
+ return entry ? { ...entry } : null;
204
+ }
205
+ }
206
+ const NO_EXPIRY = Number.POSITIVE_INFINITY;
207
+ class MemoryKeyValueStore {
208
+ now;
209
+ values;
210
+ constructor(maxKeys, now) {
211
+ this.now = now;
212
+ this.values = new ExpiringMap(maxKeys, now);
213
+ }
214
+ async get(key) {
215
+ const entry = this.values.get(key);
216
+ return entry ? structuredClone(entry.value) : null;
217
+ }
218
+ async set(key, value, options = {}) {
219
+ this.values.set(key, { value: structuredClone(value), expiresAt: this.expiry(options.ttlMs) });
220
+ }
221
+ async setIfAbsent(key, value, options = {}) {
222
+ if (this.values.get(key))
223
+ return false;
224
+ this.values.set(key, { value: structuredClone(value), expiresAt: this.expiry(options.ttlMs) });
225
+ return true;
226
+ }
227
+ async delete(key) {
228
+ this.values.delete(key);
229
+ }
230
+ expiry(ttlMs) {
231
+ return ttlMs === undefined ? NO_EXPIRY : this.now() + ttlMs;
232
+ }
233
+ }
234
+ class MemoryEventSink {
235
+ maxEvents;
236
+ events = [];
237
+ constructor(maxEvents) {
238
+ this.maxEvents = maxEvents;
239
+ }
240
+ async write(events) {
241
+ this.events.push(...structuredClone(events));
242
+ if (this.events.length > this.maxEvents) {
243
+ this.events = this.events.slice(-this.maxEvents);
244
+ }
245
+ }
246
+ async query(query) {
247
+ const size = query.groupBy === 'hour' ? HOUR_MS : DAY_MS;
248
+ const counts = new Map();
249
+ for (const event of this.events) {
250
+ if (event.timestamp < query.from || event.timestamp >= query.to)
251
+ continue;
252
+ if (query.type && event.type !== query.type)
253
+ continue;
254
+ const bucket = Math.floor(event.timestamp / size) * size;
255
+ const key = `${bucket}:${event.type}`;
256
+ const count = counts.get(key);
257
+ if (count)
258
+ count.count++;
259
+ else
260
+ counts.set(key, { bucket, type: event.type, count: 1 });
261
+ }
262
+ return [...counts.values()].sort((a, b) => a.bucket - b.bucket || (a.type < b.type ? -1 : 1));
263
+ }
264
+ }
265
+ //# sourceMappingURL=memory.js.map