@jini-ai/chat 0.5.3 → 0.5.5

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 (138) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/core/ag-ui/entry.d.ts +33 -0
  3. package/dist/core/ag-ui/entry.js +80 -0
  4. package/dist/core/agentic/chat-capabilities.d.ts +2 -4
  5. package/dist/core/agentic/index.d.ts +4 -17
  6. package/dist/core/agentic/index.js +4 -17
  7. package/dist/core/assistant-client/a2ui-action-poster.d.ts +46 -0
  8. package/dist/core/assistant-client/a2ui-action-poster.js +113 -0
  9. package/dist/core/assistant-client/ag-ui-transport.d.ts +68 -0
  10. package/dist/core/assistant-client/ag-ui-transport.js +147 -0
  11. package/dist/core/assistant-client/attachment-liveness.d.ts +37 -0
  12. package/dist/core/assistant-client/attachment-liveness.js +93 -0
  13. package/dist/core/assistant-client/durable-run-subscription.d.ts +39 -0
  14. package/dist/core/assistant-client/durable-run-subscription.js +96 -0
  15. package/dist/core/assistant-client/entry.d.ts +13 -0
  16. package/dist/core/assistant-client/entry.js +7 -0
  17. package/dist/core/assistant-client/messages.d.ts +73 -0
  18. package/dist/core/assistant-client/messages.js +68 -0
  19. package/dist/core/assistant-client/transport.d.ts +164 -0
  20. package/dist/core/assistant-client/transport.js +734 -0
  21. package/dist/core/browser/page-actions.d.ts +121 -0
  22. package/dist/core/browser/page-actions.js +113 -0
  23. package/dist/core/composer-history.d.ts +54 -0
  24. package/dist/core/composer-history.js +57 -0
  25. package/dist/core/durable-projection.d.ts +28 -0
  26. package/dist/core/durable-projection.js +71 -0
  27. package/dist/core/durable-runs/continuation.d.ts +18 -0
  28. package/dist/core/durable-runs/continuation.js +61 -0
  29. package/dist/core/durable-runs/ledger-contracts.d.ts +53 -0
  30. package/dist/core/durable-runs/ledger-contracts.js +2 -0
  31. package/dist/core/durable-runs/policies.d.ts +6 -0
  32. package/dist/core/durable-runs/policies.js +6 -0
  33. package/dist/core/durable-runs/ports.d.ts +123 -0
  34. package/dist/core/durable-runs/ports.js +2 -0
  35. package/dist/core/durable-runs/run-contracts.d.ts +17 -0
  36. package/dist/core/durable-runs/run-contracts.js +2 -0
  37. package/dist/core/durable-runs/run-lock.d.ts +4 -0
  38. package/dist/core/durable-runs/run-lock.js +4 -0
  39. package/dist/core/index.d.ts +9 -19
  40. package/dist/core/index.js +8 -19
  41. package/dist/core/messages.d.ts +2 -0
  42. package/dist/core/question-form/format.d.ts +0 -9
  43. package/dist/core/question-form/format.js +16 -2
  44. package/dist/core/question-form/normalize.js +1 -0
  45. package/dist/core/question-form/secret-answers.d.ts +38 -0
  46. package/dist/core/question-form/secret-answers.js +45 -0
  47. package/dist/core/question-form/types.d.ts +2 -0
  48. package/dist/core/question-form.d.ts +1 -0
  49. package/dist/core/question-form.js +1 -0
  50. package/dist/core/run-events/entry.d.ts +208 -16
  51. package/dist/core/run-events/entry.js +273 -39
  52. package/dist/core/transcript.d.ts +3 -2
  53. package/dist/core/transcript.js +19 -3
  54. package/dist/core/transport.d.ts +4 -0
  55. package/dist/core/user-text-redaction.d.ts +56 -0
  56. package/dist/core/user-text-redaction.js +89 -0
  57. package/dist/embed-widget/browser/page-actions.d.ts +1 -120
  58. package/dist/embed-widget/browser/page-actions.js +2 -112
  59. package/dist/embed-widget/react/embed.d.ts +1 -1
  60. package/dist/embed-widget/react/embed.js +1 -1
  61. package/dist/react/artifact-types.d.ts +9 -15
  62. package/dist/react/components/Composer.js +6 -0
  63. package/dist/react/components/MessageRow.js +24 -11
  64. package/dist/react/components/QuestionForm.d.ts +2 -1
  65. package/dist/react/components/QuestionForm.js +18 -12
  66. package/dist/react/components/QuestionsPanel.d.ts +3 -2
  67. package/dist/react/components/QuestionsPanel.js +2 -2
  68. package/dist/react/features/chat-pane/agent-tools.d.ts +1 -1
  69. package/dist/react/features/chat-pane/agent-tools.js +0 -1
  70. package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +5 -2
  71. package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
  72. package/dist/react/features/chat-pane/components/ChatPane.js +9 -4
  73. package/dist/react/features/chat-pane/create-typed-answer-poster.d.ts +2 -1
  74. package/dist/react/features/chat-pane/create-typed-answer-poster.js +33 -1
  75. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js +3 -2
  76. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +7 -1
  77. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +38 -23
  78. package/dist/react/features/chat-pane/model-options.d.ts +10 -0
  79. package/dist/react/features/chat-pane/model-options.js +51 -0
  80. package/dist/react/features/chat-pane/rules.d.ts +3 -2
  81. package/dist/react/features/chat-pane/rules.js +12 -4
  82. package/dist/react/features/chat-pane/types.d.ts +30 -1
  83. package/dist/react/hooks/assistant-chats-contracts.d.ts +164 -0
  84. package/dist/react/hooks/assistant-chats-contracts.js +2 -0
  85. package/dist/react/hooks/assistant-chats.d.ts +7 -0
  86. package/dist/react/hooks/assistant-chats.js +960 -0
  87. package/dist/react/hooks/attachment-preview-cache.d.ts +1 -1
  88. package/dist/react/hooks/attachment-preview-cache.js +1 -1
  89. package/dist/react/hooks/composer-draft-cache.d.ts +2 -2
  90. package/dist/react/hooks/composer-draft-cache.js +2 -2
  91. package/dist/react/hooks/composer-history-storage.d.ts +8 -0
  92. package/dist/react/hooks/composer-history-storage.js +40 -0
  93. package/dist/react/hooks/fab-position.d.ts +90 -0
  94. package/dist/react/hooks/fab-position.js +271 -0
  95. package/dist/react/hooks/useComposer.d.ts +10 -1
  96. package/dist/react/hooks/useComposer.js +14 -2
  97. package/dist/react/hooks/useComposerHistory.hooks.d.ts +15 -0
  98. package/dist/react/hooks/useComposerHistory.hooks.js +90 -0
  99. package/dist/react/hooks/useComposerHistoryKeys.hooks.d.ts +9 -0
  100. package/dist/react/hooks/useComposerHistoryKeys.hooks.js +90 -0
  101. package/dist/react/hooks/useConversation.d.ts +4 -1
  102. package/dist/react/hooks/useConversation.js +20 -14
  103. package/dist/react/hooks/useRunStream.js +12 -2
  104. package/dist/react/hooks/useUserTextGuard.d.ts +11 -0
  105. package/dist/react/hooks/useUserTextGuard.js +19 -0
  106. package/dist/react/index.d.ts +14 -21
  107. package/dist/react/index.js +8 -0
  108. package/dist/server/durable-runs/accept.d.ts +25 -0
  109. package/dist/server/durable-runs/accept.js +21 -0
  110. package/dist/server/durable-runs/entry.d.ts +5 -0
  111. package/dist/server/durable-runs/entry.js +4 -0
  112. package/dist/server/durable-runs/recover.d.ts +5 -0
  113. package/dist/server/durable-runs/recover.js +147 -0
  114. package/dist/server/run-finalizer/ports.d.ts +2 -16
  115. package/dist/store/legacy/sqlite/messages.d.ts +24 -18
  116. package/dist/store/legacy/sqlite/messages.js +12 -2
  117. package/dist/store/pglite/index.d.ts +2 -1
  118. package/dist/store/postgres/index.d.ts +2 -1
  119. package/dist/store/runtime/chat-expiry-sweep.d.ts +40 -0
  120. package/dist/store/runtime/chat-expiry-sweep.js +70 -0
  121. package/dist/store/runtime/chat-history-store.d.ts +20 -0
  122. package/dist/store/runtime/chat-history-store.js +50 -0
  123. package/dist/store/runtime/durable-run-store.d.ts +21 -0
  124. package/dist/store/runtime/durable-run-store.js +342 -0
  125. package/dist/store/runtime/entry.d.ts +7 -0
  126. package/dist/store/runtime/entry.js +6 -0
  127. package/dist/store/runtime/kernel.d.ts +29 -0
  128. package/dist/store/runtime/kernel.js +2 -0
  129. package/dist/store/runtime/run-guarded-chat-store.d.ts +10 -0
  130. package/dist/store/runtime/run-guarded-chat-store.js +42 -0
  131. package/dist/store/runtime/run-ledger.d.ts +15 -0
  132. package/dist/store/runtime/run-ledger.js +206 -0
  133. package/dist/store/sql/boundary.d.ts +6 -0
  134. package/dist/store/sql/boundary.js +7 -1
  135. package/dist/store/sql/store.d.ts +3 -2
  136. package/dist/store/sql/store.js +9 -2
  137. package/dist/store/sqlite/index.d.ts +3 -2
  138. package/package.json +75 -10
@@ -0,0 +1,206 @@
1
+ import { chatKernelTables } from "../sql/boundary.js";
2
+ import { createDurableRunStore } from "./durable-run-store.js";
3
+ /**
4
+ * @file The run-status half of chat history: the writes that decide how an assistant turn ENDS.
5
+ *
6
+ * Purpose:
7
+ * The browser formerly competed with the finalizer to finish daemon rows. Daemon acceptance
8
+ * and recovery now own those rows, while request-bound BYOK/AG-UI keep their browser writes.
9
+ * For one attempt the first terminal write wins; subsequent checkpoints or settlements are
10
+ * ignored. Continuation changes the attempt id by CAS while retaining the logical message;
11
+ * an older attempt cannot write afterward. Terminal rows remain absorbing during recovery.
12
+ *
13
+ * How it stays race-free with async storage: every "is it settled?" check and the write it guards
14
+ * run in ONE kernel transaction holding the run's lock ({@link runLockKey}), and {@link
15
+ * ChatRunLedger.settle} takes the same lock. On SQLite that is `BEGIN IMMEDIATE` (the write lock);
16
+ * on Postgres a transaction-scoped advisory lock, since a plain transaction does not serialize there.
17
+ *
18
+ * Architectural role:
19
+ * ONE Kysely body over the chat kernel (`platform/db/chat-kernel.ts`), for every dialect. Nothing
20
+ * here exposes history to request handlers; the cross-owner boot operation
21
+ * ({@link ChatRunLedger.reconcileInterrupted}) supplies a recovery port with the persisted owner
22
+ * and run stub. The recovery coordinator decides whether to reattach, continue or finalize;
23
+ * rows it cannot adopt receive an atomic cancellation fallback rather than spinning forever.
24
+ */
25
+ const TERMINAL_STATUSES = ["succeeded", "failed", "canceled"];
26
+ import { runLockKey } from "../../core/durable-runs/run-lock.js";
27
+ export { runLockKey } from "../../core/durable-runs/run-lock.js";
28
+ function isTerminal(status) {
29
+ return typeof status === "string" && TERMINAL_STATUSES.includes(status);
30
+ }
31
+ /** `run_status` is not terminal (NULL counts as not terminal). */
32
+ const notTerminal = (eb) => eb.or([eb("run_status", "is", null), eb("run_status", "not in", TERMINAL_STATUSES)]);
33
+ /** The assistant row of exactly this run. */
34
+ const isRunRow = (eb, run) => eb.and([
35
+ eb("id", "=", run.messageId),
36
+ eb("conversation_id", "=", run.conversationId),
37
+ eb("run_id", "=", run.runId),
38
+ eb("role", "=", "assistant"),
39
+ ]);
40
+ function readEvents(eventsJson) {
41
+ let events = [];
42
+ try {
43
+ events = eventsJson ? JSON.parse(eventsJson) : [];
44
+ }
45
+ catch {
46
+ events = [];
47
+ }
48
+ return Array.isArray(events) ? events : [];
49
+ }
50
+ function hasNewToolProgress(current, next) {
51
+ const key = (event) => event.kind === "tool_use" ? `use:${event.id}` : event.kind === "tool_result" ? `result:${event.toolUseId}` : "";
52
+ const saved = new Set(current.map(key));
53
+ return next.some((event) => { const id = key(event); return id !== "" && !saved.has(id); });
54
+ }
55
+ function retainSavedEvents(saved, incoming) {
56
+ const keys = new Set(saved.map((event) => JSON.stringify(event)));
57
+ const additions = incoming.filter((event) => event.kind !== "text" && !keys.has(JSON.stringify(event)));
58
+ return [...saved, ...additions];
59
+ }
60
+ function keepToolCheckpoints(saved, incoming) {
61
+ const key = (event) => event.kind === "tool_use" ? `use:${event.id}` : event.kind === "tool_result" ? `result:${event.toolUseId}` : "";
62
+ const seen = new Set(incoming.map(key));
63
+ return [...incoming, ...saved.filter((event) => { const id = key(event); return id !== "" && !seen.has(id); })];
64
+ }
65
+ /**
66
+ * @param store the chat kernel, or the open `chat.db` handle `createChatStoreFactory` also wraps.
67
+ * @complexity each method is one or two indexed statements, except `reconcileInterrupted`, which
68
+ * is O(stuck rows).
69
+ */
70
+ export function createChatRunLedger({ kernel: hostKernel, isDaemonRunId }, _optional = {}) {
71
+ const kernel = chatKernelTables({ kernel: hostKernel }, {});
72
+ async function isSettled(run) {
73
+ const row = await kernel.run((db) => db
74
+ .selectFrom("ai_chat_messages")
75
+ .select("run_status")
76
+ .where("id", "=", run.messageId)
77
+ .where("conversation_id", "=", run.conversationId)
78
+ .where("run_id", "=", run.runId)
79
+ .executeTakeFirst());
80
+ return isTerminal(row?.run_status);
81
+ }
82
+ return {
83
+ durable: createDurableRunStore({ kernel, isDaemonRunId }, {}),
84
+ unlessSettled({ run, write }, _optional = {}) {
85
+ return kernel.transaction(async () => {
86
+ await kernel.lockKey(runLockKey(run));
87
+ if (await isSettled(run))
88
+ return { written: false };
89
+ return { written: true, value: await write() };
90
+ });
91
+ },
92
+ settle(settlement, _optional = {}) {
93
+ return kernel.transaction(async () => {
94
+ await kernel.lockKey(runLockKey(settlement));
95
+ const saved = await kernel.run((db) => db.selectFrom("ai_chat_messages").select(["content", "events_json"])
96
+ .where((eb) => eb.and([isRunRow(eb, settlement), notTerminal(eb)])).executeTakeFirst());
97
+ if (!saved)
98
+ return false;
99
+ const content = settlement.content.length >= saved.content.length ? settlement.content : saved.content;
100
+ let events = keepToolCheckpoints(readEvents(saved.events_json), settlement.events);
101
+ if (content !== settlement.content) {
102
+ events = retainSavedEvents(readEvents(saved.events_json), settlement.events);
103
+ if (!events.some((event) => event.kind === "text"))
104
+ events.unshift({ kind: "text", text: saved.content });
105
+ }
106
+ const result = await kernel.run((db) => db
107
+ .updateTable("ai_chat_messages")
108
+ .set({
109
+ content,
110
+ events_json: JSON.stringify(events),
111
+ run_status: settlement.status,
112
+ ended_at: settlement.endedAt,
113
+ })
114
+ .where((eb) => eb.and([isRunRow(eb, settlement), notTerminal(eb)]))
115
+ .executeTakeFirst());
116
+ if (Number(result.numUpdatedRows) === 0)
117
+ return false;
118
+ await kernel.run((db) => db
119
+ .updateTable("ai_chats")
120
+ .set({ updated_at: settlement.endedAt })
121
+ .where("id", "=", settlement.conversationId)
122
+ .execute());
123
+ return true;
124
+ });
125
+ },
126
+ async checkpoint(progress, _optional = {}) {
127
+ return kernel.transaction(async () => {
128
+ await kernel.lockKey(runLockKey(progress));
129
+ const current = await kernel.run((db) => db.selectFrom("ai_chat_messages").select(["content", "events_json"])
130
+ .where((eb) => eb.and([isRunRow(eb, progress), notTerminal(eb)])).executeTakeFirst());
131
+ if (!current || progress.content.length < current.content.length)
132
+ return false;
133
+ const events = keepToolCheckpoints(readEvents(current.events_json), progress.events);
134
+ const grown = progress.content.length > current.content.length || hasNewToolProgress(readEvents(current.events_json), events);
135
+ const result = await kernel.run((db) => db
136
+ .updateTable("ai_chat_messages")
137
+ .set({ content: progress.content, events_json: JSON.stringify(events) })
138
+ .where((eb) => eb.and([isRunRow(eb, progress), notTerminal(eb)]))
139
+ .executeTakeFirst());
140
+ if (Number(result.numUpdatedRows) > 0 && grown) {
141
+ // Progress earns a fresh recovery budget. Replayed prefixes and the coordinator's
142
+ // divider are not progress and cannot keep a repeatedly failing attempt alive forever.
143
+ await kernel.run((db) => db.updateTable("assistant_run_attempts")
144
+ .set({ recovery_count: 0, recovery_deadline: null, recovery_elapsed_ms: 0, last_progress_at: Date.now() })
145
+ .where("message_id", "=", progress.messageId).execute());
146
+ }
147
+ return Number(result.numUpdatedRows) > 0;
148
+ });
149
+ },
150
+ async reconcileInterrupted(required = {}, optional = {}) {
151
+ const stuck = await kernel.run((db) => db
152
+ .selectFrom("ai_chat_messages")
153
+ .innerJoin("ai_chats", "ai_chats.id", "ai_chat_messages.conversation_id")
154
+ .select(["ai_chat_messages.id", "conversation_id", "run_id", "run_status", "content", "events_json", "owner_kind", "owner_id"])
155
+ .where("ai_chat_messages.role", "=", "assistant")
156
+ .where("ai_chat_messages.run_status", "in", ["queued", "running"])
157
+ .orderBy("ai_chat_messages.id")
158
+ .execute());
159
+ let count = 0;
160
+ for (const row of stuck) {
161
+ const dispatched = await optional.recover?.({
162
+ principalId: row.owner_kind === "user" ? row.owner_id : null,
163
+ conversationId: row.conversation_id,
164
+ message: {
165
+ id: row.id, role: "assistant", content: row.content, events: readEvents(row.events_json),
166
+ ...(row.run_id ? { runId: row.run_id } : {}),
167
+ runStatus: row.run_status,
168
+ },
169
+ });
170
+ if (dispatched) {
171
+ count += 1;
172
+ continue;
173
+ }
174
+ // A legacy stub without an attempt id, or a host without durable recovery, cannot ever
175
+ // be adopted. Preserve the latest checkpoint under the same lock as settle: discovery's
176
+ // snapshot may be old, and acceptance/recovery may have replaced its run id meanwhile.
177
+ const canceled = await kernel.transaction(async () => {
178
+ await kernel.lockKey(runLockKey({ conversationId: row.conversation_id, messageId: row.id, runId: row.run_id ?? "" }));
179
+ const matches = (eb) => eb.and([
180
+ eb("id", "=", row.id), eb("conversation_id", "=", row.conversation_id), eb("role", "=", "assistant"),
181
+ row.run_id === null ? eb("run_id", "is", null) : eb("run_id", "=", row.run_id),
182
+ notTerminal(eb),
183
+ ]);
184
+ const current = await kernel.run((db) => db.selectFrom("ai_chat_messages").select("events_json")
185
+ .where(matches).executeTakeFirst());
186
+ if (!current)
187
+ return false;
188
+ const endedAt = required.now ?? Date.now();
189
+ const events = [...readEvents(current.events_json), { kind: "status", label: "Stopped. Saved work is above." }];
190
+ const result = await kernel.run((db) => db.updateTable("ai_chat_messages")
191
+ .set({ run_status: "canceled", ended_at: endedAt, events_json: JSON.stringify(events) })
192
+ .where(matches).executeTakeFirst());
193
+ if (Number(result.numUpdatedRows) === 0)
194
+ return false;
195
+ await kernel.run((db) => db.updateTable("ai_chats").set({ updated_at: endedAt })
196
+ .where("id", "=", row.conversation_id).execute());
197
+ return true;
198
+ });
199
+ if (canceled)
200
+ count += 1;
201
+ }
202
+ return count;
203
+ },
204
+ };
205
+ }
206
+ //# sourceMappingURL=run-ledger.js.map
@@ -20,7 +20,13 @@ export declare function atStoreBoundary<T>({ operation }: {
20
20
  /** The single audited table-subset projection. Required table types are proven by DB's bound;
21
21
  * host extra tables and all transaction/locking effects stay on the original kernel object.
22
22
  * Kysely's invariant DB parameter requires this one erasure; it never bridges driver/version copies.
23
+ * @returns The original kernel with only the required table types exposed.
24
+ * @complexity O(1) time and space; no wrapper, connection or transaction is created.
23
25
  */
26
+ export declare function chatKernelTables<DB extends Tables, Tables extends ChatDatabase = ChatDatabase>({ kernel }: {
27
+ kernel: StorageKernel<DB>;
28
+ }, _optional?: {}): StorageKernel<Tables>;
29
+ /** Execute a transcript query through the shared table-subset projection. */
24
30
  export declare function runChatQuery<DB extends ChatDatabase, T>({ kernel, body }: {
25
31
  kernel: StorageKernel<DB>;
26
32
  body: (db: Kysely<ChatDatabase>) => T | Promise<T>;
@@ -25,8 +25,14 @@ export async function atStoreBoundary({ operation }) {
25
25
  /** The single audited table-subset projection. Required table types are proven by DB's bound;
26
26
  * host extra tables and all transaction/locking effects stay on the original kernel object.
27
27
  * Kysely's invariant DB parameter requires this one erasure; it never bridges driver/version copies.
28
+ * @returns The original kernel with only the required table types exposed.
29
+ * @complexity O(1) time and space; no wrapper, connection or transaction is created.
28
30
  */
31
+ export function chatKernelTables({ kernel }, _optional = {}) {
32
+ return kernel;
33
+ }
34
+ /** Execute a transcript query through the shared table-subset projection. */
29
35
  export function runChatQuery({ kernel, body }) {
30
- return kernel.run(db => body(db));
36
+ return chatKernelTables({ kernel }, {}).run(body);
31
37
  }
32
38
  //# sourceMappingURL=boundary.js.map
@@ -1,5 +1,6 @@
1
1
  import { type Clock } from '@jini-ai/core/primitives';
2
2
  import { type StorageKernel } from '@jini-ai/db/kernel';
3
+ import { type UserTextRedactionOptions } from '../../core/user-text-redaction.js';
3
4
  import type { ChatStore, ChatOwnerScope } from '../ports.js';
4
5
  import type { ChatDatabase } from './tables.js';
5
6
  /**
@@ -16,7 +17,7 @@ import type { ChatDatabase } from './tables.js';
16
17
  export declare function createSqlChatStore<DB extends ChatDatabase>({ kernel, scope: inputScope }: {
17
18
  kernel: StorageKernel<DB>;
18
19
  scope: ChatOwnerScope;
19
- }, { clock }?: {
20
+ }, { clock, ...redactionOptions }?: {
20
21
  clock?: Clock;
21
- }): ChatStore;
22
+ } & UserTextRedactionOptions): ChatStore;
22
23
  //# sourceMappingURL=store.d.ts.map
@@ -12,6 +12,7 @@
12
12
  import { sql } from 'kysely';
13
13
  import { createSystemClock } from '@jini-ai/core/primitives';
14
14
  import { isUniqueViolation } from '@jini-ai/db/kernel';
15
+ import { redactUserMessage } from '../../core/user-text-redaction.js';
15
16
  import { ChatStoreError } from '../errors.js';
16
17
  import { copyScope, atStoreBoundary, runChatQuery } from './boundary.js';
17
18
  import { pageLimit, decodeCursor, encodeCursor } from './cursor.js';
@@ -80,7 +81,7 @@ function toMessage(row) {
80
81
  * @complexity each method is one to four indexed statements; `list` is O(owned conversations),
81
82
  * `messages` O(messages in the conversation).
82
83
  */
83
- export function createSqlChatStore({ kernel, scope: inputScope }, { clock = createSystemClock() } = {}) {
84
+ export function createSqlChatStore({ kernel, scope: inputScope }, { clock = createSystemClock(), ...redactionOptions } = {}) {
84
85
  const scope = copyScope({ scope: inputScope });
85
86
  const { scopeId, ownerKind, ownerId } = scope;
86
87
  // The single subset boundary: DB must contain both required table definitions. Extra host tables
@@ -283,7 +284,13 @@ export function createSqlChatStore({ kernel, scope: inputScope }, { clock = crea
283
284
  },
284
285
  messages,
285
286
  async appendMessage({ conversationId, message }, _optional = {}) {
286
- return writeMessage(conversationId, message);
287
+ const prepared = redactUserMessage({ message }, { ...(redactionOptions.redactUserText ? { redactUserText: redactionOptions.redactUserText } : {}) });
288
+ const saved = await writeMessage(conversationId, prepared.message);
289
+ if (!saved || !prepared.secretRedacted)
290
+ return saved;
291
+ const signal = { secretRedacted: true, count: prepared.count };
292
+ redactionOptions.onSecretRedacted?.(signal);
293
+ return { ...saved, secretRedaction: signal };
287
294
  },
288
295
  async pageConversations(_required, options = {}) {
289
296
  const limit = pageLimit({ options: options });
@@ -1,3 +1,4 @@
1
+ import type { UserTextRedactionOptions } from '../../core/user-text-redaction.js';
1
2
  /** Sqlite transcript adapter. The host owns the opened, migrated kernel and its lifetime. */
2
3
  import type { Clock } from '@jini-ai/core/primitives';
3
4
  import type { StorageKernel } from '@jini-ai/db/kernel';
@@ -13,7 +14,7 @@ export declare function createSqliteChatStore<DB extends ChatDatabase>(required:
13
14
  scope: ChatOwnerScope;
14
15
  }, optional?: {
15
16
  clock?: Clock;
16
- }): ChatStore;
17
+ } & UserTextRedactionOptions): ChatStore;
17
18
  /** Privileged bounded expiry maintenance; never hand this collaborator to request handlers. */
18
19
  export declare function createSqliteChatMaintenance<DB extends ChatDatabase>(required: {
19
20
  kernel: StorageKernel<DB>;
@@ -28,7 +29,7 @@ export declare function createChatHistoryStore({ db, scope }: {
28
29
  scope: ChatOwnerScope;
29
30
  }, optional?: {
30
31
  clock?: Clock;
31
- }): ChatStore;
32
+ } & UserTextRedactionOptions): ChatStore;
32
33
  /** Compatibility retention wrapper; the caller still owns the handle and its configuration. */
33
34
  export declare function createChatHistoryMaintenance({ db }: {
34
35
  db: SqliteConnectionSource;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/chat",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Jini's chat/artifact frontend, consolidated into one package with two subpaths: framework-free chat vocabulary and pure parsers in `/core`, headless hooks + presentational components + slots in `/react`. Import only the layer you need.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -29,12 +29,23 @@
29
29
  "./server/run-finalizer": "universal",
30
30
  "./react/embed": "browser",
31
31
  "./transports/fetch-sse": "universal",
32
+ "./transports/http": "universal",
33
+ "./transports/ag-ui": "universal",
32
34
  "./browser/session-store": "browser",
33
- "./browser/page-actions": "browser"
35
+ "./browser/page-actions": "browser",
36
+ "./server/durable-runs": "node",
37
+ "./store/runtime": "node"
34
38
  },
35
39
  "neutralEntries": [
36
40
  "src/store/contracts"
37
- ]
41
+ ],
42
+ "isolation": {
43
+ "entries": {
44
+ "./browser/page-actions": "core",
45
+ "./transports/http": "core",
46
+ "./transports/ag-ui": "core"
47
+ }
48
+ }
38
49
  },
39
50
  "type": "module",
40
51
  "sideEffects": [
@@ -110,6 +121,16 @@
110
121
  "import": "./dist/embed-widget/react/embed.js",
111
122
  "default": "./dist/embed-widget/react/embed.js"
112
123
  },
124
+ "./transports/ag-ui": {
125
+ "types": "./dist/core/assistant-client/ag-ui-transport.d.ts",
126
+ "import": "./dist/core/assistant-client/ag-ui-transport.js",
127
+ "default": "./dist/core/assistant-client/ag-ui-transport.js"
128
+ },
129
+ "./transports/http": {
130
+ "types": "./dist/core/assistant-client/entry.d.ts",
131
+ "import": "./dist/core/assistant-client/entry.js",
132
+ "default": "./dist/core/assistant-client/entry.js"
133
+ },
113
134
  "./transports/fetch-sse": {
114
135
  "types": "./dist/embed-widget/transports/fetch-sse.d.ts",
115
136
  "import": "./dist/embed-widget/transports/fetch-sse.js",
@@ -124,6 +145,16 @@
124
145
  "types": "./dist/embed-widget/browser/page-actions.d.ts",
125
146
  "import": "./dist/embed-widget/browser/page-actions.js",
126
147
  "default": "./dist/embed-widget/browser/page-actions.js"
148
+ },
149
+ "./server/durable-runs": {
150
+ "types": "./dist/server/durable-runs/entry.d.ts",
151
+ "import": "./dist/server/durable-runs/entry.js",
152
+ "default": "./dist/server/durable-runs/entry.js"
153
+ },
154
+ "./store/runtime": {
155
+ "types": "./dist/store/runtime/entry.d.ts",
156
+ "import": "./dist/store/runtime/entry.js",
157
+ "default": "./dist/store/runtime/entry.js"
127
158
  }
128
159
  },
129
160
  "files": [
@@ -145,10 +176,7 @@
145
176
  "registry": "https://registry.npmjs.org"
146
177
  },
147
178
  "dependencies": {
148
- "@jini-ai/agentic": "^0.4.2",
149
- "@jini-ai/ui": "^0.4.4",
150
- "@jini-ai/protocol": "^0.4.0",
151
- "@jini-ai/core": "^0.4.1"
179
+ "@jini-ai/core": "^0.4.2"
152
180
  },
153
181
  "peerDependencies": {
154
182
  "react": "^18.3.0 || ^19.0.0",
@@ -165,7 +193,12 @@
165
193
  "@radix-ui/react-radio-group": "^1.4.7",
166
194
  "@radix-ui/react-select": "^2.3.7",
167
195
  "@radix-ui/react-slot": "^1.3.3",
168
- "recharts": "^3.10.1"
196
+ "recharts": "^3.10.1",
197
+ "@jini-ai/agent-runtime": "^0.4.1",
198
+ "@jini-ai/agentic": "^0.4.2",
199
+ "@jini-ai/protocol": "^0.4.1",
200
+ "@jini-ai/ui": "^0.4.5",
201
+ "@jini-ai/diagnostics": "^0.5.0"
169
202
  },
170
203
  "peerDependenciesMeta": {
171
204
  "react": {
@@ -212,6 +245,21 @@
212
245
  },
213
246
  "recharts": {
214
247
  "optional": true
248
+ },
249
+ "@jini-ai/agent-runtime": {
250
+ "optional": true
251
+ },
252
+ "@jini-ai/agentic": {
253
+ "optional": true
254
+ },
255
+ "@jini-ai/protocol": {
256
+ "optional": true
257
+ },
258
+ "@jini-ai/ui": {
259
+ "optional": true
260
+ },
261
+ "@jini-ai/diagnostics": {
262
+ "optional": true
215
263
  }
216
264
  },
217
265
  "devDependencies": {
@@ -238,8 +286,13 @@
238
286
  "@electric-sql/pglite": "0.5.8",
239
287
  "pg": "8.23.0",
240
288
  "@ag-ui/core": "0.0.58",
241
- "@jini-ai/db": "0.3.0",
242
- "@jini-ai/agent-runtime": "0.4.0"
289
+ "@jini-ai/db": "0.3.1",
290
+ "@jini-ai/agent-runtime": "0.4.2",
291
+ "@jini-ai/agentic": "^0.4.3",
292
+ "@jini-ai/ui": "^0.4.5",
293
+ "@jini-ai/protocol": "^0.4.2",
294
+ "@jini-ai/core": "^0.4.2",
295
+ "@jini-ai/diagnostics": "^0.5.1"
243
296
  },
244
297
  "typesVersions": {
245
298
  "*": {
@@ -276,6 +329,12 @@
276
329
  "react/embed": [
277
330
  "./dist/embed-widget/react/embed.d.ts"
278
331
  ],
332
+ "transports/ag-ui": [
333
+ "./dist/core/assistant-client/ag-ui-transport.d.ts"
334
+ ],
335
+ "transports/http": [
336
+ "./dist/core/assistant-client/entry.d.ts"
337
+ ],
279
338
  "transports/fetch-sse": [
280
339
  "./dist/embed-widget/transports/fetch-sse.d.ts"
281
340
  ],
@@ -284,6 +343,12 @@
284
343
  ],
285
344
  "browser/page-actions": [
286
345
  "./dist/embed-widget/browser/page-actions.d.ts"
346
+ ],
347
+ "server/durable-runs": [
348
+ "./dist/server/durable-runs/entry.d.ts"
349
+ ],
350
+ "store/runtime": [
351
+ "./dist/store/runtime/entry.d.ts"
287
352
  ]
288
353
  }
289
354
  },