@jini-ai/chat 0.3.11 → 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 (205) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +152 -6
  3. package/dist/core/ag-ui/entry.d.ts +53 -0
  4. package/dist/core/ag-ui/entry.js +135 -0
  5. package/dist/core/agentic/chat-capabilities.d.ts +1 -1
  6. package/dist/core/agentic/index.d.ts +1 -1
  7. package/dist/core/agentic/index.js +1 -1
  8. package/dist/core/assistant-content.d.ts +6 -2
  9. package/dist/core/assistant-content.js +2 -2
  10. package/dist/core/compact-events.d.ts +3 -1
  11. package/dist/core/compact-events.js +1 -1
  12. package/dist/core/events.d.ts +1 -1
  13. package/dist/core/events.js +1 -1
  14. package/dist/core/index.d.ts +3 -3
  15. package/dist/core/index.js +3 -3
  16. package/dist/core/messages.d.ts +4 -2
  17. package/dist/core/messages.js +2 -2
  18. package/dist/core/partial-json.d.ts +6 -2
  19. package/dist/core/partial-json.js +3 -3
  20. package/dist/core/persistence/index.d.ts +1 -1
  21. package/dist/core/persistence/index.js +1 -1
  22. package/dist/core/persistence/ports.d.ts +2 -123
  23. package/dist/core/persistence/title.d.ts +3 -1
  24. package/dist/core/persistence/title.js +1 -1
  25. package/dist/core/question-form/format.d.ts +16 -7
  26. package/dist/core/question-form/format.js +3 -3
  27. package/dist/core/question-form/normalize.d.ts +7 -2
  28. package/dist/core/question-form/normalize.js +5 -5
  29. package/dist/core/question-form/scan.d.ts +18 -6
  30. package/dist/core/question-form/scan.js +14 -14
  31. package/dist/core/run-activity.d.ts +16 -4
  32. package/dist/core/run-activity.js +11 -11
  33. package/dist/core/run-events/entry.d.ts +121 -0
  34. package/dist/core/run-events/entry.js +290 -0
  35. package/dist/core/todos.d.ts +20 -8
  36. package/dist/core/todos.js +11 -11
  37. package/dist/core/tool-events.d.ts +3 -1
  38. package/dist/core/tool-events.js +1 -1
  39. package/dist/core/tool-output.d.ts +3 -1
  40. package/dist/core/tool-output.js +1 -1
  41. package/dist/core/tools.d.ts +14 -3
  42. package/dist/core/tools.js +3 -3
  43. package/dist/core/transcript.d.ts +13 -5
  44. package/dist/core/transcript.js +5 -5
  45. package/dist/core/util/manifest.d.ts +9 -3
  46. package/dist/core/util/manifest.js +3 -3
  47. package/dist/core/util/markdown-context.d.ts +11 -3
  48. package/dist/core/util/markdown-context.js +3 -3
  49. package/dist/core/util/markup-attributes.d.ts +3 -1
  50. package/dist/core/util/markup-attributes.js +1 -1
  51. package/dist/core/util/parser.d.ts +9 -5
  52. package/dist/core/util/parser.js +13 -13
  53. package/dist/core/util/recover.d.ts +6 -2
  54. package/dist/core/util/recover.js +6 -6
  55. package/dist/core/util/strip.d.ts +19 -5
  56. package/dist/core/util/strip.js +17 -17
  57. package/dist/core/util/validate.d.ts +3 -1
  58. package/dist/core/util/validate.js +1 -1
  59. package/dist/embed-widget/browser/page-actions.d.ts +121 -0
  60. package/dist/embed-widget/browser/page-actions.js +113 -0
  61. package/dist/embed-widget/browser/session-store.d.ts +44 -0
  62. package/dist/embed-widget/browser/session-store.js +103 -0
  63. package/dist/embed-widget/react/embed.d.ts +45 -0
  64. package/dist/embed-widget/react/embed.js +87 -0
  65. package/dist/embed-widget/react/header.d.ts +21 -0
  66. package/dist/embed-widget/react/header.js +18 -0
  67. package/dist/embed-widget/transports/fetch-sse.d.ts +75 -0
  68. package/dist/embed-widget/transports/fetch-sse.js +175 -0
  69. package/dist/react/agent-bridge/frontend-session-bridge.d.ts +15 -3
  70. package/dist/react/agent-bridge/frontend-session-bridge.js +13 -6
  71. package/dist/react/artifact-types.d.ts +3 -3
  72. package/dist/react/artifact-types.js +3 -3
  73. package/dist/react/components/A2uiSurfaceCard.js +34 -13
  74. package/dist/react/components/AttachmentPreviewModal.hooks.d.ts +7 -2
  75. package/dist/react/components/AttachmentPreviewModal.hooks.js +5 -5
  76. package/dist/react/components/AttachmentPreviewModal.js +2 -2
  77. package/dist/react/components/AttachmentTray.d.ts +3 -1
  78. package/dist/react/components/AttachmentTray.js +2 -2
  79. package/dist/react/components/ChatFab.js +1 -1
  80. package/dist/react/components/Composer.js +9 -9
  81. package/dist/react/components/ConversationList.js +1 -1
  82. package/dist/react/components/McpUiSurfaceCard.d.ts +4 -2
  83. package/dist/react/components/McpUiSurfaceCard.js +5 -5
  84. package/dist/react/components/MessageList.js +1 -1
  85. package/dist/react/components/MessageRow.js +9 -9
  86. package/dist/react/components/QuestionForm.js +5 -5
  87. package/dist/react/components/ToolCard.js +9 -9
  88. package/dist/react/components/composer-discovery.d.ts +29 -8
  89. package/dist/react/components/composer-discovery.js +10 -10
  90. package/dist/react/ext-event-renderer-registry.d.ts +15 -5
  91. package/dist/react/ext-event-renderer-registry.js +4 -4
  92. package/dist/react/features/chat-pane/components/AgentRuntimePicker.d.ts +3 -1
  93. package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +3 -3
  94. package/dist/react/features/chat-pane/components/ChatPane.js +43 -43
  95. package/dist/react/features/chat-pane/create-daemon-attachment-uploader.d.ts +4 -1
  96. package/dist/react/features/chat-pane/create-daemon-attachment-uploader.js +5 -35
  97. package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.d.ts +5 -2
  98. package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js +1 -1
  99. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.d.ts +38 -10
  100. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js +15 -15
  101. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +31 -31
  102. package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.d.ts +3 -1
  103. package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.js +3 -3
  104. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.d.ts +4 -1
  105. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.js +1 -1
  106. package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +30 -30
  107. package/dist/react/features/chat-pane/hooks/useChatPaneWorkingDirectory.hooks.js +57 -57
  108. package/dist/react/features/chat-pane/rules.d.ts +13 -4
  109. package/dist/react/features/chat-pane/rules.js +5 -5
  110. package/dist/react/features/chat-pane/types.d.ts +1 -1
  111. package/dist/react/hooks/attachment-preview-cache.d.ts +7 -2
  112. package/dist/react/hooks/attachment-preview-cache.js +2 -2
  113. package/dist/react/hooks/composer-draft-cache.d.ts +17 -5
  114. package/dist/react/hooks/composer-draft-cache.js +6 -6
  115. package/dist/react/hooks/useArtifactStream.d.ts +5 -1
  116. package/dist/react/hooks/useArtifactStream.js +3 -3
  117. package/dist/react/hooks/useChatFabDrag.d.ts +9 -4
  118. package/dist/react/hooks/useChatFabDrag.js +4 -4
  119. package/dist/react/hooks/useComposer.js +8 -8
  120. package/dist/react/hooks/useConversation.js +56 -4
  121. package/dist/react/hooks/useExtEventGroups.d.ts +5 -1
  122. package/dist/react/hooks/useExtEventGroups.js +1 -1
  123. package/dist/react/hooks/useLatestOperation.d.ts +8 -3
  124. package/dist/react/hooks/useLatestOperation.js +4 -4
  125. package/dist/react/hooks/usePinnedTodos.d.ts +3 -1
  126. package/dist/react/hooks/usePinnedTodos.js +3 -3
  127. package/dist/react/hooks/useQuestionForms.d.ts +7 -2
  128. package/dist/react/hooks/useQuestionForms.js +7 -7
  129. package/dist/react/hooks/useRunActivity.d.ts +6 -1
  130. package/dist/react/hooks/useRunActivity.js +3 -3
  131. package/dist/react/hooks/useRunStream.d.ts +3 -1
  132. package/dist/react/hooks/useRunStream.js +1 -1
  133. package/dist/react/hooks/useToolTimeline.d.ts +3 -1
  134. package/dist/react/hooks/useToolTimeline.js +3 -3
  135. package/dist/react/index.d.ts +4 -4
  136. package/dist/react/index.js +6 -6
  137. package/dist/react/message-blocks.d.ts +10 -4
  138. package/dist/react/message-blocks.js +2 -2
  139. package/dist/react/styles/reference.css +3 -3
  140. package/dist/react/tool-renderer-registry.d.ts +8 -3
  141. package/dist/react/tool-renderer-registry.js +3 -3
  142. package/dist/react/util/defined-props.d.ts +3 -1
  143. package/dist/react/util/defined-props.js +1 -1
  144. package/dist/server/run-finalizer/entry.d.ts +47 -0
  145. package/dist/server/run-finalizer/entry.js +156 -0
  146. package/dist/server/run-finalizer/ports.d.ts +52 -0
  147. package/dist/store/contracts/index.d.ts +5 -0
  148. package/dist/store/contracts/index.js +5 -0
  149. package/dist/store/errors.d.ts +12 -0
  150. package/dist/store/errors.js +18 -0
  151. package/dist/store/index.d.ts +4 -0
  152. package/dist/store/index.js +2 -0
  153. package/dist/store/legacy/index.d.ts +3 -0
  154. package/dist/store/legacy/index.js +2 -0
  155. package/dist/store/legacy/sqlite/conversations.d.ts +129 -0
  156. package/dist/store/legacy/sqlite/conversations.js +274 -0
  157. package/dist/store/legacy/sqlite/index.d.ts +6 -0
  158. package/dist/store/legacy/sqlite/index.js +5 -0
  159. package/dist/store/legacy/sqlite/messages.d.ts +156 -0
  160. package/dist/store/legacy/sqlite/messages.js +246 -0
  161. package/dist/store/legacy/sqlite/schema.d.ts +3 -0
  162. package/dist/store/legacy/sqlite/schema.js +40 -0
  163. package/dist/store/pglite/index.d.ts +21 -0
  164. package/dist/store/pglite/index.js +17 -0
  165. package/dist/store/ports.d.ts +121 -0
  166. package/dist/store/ports.js +2 -0
  167. package/dist/store/postgres/index.d.ts +21 -0
  168. package/dist/store/postgres/index.js +17 -0
  169. package/dist/store/sql/boundary.d.ts +28 -0
  170. package/dist/store/sql/boundary.js +32 -0
  171. package/dist/store/sql/cursor.d.ts +24 -0
  172. package/dist/store/sql/cursor.js +49 -0
  173. package/dist/store/sql/maintenance.d.ts +9 -0
  174. package/dist/store/sql/maintenance.js +16 -0
  175. package/dist/store/sql/store.d.ts +22 -0
  176. package/dist/store/sql/store.js +340 -0
  177. package/dist/store/sql/tables.d.ts +34 -0
  178. package/dist/store/sql/tables.js +2 -0
  179. package/dist/store/sqlite/index.d.ts +36 -0
  180. package/dist/store/sqlite/index.js +29 -0
  181. package/dist/store/sqlite/schema.d.ts +49 -0
  182. package/dist/store/sqlite/schema.js +80 -0
  183. package/package.json +211 -14
  184. package/dist/react/features/model-picker/components/CredentialStatusBadge.d.ts +0 -7
  185. package/dist/react/features/model-picker/components/CredentialStatusBadge.js +0 -20
  186. package/dist/react/features/model-picker/components/ModelPicker.d.ts +0 -9
  187. package/dist/react/features/model-picker/components/ModelPicker.js +0 -34
  188. package/dist/react/features/model-picker/constants.d.ts +0 -6
  189. package/dist/react/features/model-picker/constants.js +0 -9
  190. package/dist/react/features/model-picker/dependencies.d.ts +0 -11
  191. package/dist/react/features/model-picker/dependencies.js +0 -10
  192. package/dist/react/features/model-picker/hooks/useModelPicker.hooks.d.ts +0 -27
  193. package/dist/react/features/model-picker/hooks/useModelPicker.hooks.js +0 -62
  194. package/dist/react/features/model-picker/index.d.ts +0 -32
  195. package/dist/react/features/model-picker/index.js +0 -29
  196. package/dist/react/features/model-picker/ports.d.ts +0 -11
  197. package/dist/react/features/model-picker/rules.d.ts +0 -26
  198. package/dist/react/features/model-picker/rules.js +0 -84
  199. package/dist/react/features/model-picker/types.d.ts +0 -38
  200. package/dist/react/features/model-picker/types.js +0 -2
  201. package/dist/react/poc-mcp-ui-official/PocMcpUiCard.d.ts +0 -9
  202. package/dist/react/poc-mcp-ui-official/PocMcpUiCard.js +0 -29
  203. package/dist/react/poc-mcp-ui-official/create-poc-resource.d.ts +0 -30
  204. package/dist/react/poc-mcp-ui-official/create-poc-resource.js +0 -44
  205. /package/dist/{react/features/model-picker → server/run-finalizer}/ports.js +0 -0
@@ -0,0 +1,6 @@
1
+ /** Sync legacy conversations/messages over a borrowed host handle. */
2
+ export * from './conversations.js';
3
+ export * from './messages.js';
4
+ export { LEGACY_CHAT_DDL } from './schema.js';
5
+ export type { ChatSessionMode } from '../index.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,5 @@
1
+ /** Sync legacy conversations/messages over a borrowed host handle. */
2
+ export * from './conversations.js';
3
+ export * from './messages.js';
4
+ export { LEGACY_CHAT_DDL } from './schema.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @module db/messages/messages
3
+ * Chat message CRUD, streamed-event append, and telemetry finalization. Imports only the
4
+ * core/ kernel (SqliteDb, DbRow, ChatSessionMode, parseJsonOrUndef).
5
+ *
6
+ * NOTE (neutral port): drops six OD-product JSON columns with no generic engine equivalent —
7
+ * `comment_attachments_json` (the preview-comments/canvas-annotation feature, explicitly out of
8
+ * scope for this port), `produced_files_json` and `trace_object_files_json` (design-canvas file
9
+ * output tracking), `feedback_json` (Critique Theater's scoring feedback), `pre_turn_file_names_json`
10
+ * (design-canvas per-turn file context), and `applied_plugin_snapshot_json` (OD's plugin host). The
11
+ * already-ported `schema/migrate.ts` messages table was pre-trimmed to match; see that file and
12
+ * `source-map.md`'s Design Decisions for the same drop list.
13
+ */
14
+ import type { SqliteDb } from '@jini-ai/db/sqlite';
15
+ import type { DbRow } from '@jini-ai/db/core';
16
+ import type { ChatSessionMode } from '../index.js';
17
+ /**
18
+ * Returns all messages for a conversation in ascending position order, with JSON columns parsed.
19
+ * @param db - Open SQLite connection.
20
+ * @param conversationId - Conversation whose messages to fetch.
21
+ * @returns Normalized message objects ready for API serialization.
22
+ */
23
+ export declare function listMessages({ db, conversationId }: {
24
+ db: SqliteDb;
25
+ conversationId: string;
26
+ }): {
27
+ id: any;
28
+ role: any;
29
+ content: any;
30
+ agentId: any;
31
+ agentName: any;
32
+ runId: any;
33
+ runStatus: any;
34
+ lastRunEventId: any;
35
+ events: any;
36
+ attachments: any;
37
+ sessionMode: ChatSessionMode | undefined;
38
+ runContext: any;
39
+ createdAt: any;
40
+ startedAt: any;
41
+ endedAt: any;
42
+ }[];
43
+ /**
44
+ * Thrown by `upsertMessage` when the caller presents a message `id` together with a
45
+ * `conversationId` that does not match the conversation the row already belongs to (`id` is a
46
+ * global primary key, not scoped per-conversation, so this is the only signal available). Rather
47
+ * than silently updating the wrong conversation's message — or silently doing nothing — while
48
+ * still bumping the caller-supplied `conversationId`'s `updated_at`, this is a hard failure: it is
49
+ * always a caller/transport bug (a confused id, or a multi-tenant mixup) and never a legitimate
50
+ * state to paper over.
51
+ */
52
+ export declare class MessageConversationMismatchError extends Error {
53
+ readonly messageId: string;
54
+ readonly actualConversationId: string;
55
+ readonly requestedConversationId: string;
56
+ constructor({ messageId, actualConversationId, requestedConversationId }: {
57
+ messageId: string;
58
+ actualConversationId: string;
59
+ requestedConversationId: string;
60
+ });
61
+ }
62
+ /**
63
+ * Inserts or updates a message row and bumps the parent conversation's `updated_at` for recency sorting.
64
+ * On insert, appends to the end of the conversation's position sequence.
65
+ * On update, preserves `telemetry_finalized_at` once set (one-way latch).
66
+ *
67
+ * The existence check, position calculation, insert-or-update, and conversation `updated_at` bump
68
+ * all run inside one `better-sqlite3` transaction (opened with `BEGIN IMMEDIATE` via `.immediate()`,
69
+ * not the default deferred `BEGIN`) so the whole read-then-write sequence is atomic: a concurrent
70
+ * connection's own `upsertMessage` call for the same conversation cannot read the pre-write
71
+ * `MAX(position)` while this one is still in flight and thereby compute the same next position —
72
+ * `BEGIN IMMEDIATE` acquires the write lock before this transaction's first read, so a concurrent
73
+ * writer blocks (or fails with `SQLITE_BUSY`, per its own busy-timeout policy) until this one
74
+ * commits, rather than interleaving.
75
+ *
76
+ * `id` is the messages table's global primary key (not scoped per-conversation), so a row can only
77
+ * ever belong to one conversation. If the caller-supplied `m.id` already exists under a different
78
+ * `conversationId`, this throws {@link MessageConversationMismatchError} instead of silently
79
+ * updating the wrong conversation's row (or the wrong conversation's `updated_at`).
80
+ *
81
+ * @param db - Open SQLite connection.
82
+ * @param conversationId - Owning conversation id.
83
+ * @param m - Message fields; JSON sub-fields are stringified automatically.
84
+ * @returns The normalized, re-fetched message row, or null if the row vanished between write and read.
85
+ * @throws {MessageConversationMismatchError} if `m.id` already exists under a different conversation.
86
+ */
87
+ export declare function upsertMessage({ db, conversationId, m }: {
88
+ db: SqliteDb;
89
+ conversationId: string;
90
+ m: DbRow;
91
+ }): {
92
+ id: any;
93
+ role: any;
94
+ content: any;
95
+ agentId: any;
96
+ agentName: any;
97
+ runId: any;
98
+ runStatus: any;
99
+ lastRunEventId: any;
100
+ events: any;
101
+ attachments: any;
102
+ sessionMode: ChatSessionMode | undefined;
103
+ runContext: any;
104
+ createdAt: any;
105
+ startedAt: any;
106
+ endedAt: any;
107
+ } | null;
108
+ /**
109
+ * Reads whether telemetry has been finalized for a message without loading the full row.
110
+ * Used to decide whether to enqueue a finalization job.
111
+ * @returns `{ exists: false }` when the message is not found; otherwise `{ exists: true, finalizedAt }`.
112
+ */
113
+ export declare function getMessageTelemetryFinalizationState({ db, messageId }: {
114
+ db: SqliteDb;
115
+ messageId: string;
116
+ }): {
117
+ exists: boolean;
118
+ finalizedAt: number | null;
119
+ };
120
+ /**
121
+ * Appends a `{ kind: 'status', label, detail? }` event to the message's events array.
122
+ * Deduplicates consecutive identical status events so the UI does not stutter on repeated flushes.
123
+ * @param db - Open SQLite connection.
124
+ * @param messageId - Target message id.
125
+ * @param event - Must carry a non-empty `label`; `detail` is optional.
126
+ * @returns The updated events array, or null if the message does not exist or label is empty.
127
+ */
128
+ export declare function appendMessageStatusEvent({ db, messageId, event }: {
129
+ db: SqliteDb;
130
+ messageId: string;
131
+ event: DbRow;
132
+ }): any[] | null;
133
+ /**
134
+ * Appends an arbitrary agent event to the message's events array, also streaming `text` deltas
135
+ * into the `content` column so the message text grows incrementally during a run.
136
+ * Deduplicates byte-identical consecutive events.
137
+ * @param db - Open SQLite connection.
138
+ * @param messageId - Target message id.
139
+ * @param event - Must carry a non-empty `kind`; `text` field is appended to content when kind is 'text'.
140
+ * @returns The updated events array, or null if the message does not exist or kind is empty.
141
+ */
142
+ export declare function appendMessageAgentEvent({ db, messageId, event }: {
143
+ db: SqliteDb;
144
+ messageId: string;
145
+ event: DbRow;
146
+ }): any[] | null;
147
+ /**
148
+ * Hard-deletes a single message row by id.
149
+ * @param db - Open SQLite connection.
150
+ * @param id - Message id to remove.
151
+ */
152
+ export declare function deleteMessage({ db, id }: {
153
+ db: SqliteDb;
154
+ id: string;
155
+ }): void;
156
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1,246 @@
1
+ import { parseJsonOrUndef } from '@jini-ai/db/core';
2
+ /** @internal Column alias list used by both `listMessages` and `upsertMessage`'s re-fetch. */
3
+ const MESSAGE_COLS = `id, role, content, agent_id AS agentId, agent_name AS agentName,
4
+ run_id AS runId, run_status AS runStatus,
5
+ last_run_event_id AS lastRunEventId,
6
+ events_json AS eventsJson,
7
+ attachments_json AS attachmentsJson,
8
+ session_mode AS sessionMode,
9
+ run_context_json AS runContextJson,
10
+ created_at AS createdAt, started_at AS startedAt, ended_at AS endedAt,
11
+ position`;
12
+ /**
13
+ * Returns all messages for a conversation in ascending position order, with JSON columns parsed.
14
+ * @param db - Open SQLite connection.
15
+ * @param conversationId - Conversation whose messages to fetch.
16
+ * @returns Normalized message objects ready for API serialization.
17
+ */
18
+ export function listMessages({ db, conversationId }) {
19
+ return db
20
+ .prepare(`SELECT ${MESSAGE_COLS}
21
+ FROM messages
22
+ WHERE conversation_id = ?
23
+ ORDER BY position ASC`)
24
+ .all(conversationId)
25
+ .map(normalizeMessage);
26
+ }
27
+ /**
28
+ * Thrown by `upsertMessage` when the caller presents a message `id` together with a
29
+ * `conversationId` that does not match the conversation the row already belongs to (`id` is a
30
+ * global primary key, not scoped per-conversation, so this is the only signal available). Rather
31
+ * than silently updating the wrong conversation's message — or silently doing nothing — while
32
+ * still bumping the caller-supplied `conversationId`'s `updated_at`, this is a hard failure: it is
33
+ * always a caller/transport bug (a confused id, or a multi-tenant mixup) and never a legitimate
34
+ * state to paper over.
35
+ */
36
+ export class MessageConversationMismatchError extends Error {
37
+ messageId;
38
+ actualConversationId;
39
+ requestedConversationId;
40
+ constructor({ messageId, actualConversationId, requestedConversationId }) {
41
+ super(`upsertMessage: message id ${JSON.stringify(messageId)} belongs to a different conversation ` +
42
+ `(${JSON.stringify(actualConversationId)}), not ${JSON.stringify(requestedConversationId)}`);
43
+ this.name = 'MessageConversationMismatchError';
44
+ this.messageId = messageId;
45
+ this.actualConversationId = actualConversationId;
46
+ this.requestedConversationId = requestedConversationId;
47
+ }
48
+ }
49
+ /**
50
+ * Inserts or updates a message row and bumps the parent conversation's `updated_at` for recency sorting.
51
+ * On insert, appends to the end of the conversation's position sequence.
52
+ * On update, preserves `telemetry_finalized_at` once set (one-way latch).
53
+ *
54
+ * The existence check, position calculation, insert-or-update, and conversation `updated_at` bump
55
+ * all run inside one `better-sqlite3` transaction (opened with `BEGIN IMMEDIATE` via `.immediate()`,
56
+ * not the default deferred `BEGIN`) so the whole read-then-write sequence is atomic: a concurrent
57
+ * connection's own `upsertMessage` call for the same conversation cannot read the pre-write
58
+ * `MAX(position)` while this one is still in flight and thereby compute the same next position —
59
+ * `BEGIN IMMEDIATE` acquires the write lock before this transaction's first read, so a concurrent
60
+ * writer blocks (or fails with `SQLITE_BUSY`, per its own busy-timeout policy) until this one
61
+ * commits, rather than interleaving.
62
+ *
63
+ * `id` is the messages table's global primary key (not scoped per-conversation), so a row can only
64
+ * ever belong to one conversation. If the caller-supplied `m.id` already exists under a different
65
+ * `conversationId`, this throws {@link MessageConversationMismatchError} instead of silently
66
+ * updating the wrong conversation's row (or the wrong conversation's `updated_at`).
67
+ *
68
+ * @param db - Open SQLite connection.
69
+ * @param conversationId - Owning conversation id.
70
+ * @param m - Message fields; JSON sub-fields are stringified automatically.
71
+ * @returns The normalized, re-fetched message row, or null if the row vanished between write and read.
72
+ * @throws {MessageConversationMismatchError} if `m.id` already exists under a different conversation.
73
+ */
74
+ export function upsertMessage({ db, conversationId, m }) {
75
+ const runUpsert = db.transaction(() => {
76
+ const existing = db
77
+ .prepare(`SELECT conversation_id AS conversationId, position FROM messages WHERE id = ?`)
78
+ .get(m.id);
79
+ if (existing && existing.conversationId !== conversationId) {
80
+ throw new MessageConversationMismatchError({ messageId: m.id, actualConversationId: existing.conversationId, requestedConversationId: conversationId });
81
+ }
82
+ const now = Date.now();
83
+ if (existing) {
84
+ db.prepare(`UPDATE messages
85
+ SET role = ?, content = ?, agent_id = ?, agent_name = ?,
86
+ run_id = ?, run_status = ?, last_run_event_id = ?,
87
+ events_json = ?, attachments_json = ?,
88
+ session_mode = ?, run_context_json = ?,
89
+ telemetry_finalized_at = CASE
90
+ WHEN ? THEN COALESCE(telemetry_finalized_at, ?)
91
+ ELSE telemetry_finalized_at
92
+ END,
93
+ started_at = ?, ended_at = ?
94
+ WHERE id = ? AND conversation_id = ?`).run(m.role, m.content, m.agentId ?? null, m.agentName ?? null, m.runId ?? null, m.runStatus ?? null, m.lastRunEventId ?? null, m.events ? JSON.stringify(m.events) : null, m.attachments ? JSON.stringify(m.attachments) : null, normalizeMessageSessionModeForStorage(m.sessionMode), m.runContext ? JSON.stringify(m.runContext) : null, m.telemetryFinalized === true ? 1 : 0, now, m.startedAt ?? null, m.endedAt ?? null, m.id, conversationId);
95
+ }
96
+ else {
97
+ const max = db
98
+ .prepare(`SELECT COALESCE(MAX(position), -1) AS m FROM messages WHERE conversation_id = ?`)
99
+ .get(conversationId);
100
+ const position = (max?.m ?? -1) + 1;
101
+ const createdAt = typeof m.createdAt === 'number' && Number.isFinite(m.createdAt)
102
+ ? m.createdAt
103
+ : now;
104
+ // 18 values: id, conversation_id, role, content, agent_id, agent_name,
105
+ // run_id, run_status, last_run_event_id, events_json, attachments_json,
106
+ // session_mode, run_context_json, telemetry_finalized_at, started_at,
107
+ // ended_at, position, created_at.
108
+ db.prepare(`INSERT INTO messages
109
+ (id, conversation_id, role, content, agent_id, agent_name,
110
+ run_id, run_status, last_run_event_id, events_json,
111
+ attachments_json, session_mode, run_context_json,
112
+ telemetry_finalized_at, started_at, ended_at, position, created_at)
113
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(m.id, conversationId, m.role, m.content, m.agentId ?? null, m.agentName ?? null, m.runId ?? null, m.runStatus ?? null, m.lastRunEventId ?? null, m.events ? JSON.stringify(m.events) : null, m.attachments ? JSON.stringify(m.attachments) : null, normalizeMessageSessionModeForStorage(m.sessionMode), m.runContext ? JSON.stringify(m.runContext) : null, m.telemetryFinalized === true ? now : null, m.startedAt ?? null, m.endedAt ?? null, position, createdAt);
114
+ }
115
+ // Bump conversation activity so recency-sorted listings (e.g. `listConversations`) reflect this write.
116
+ db.prepare(`UPDATE conversations SET updated_at = ? WHERE id = ?`).run(now, conversationId);
117
+ const row = db
118
+ .prepare(`SELECT ${MESSAGE_COLS} FROM messages WHERE id = ? AND conversation_id = ?`)
119
+ .get(m.id, conversationId);
120
+ return row ? normalizeMessage(row) : null;
121
+ });
122
+ return runUpsert.immediate();
123
+ }
124
+ /**
125
+ * Reads whether telemetry has been finalized for a message without loading the full row.
126
+ * Used to decide whether to enqueue a finalization job.
127
+ * @returns `{ exists: false }` when the message is not found; otherwise `{ exists: true, finalizedAt }`.
128
+ */
129
+ export function getMessageTelemetryFinalizationState({ db, messageId }) {
130
+ const row = db
131
+ .prepare(`SELECT telemetry_finalized_at AS telemetryFinalizedAt
132
+ FROM messages
133
+ WHERE id = ?`)
134
+ .get(messageId);
135
+ if (!row) {
136
+ return {
137
+ exists: false,
138
+ finalizedAt: null,
139
+ };
140
+ }
141
+ return {
142
+ exists: true,
143
+ finalizedAt: typeof row.telemetryFinalizedAt === 'number' ? row.telemetryFinalizedAt : null,
144
+ };
145
+ }
146
+ /**
147
+ * Appends a `{ kind: 'status', label, detail? }` event to the message's events array.
148
+ * Deduplicates consecutive identical status events so the UI does not stutter on repeated flushes.
149
+ * @param db - Open SQLite connection.
150
+ * @param messageId - Target message id.
151
+ * @param event - Must carry a non-empty `label`; `detail` is optional.
152
+ * @returns The updated events array, or null if the message does not exist or label is empty.
153
+ */
154
+ export function appendMessageStatusEvent({ db, messageId, event }) {
155
+ const label = typeof event?.label === 'string' ? event.label.trim() : '';
156
+ const detail = typeof event?.detail === 'string' ? event.detail.trim() : '';
157
+ if (!label)
158
+ return null;
159
+ const row = db
160
+ .prepare(`SELECT events_json AS eventsJson FROM messages WHERE id = ?`)
161
+ .get(messageId);
162
+ if (!row)
163
+ return null;
164
+ const parsed = parseJsonOrUndef(row.eventsJson);
165
+ const events = Array.isArray(parsed) ? parsed : [];
166
+ const last = events[events.length - 1];
167
+ if (last?.kind === 'status' && last.label === label && (last.detail ?? '') === detail) {
168
+ return events;
169
+ }
170
+ const nextEvent = detail
171
+ ? { kind: 'status', label, detail }
172
+ : { kind: 'status', label };
173
+ const next = [...events, nextEvent];
174
+ db.prepare(`UPDATE messages SET events_json = ? WHERE id = ?`)
175
+ .run(JSON.stringify(next), messageId);
176
+ return next;
177
+ }
178
+ /**
179
+ * Appends an arbitrary agent event to the message's events array, also streaming `text` deltas
180
+ * into the `content` column so the message text grows incrementally during a run.
181
+ * Deduplicates byte-identical consecutive events.
182
+ * @param db - Open SQLite connection.
183
+ * @param messageId - Target message id.
184
+ * @param event - Must carry a non-empty `kind`; `text` field is appended to content when kind is 'text'.
185
+ * @returns The updated events array, or null if the message does not exist or kind is empty.
186
+ */
187
+ export function appendMessageAgentEvent({ db, messageId, event }) {
188
+ if (!event || typeof event !== 'object')
189
+ return null;
190
+ const kind = typeof event.kind === 'string' ? event.kind : '';
191
+ if (!kind)
192
+ return null;
193
+ const row = db
194
+ .prepare(`SELECT content, events_json AS eventsJson FROM messages WHERE id = ?`)
195
+ .get(messageId);
196
+ if (!row)
197
+ return null;
198
+ const parsed = parseJsonOrUndef(row.eventsJson);
199
+ const events = Array.isArray(parsed) ? parsed : [];
200
+ const last = events[events.length - 1];
201
+ if (last && JSON.stringify(last) === JSON.stringify(event)) {
202
+ return events;
203
+ }
204
+ const next = [...events, event];
205
+ const textDelta = kind === 'text' && typeof event.text === 'string' ? event.text : '';
206
+ db.prepare(`UPDATE messages SET content = COALESCE(content, '') || ?, events_json = ? WHERE id = ?`)
207
+ .run(textDelta, JSON.stringify(next), messageId);
208
+ return next;
209
+ }
210
+ /**
211
+ * Hard-deletes a single message row by id.
212
+ * @param db - Open SQLite connection.
213
+ * @param id - Message id to remove.
214
+ */
215
+ export function deleteMessage({ db, id }) {
216
+ db.prepare(`DELETE FROM messages WHERE id = ?`).run(id);
217
+ }
218
+ /** @internal Converts a raw SQLite row to a plain object with JSON columns parsed and null coerced to undefined. */
219
+ function normalizeMessage(row) {
220
+ return {
221
+ id: row.id,
222
+ role: row.role,
223
+ content: row.content,
224
+ agentId: row.agentId ?? undefined,
225
+ agentName: row.agentName ?? undefined,
226
+ runId: row.runId ?? undefined,
227
+ runStatus: row.runStatus ?? undefined,
228
+ lastRunEventId: row.lastRunEventId ?? undefined,
229
+ events: parseJsonOrUndef(row.eventsJson),
230
+ attachments: parseJsonOrUndef(row.attachmentsJson),
231
+ sessionMode: normalizeMessageSessionMode(row.sessionMode),
232
+ runContext: parseJsonOrUndef(row.runContextJson),
233
+ createdAt: row.createdAt ?? undefined,
234
+ startedAt: row.startedAt ?? undefined,
235
+ endedAt: row.endedAt ?? undefined,
236
+ };
237
+ }
238
+ /** @internal Validates that a stored session_mode value is a known ChatSessionMode; returns undefined for unrecognised values. */
239
+ function normalizeMessageSessionMode(value) {
240
+ return value === 'chat' || value === 'design' || value === 'plan' ? value : undefined;
241
+ }
242
+ /** @internal Same validation as normalizeMessageSessionMode but returns null (SQLite-safe) for unrecognised values. */
243
+ function normalizeMessageSessionModeForStorage(value) {
244
+ return value === 'chat' || value === 'design' || value === 'plan' ? value : null;
245
+ }
246
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1,3 @@
1
+ /** Unchanged legacy local-chat tables; explicitly bootstrapped by server storage. */
2
+ export declare const LEGACY_CHAT_DDL = " CREATE TABLE IF NOT EXISTS conversations (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL,\n title TEXT,\n session_mode TEXT NOT NULL DEFAULT 'design',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_conv_project\n ON conversations(project_id, updated_at DESC);\n\n CREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n conversation_id TEXT NOT NULL,\n role TEXT NOT NULL,\n content TEXT NOT NULL,\n agent_id TEXT,\n agent_name TEXT,\n events_json TEXT,\n attachments_json TEXT,\n run_context_json TEXT,\n session_mode TEXT,\n run_id TEXT,\n run_status TEXT,\n last_run_event_id TEXT,\n telemetry_finalized_at INTEGER,\n started_at INTEGER,\n ended_at INTEGER,\n position INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n FOREIGN KEY(conversation_id) REFERENCES conversations(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_messages_conv\n ON messages(conversation_id, position);\n ";
3
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1,40 @@
1
+ /** Unchanged legacy local-chat tables; explicitly bootstrapped by server storage. */
2
+ export const LEGACY_CHAT_DDL = ` CREATE TABLE IF NOT EXISTS conversations (
3
+ id TEXT PRIMARY KEY,
4
+ project_id TEXT NOT NULL,
5
+ title TEXT,
6
+ session_mode TEXT NOT NULL DEFAULT 'design',
7
+ created_at INTEGER NOT NULL,
8
+ updated_at INTEGER NOT NULL,
9
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
10
+ );
11
+
12
+ CREATE INDEX IF NOT EXISTS idx_conv_project
13
+ ON conversations(project_id, updated_at DESC);
14
+
15
+ CREATE TABLE IF NOT EXISTS messages (
16
+ id TEXT PRIMARY KEY,
17
+ conversation_id TEXT NOT NULL,
18
+ role TEXT NOT NULL,
19
+ content TEXT NOT NULL,
20
+ agent_id TEXT,
21
+ agent_name TEXT,
22
+ events_json TEXT,
23
+ attachments_json TEXT,
24
+ run_context_json TEXT,
25
+ session_mode TEXT,
26
+ run_id TEXT,
27
+ run_status TEXT,
28
+ last_run_event_id TEXT,
29
+ telemetry_finalized_at INTEGER,
30
+ started_at INTEGER,
31
+ ended_at INTEGER,
32
+ position INTEGER NOT NULL,
33
+ created_at INTEGER NOT NULL,
34
+ FOREIGN KEY(conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
35
+ );
36
+
37
+ CREATE INDEX IF NOT EXISTS idx_messages_conv
38
+ ON messages(conversation_id, position);
39
+ `;
40
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,21 @@
1
+ /** Pglite transcript adapter. The host owns the opened, migrated kernel and its lifetime. */
2
+ import type { Clock } from '@jini-ai/core/primitives';
3
+ import type { StorageKernel } from '@jini-ai/db/kernel';
4
+ import type { ChatDatabase } from '../sql/tables.js';
5
+ import type { ChatOwnerScope, ChatStore, ChatHistoryMaintenance } from '../ports.js';
6
+ export type { ChatDatabase, AiChatsTable, AiChatMessagesTable } from '../sql/tables.js';
7
+ /** Borrow an existing kernel, with no configuration, migration or connection lifecycle effects.
8
+ * @example createPgliteChatStore({ kernel, scope }, { clock: { nowMs: () => 42 } })
9
+ * @throws ChatStoreError invalid-input for a mismatched dialect/transport or owner scope.
10
+ */
11
+ export declare function createPgliteChatStore<DB extends ChatDatabase>(required: {
12
+ kernel: StorageKernel<DB>;
13
+ scope: ChatOwnerScope;
14
+ }, optional?: {
15
+ clock?: Clock;
16
+ }): ChatStore;
17
+ /** Privileged bounded expiry maintenance; never hand this collaborator to request handlers. */
18
+ export declare function createPgliteChatMaintenance<DB extends ChatDatabase>(required: {
19
+ kernel: StorageKernel<DB>;
20
+ }): ChatHistoryMaintenance;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { createSqlChatStore } from '../sql/store.js';
2
+ import { createSqlChatMaintenance } from '../sql/maintenance.js';
3
+ import { validateKernel } from '../sql/boundary.js';
4
+ /** Borrow an existing kernel, with no configuration, migration or connection lifecycle effects.
5
+ * @example createPgliteChatStore({ kernel, scope }, { clock: { nowMs: () => 42 } })
6
+ * @throws ChatStoreError invalid-input for a mismatched dialect/transport or owner scope.
7
+ */
8
+ export function createPgliteChatStore(required, optional = {}) {
9
+ validateKernel({ kernel: required.kernel, transports: ['pglite', 'pglite-socket'], dialect: 'postgres' });
10
+ return createSqlChatStore(required, optional);
11
+ }
12
+ /** Privileged bounded expiry maintenance; never hand this collaborator to request handlers. */
13
+ export function createPgliteChatMaintenance(required) {
14
+ validateKernel({ kernel: required.kernel, transports: ['pglite', 'pglite-socket'], dialect: 'postgres' });
15
+ return createSqlChatMaintenance(required);
16
+ }
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,121 @@
1
+ /** Owner-scoped, provider-neutral durable transcripts. See README for paging and composition. */
2
+ import type { ChatMessage } from '../core/messages.js';
3
+ export type ChatOwnerKind = 'user' | 'guest';
4
+ export type ChatTitleSource = 'fallback' | 'generated' | 'manual';
5
+ export interface ChatOwnerScope {
6
+ readonly scopeId: string;
7
+ readonly ownerKind: ChatOwnerKind;
8
+ /** Guest identifier is already hashed by the host. Never a raw bearer token. */
9
+ readonly ownerId: string;
10
+ }
11
+ export interface ChatConversation {
12
+ readonly id: string;
13
+ readonly title: string | null;
14
+ readonly titleSource: ChatTitleSource;
15
+ readonly messageCount: number;
16
+ readonly createdAt: number;
17
+ readonly updatedAt: number;
18
+ /** Epoch milliseconds; absence means retained until explicitly deleted. */
19
+ readonly expiresAt?: number;
20
+ }
21
+ export interface CreateChatConversationInput {
22
+ readonly id: string;
23
+ readonly title?: string | null;
24
+ readonly titleSource?: ChatTitleSource;
25
+ readonly expiresAt?: number;
26
+ }
27
+ /** Versioned opaque continuation, not an offset or a driver cursor object. */
28
+ export type ChatPageCursor = string;
29
+ export interface ChatPageOptions {
30
+ /** Default 50; integer from 1 through 200. */
31
+ readonly limit?: number;
32
+ readonly cursor?: ChatPageCursor;
33
+ }
34
+ export interface ChatPage<T> {
35
+ readonly items: readonly T[];
36
+ /** Absent when this page has no continuation. */
37
+ readonly nextCursor?: ChatPageCursor;
38
+ }
39
+ export interface ChatMessagePageInput {
40
+ readonly conversationId: string;
41
+ }
42
+ export interface ChatSearchInput {
43
+ /** Plain human text, never an FTS expression or SQL fragment. */
44
+ readonly query: string;
45
+ readonly conversationId?: string;
46
+ }
47
+ export interface ChatSearchHit {
48
+ readonly conversationId: string;
49
+ readonly messageId: string;
50
+ readonly excerpt: string;
51
+ /** Relative relevance within this response only, never cross-provider comparable. */
52
+ readonly score?: number;
53
+ }
54
+ /** Optional capability. No implementation of this interface is promised in this move. */
55
+ export interface ChatSearchCapability {
56
+ readonly consistency: 'immediate' | 'eventual';
57
+ search(input: ChatSearchInput, options?: ChatPageOptions): Promise<ChatPage<ChatSearchHit>>;
58
+ }
59
+ /**
60
+ * Every instance is permanently bound to one immutable owner scope.
61
+ * Required identity/payload arguments and optional policy/paging arguments occupy separate bags.
62
+ * Omitted rename source is manual; omitted touch expiry preserves the persisted expiry.
63
+ * @example store.rename({ id, title }, { source: 'generated' }); store.pageConversations({}, { limit: 50 })
64
+ */
65
+ export interface ChatStore {
66
+ /** Compatibility read: all owned conversations, updatedAt descending. */
67
+ list(): Promise<ChatConversation[]>;
68
+ /** Unknown and other-owned IDs both return null. */
69
+ get(required: {
70
+ readonly id: string;
71
+ }, optional?: Record<string, never>): Promise<ChatConversation | null>;
72
+ create(input: CreateChatConversationInput, optional?: Record<string, never>): Promise<ChatConversation>;
73
+ /** A generated title must not overwrite a manual title. Default source: manual. */
74
+ rename(required: {
75
+ readonly id: string;
76
+ readonly title: string;
77
+ }, optional?: {
78
+ readonly source?: ChatTitleSource;
79
+ }): Promise<ChatConversation | null>;
80
+ touch(required: {
81
+ readonly id: string;
82
+ }, optional?: {
83
+ readonly expiresAt?: number;
84
+ }): Promise<void>;
85
+ /** Cascades messages; unknown/other-owned IDs are no-ops. */
86
+ delete(required: {
87
+ readonly id: string;
88
+ }, optional?: Record<string, never>): Promise<void>;
89
+ /** Compatibility read: all messages in stored position order; unauthorized returns []. */
90
+ messages(required: ChatMessagePageInput, optional?: Record<string, never>): Promise<ChatMessage[]>;
91
+ /** Idempotent update by message ID within its original conversation; unauthorized: null. */
92
+ appendMessage(required: {
93
+ readonly conversationId: string;
94
+ readonly message: ChatMessage;
95
+ }, optional?: Record<string, never>): Promise<ChatMessage | null>;
96
+ pageConversations(required: Record<string, never>, optional?: ChatPageOptions): Promise<ChatPage<ChatConversation>>;
97
+ pageMessages(input: ChatMessagePageInput, options?: ChatPageOptions): Promise<ChatPage<ChatMessage>>;
98
+ readonly search?: ChatSearchCapability;
99
+ }
100
+ /** Eight-method subset for hosts/fakes without paging; shares the object call contract. */
101
+ export type ChatHistoryStore = Pick<ChatStore, 'list' | 'get' | 'create' | 'rename' | 'touch' | 'delete' | 'messages' | 'appendMessage'>;
102
+ /** Authority for cross-owner retention must never reach ordinary request handlers. */
103
+ export interface ChatHistoryMaintenance {
104
+ /** Delete at most limit conversations; default 500, integer 1..500. */
105
+ sweepExpired(required: {
106
+ readonly now: number;
107
+ }, optional?: {
108
+ readonly limit?: number;
109
+ }): Promise<number>;
110
+ }
111
+ /** Bind one immutable owner scope; no per-call escape to an unscoped store. */
112
+ export type ChatStoreFactory = (required: {
113
+ readonly scope: ChatOwnerScope;
114
+ }, optional?: Record<string, never>) => ChatStore;
115
+ export type ChatStoreErrorCode = 'invalid-input' | 'invalid-cursor' | 'conflict' | 'unavailable';
116
+ /** Runtime error class in src/store/errors.ts; preserve the original error as cause. */
117
+ export interface ChatStoreError extends Error {
118
+ readonly name: 'ChatStoreError';
119
+ readonly code: ChatStoreErrorCode;
120
+ }
121
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1,21 @@
1
+ /** Postgres transcript adapter. The host owns the opened, migrated kernel and its lifetime. */
2
+ import type { Clock } from '@jini-ai/core/primitives';
3
+ import type { StorageKernel } from '@jini-ai/db/kernel';
4
+ import type { ChatDatabase } from '../sql/tables.js';
5
+ import type { ChatOwnerScope, ChatStore, ChatHistoryMaintenance } from '../ports.js';
6
+ export type { ChatDatabase, AiChatsTable, AiChatMessagesTable } from '../sql/tables.js';
7
+ /** Borrow an existing kernel, with no configuration, migration or connection lifecycle effects.
8
+ * @example createPostgresChatStore({ kernel, scope }, { clock: { nowMs: () => 42 } })
9
+ * @throws ChatStoreError invalid-input for a mismatched dialect/transport or owner scope.
10
+ */
11
+ export declare function createPostgresChatStore<DB extends ChatDatabase>(required: {
12
+ kernel: StorageKernel<DB>;
13
+ scope: ChatOwnerScope;
14
+ }, optional?: {
15
+ clock?: Clock;
16
+ }): ChatStore;
17
+ /** Privileged bounded expiry maintenance; never hand this collaborator to request handlers. */
18
+ export declare function createPostgresChatMaintenance<DB extends ChatDatabase>(required: {
19
+ kernel: StorageKernel<DB>;
20
+ }): ChatHistoryMaintenance;
21
+ //# sourceMappingURL=index.d.ts.map