@jini-ai/chat 0.6.16 → 0.6.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/core/accepted-input.d.ts +12 -0
- package/dist/core/accepted-input.js +29 -1
- package/dist/core/assistant-client/transport.js +2 -0
- package/dist/core/events.d.ts +6 -0
- package/dist/core/messages.d.ts +2 -0
- package/dist/core/run-events/entry.js +15 -2
- package/dist/core/surface-recovery.d.ts +19 -0
- package/dist/core/surface-recovery.js +26 -0
- package/dist/core/tool-events.d.ts +6 -1
- package/dist/core/tool-events.js +74 -27
- package/dist/core/transport.d.ts +2 -0
- package/dist/core/turn-usage.d.ts +1 -1
- package/dist/core/turn-usage.js +5 -3
- package/dist/core/user-text-redaction.js +30 -4
- package/dist/embed-widget/browser/frontend-session-bridge.d.ts +14 -1
- package/dist/embed-widget/browser/frontend-session-bridge.js +120 -47
- package/dist/react/components/Composer.js +4 -2
- package/dist/react/components/ConversationList.js +2 -1
- package/dist/react/components/ConversationTitleEditor.js +2 -1
- package/dist/react/components/CopyMessageButton.d.ts +4 -1
- package/dist/react/components/CopyMessageButton.js +3 -2
- package/dist/react/components/ExtEventErrorBoundary.js +2 -1
- package/dist/react/components/FolderChips.d.ts +13 -0
- package/dist/react/components/FolderChips.js +32 -0
- package/dist/react/components/Icon.d.ts +1 -1
- package/dist/react/components/Icon.js +1 -0
- package/dist/react/components/McpUiSurfaceCard.js +12 -5
- package/dist/react/components/MessageList.d.ts +1 -0
- package/dist/react/components/MessageList.js +2 -1
- package/dist/react/components/MessageRow.js +2 -1
- package/dist/react/components/MidRunUserMessage.js +3 -2
- package/dist/react/components/ToolCard.js +3 -2
- package/dist/react/components/ToolCard.rules.js +2 -1
- package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +5 -3
- package/dist/react/features/chat-pane/components/ChatPane.js +1 -0
- package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.d.ts +15 -2
- package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js +19 -3
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +47 -16
- package/dist/react/features/chat-pane/hooks/useChatPaneFileDrop.hooks.js +3 -2
- package/dist/react/features/chat-pane/hooks/useRuntimeDropUp.hooks.js +20 -3
- package/dist/react/features/chat-pane/model-options.d.ts +10 -0
- package/dist/react/features/chat-pane/model-options.js +22 -3
- package/dist/react/features/chat-pane/styles.d.ts +1 -1
- package/dist/react/features/chat-pane/styles.js +23 -1
- package/dist/react/features/chat-pane/surface-recovery-stream.d.ts +12 -0
- package/dist/react/features/chat-pane/surface-recovery-stream.js +47 -0
- package/dist/react/features/chat-pane/types.d.ts +4 -0
- package/dist/react/hooks/composer-draft-cache.d.ts +13 -10
- package/dist/react/hooks/composer-draft-cache.js +39 -14
- package/dist/react/hooks/useComposer.d.ts +7 -0
- package/dist/react/hooks/useComposer.js +83 -8
- package/dist/react/hooks/useConversation.d.ts +2 -0
- package/dist/react/hooks/useConversation.js +31 -20
- package/dist/react/hooks/useRunStream.js +2 -1
- package/dist/react/hooks/useSurfaceRecovery.d.ts +23 -0
- package/dist/react/hooks/useSurfaceRecovery.js +84 -0
- package/dist/react/styles/reference.css +10 -5
- package/dist/store/legacy/sqlite/messages.d.ts +12 -10
- package/dist/store/legacy/sqlite/messages.js +4 -1
- package/dist/store/runtime/chat-expiry-sweep.js +3 -1
- package/dist/store/runtime/durable-run-store.js +35 -3
- package/dist/store/sql/store.js +5 -1
- package/package.json +12 -12
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withFolderReceipt, foldersFromReceipts } from "../../../core/accepted-input.js";
|
|
1
2
|
import { redactUserText } from '../../../core/user-text-redaction.js';
|
|
2
3
|
import { parseJsonOrUndef } from '@jini-ai/db/core';
|
|
3
4
|
/** @internal Column alias list used by both `listMessages` and `upsertMessage`'s re-fetch. */
|
|
@@ -117,7 +118,7 @@ function messageFieldValues(m) {
|
|
|
117
118
|
m.role,
|
|
118
119
|
m.content,
|
|
119
120
|
...[m.agentId, m.agentName, m.runId, m.runStatus, m.lastRunEventId].map(orNull),
|
|
120
|
-
jsonOrNull(m.events),
|
|
121
|
+
jsonOrNull(withFolderReceipt({ message: m }).events),
|
|
121
122
|
jsonOrNull(m.attachments),
|
|
122
123
|
normalizeMessageSessionMode(m.sessionMode) ?? null,
|
|
123
124
|
jsonOrNull(m.runContext),
|
|
@@ -273,6 +274,7 @@ export function deleteMessage({ db, id }) {
|
|
|
273
274
|
}
|
|
274
275
|
/** @internal Converts a raw SQLite row to a plain object with JSON columns parsed and null coerced to undefined. */
|
|
275
276
|
function normalizeMessage(row) {
|
|
277
|
+
const folders = foldersFromReceipts({ events: parseJsonOrUndef(row.eventsJson) });
|
|
276
278
|
return {
|
|
277
279
|
id: row.id,
|
|
278
280
|
role: row.role,
|
|
@@ -284,6 +286,7 @@ function normalizeMessage(row) {
|
|
|
284
286
|
lastRunEventId: row.lastRunEventId ?? undefined,
|
|
285
287
|
events: parseJsonOrUndef(row.eventsJson),
|
|
286
288
|
attachments: parseJsonOrUndef(row.attachmentsJson),
|
|
289
|
+
...(folders.length ? { folders } : {}),
|
|
287
290
|
sessionMode: normalizeMessageSessionMode(row.sessionMode),
|
|
288
291
|
runContext: parseJsonOrUndef(row.runContextJson),
|
|
289
292
|
createdAt: row.createdAt ?? undefined,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import { redactSecrets } from '@jini-ai/core';
|
|
1
3
|
import { ChatStoreError } from "../errors.js";
|
|
2
4
|
/**
|
|
3
5
|
* @file Retention for chat history: deletes every conversation whose `expires_at` has passed.
|
|
@@ -47,7 +49,7 @@ export function startChatExpirySweep({ maintenance }, options = {}) {
|
|
|
47
49
|
const onError = options.onError ??
|
|
48
50
|
((error) => {
|
|
49
51
|
// eslint-disable-next-line no-console
|
|
50
|
-
console.warn(
|
|
52
|
+
console.warn('[chat-expiry-sweep] sweep failed:', redactSecrets({ input: inspect(error) }, { redactPii: false }));
|
|
51
53
|
});
|
|
52
54
|
let active;
|
|
53
55
|
const pass = () => {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { acceptedInputEvent, acceptedInputUserEvent } from "../../core/accepted-input.js";
|
|
2
|
+
import { redactUserMessage } from "../../core/user-text-redaction.js";
|
|
1
3
|
import { recoveredRunEvents } from "../../core/index.js";
|
|
2
4
|
import { chatKernelTables } from "../sql/boundary.js";
|
|
3
5
|
import { sql } from "kysely";
|
|
@@ -18,6 +20,10 @@ function isAcceptedUserShape({ message }, _optional = {}) {
|
|
|
18
20
|
return false;
|
|
19
21
|
return message.createdAt === undefined || Number.isFinite(message.createdAt);
|
|
20
22
|
}
|
|
23
|
+
/** Directory existence is host policy; the neutral store validates only its message contract. */
|
|
24
|
+
function validAcceptedFolders({ folders }, _options = {}) {
|
|
25
|
+
return Array.isArray(folders) && folders.length <= 10 && folders.every(path => typeof path === 'string' && path.length > 0);
|
|
26
|
+
}
|
|
21
27
|
function areAcceptedAttachments({ attachments }, _optional = {}) {
|
|
22
28
|
return Array.isArray(attachments) && attachments.every((attachment) => {
|
|
23
29
|
if (!attachment || typeof attachment.path !== "string" || typeof attachment.name !== "string")
|
|
@@ -25,6 +31,31 @@ function areAcceptedAttachments({ attachments }, _optional = {}) {
|
|
|
25
31
|
return attachment.kind === "file" || attachment.kind === "image";
|
|
26
32
|
});
|
|
27
33
|
}
|
|
34
|
+
/** Both top-level and queued user inputs obey the same reference contract. */
|
|
35
|
+
function validateAcceptedMetadata({ message }, _options = {}) {
|
|
36
|
+
if (message.folders !== undefined && !validAcceptedFolders({ folders: message.folders }))
|
|
37
|
+
throw new Error("Invalid accepted user folders");
|
|
38
|
+
if (message.attachments !== undefined && !areAcceptedAttachments({ attachments: message.attachments }, {}))
|
|
39
|
+
throw new Error("Invalid accepted user attachments");
|
|
40
|
+
}
|
|
41
|
+
/** Preserve only accepted user receipts; a browser cannot supply assistant lifecycle events.
|
|
42
|
+
* Receipt text uses the same guard as ordinary user text; folder data remains byte-exact.
|
|
43
|
+
*/
|
|
44
|
+
function acceptedReceiptFields({ message }, _options = {}) {
|
|
45
|
+
if (!Array.isArray(message.events))
|
|
46
|
+
return {};
|
|
47
|
+
const events = message.events.flatMap(event => {
|
|
48
|
+
if (!event || typeof event !== 'object')
|
|
49
|
+
return [];
|
|
50
|
+
const input = acceptedInputEvent({ event })?.data;
|
|
51
|
+
if (!input)
|
|
52
|
+
return [];
|
|
53
|
+
validateAcceptedMetadata({ message: input });
|
|
54
|
+
const safe = redactUserMessage({ message: { role: 'user', content: input.text } }).message;
|
|
55
|
+
return [acceptedInputUserEvent({ input: { ...input, text: safe.content } }, { receipt: 'accepted' })];
|
|
56
|
+
});
|
|
57
|
+
return events.length ? { events } : {};
|
|
58
|
+
}
|
|
28
59
|
function acceptedUserMessage({ request, now }, _optional = {}) {
|
|
29
60
|
const context = JSON.parse(request.contextRef);
|
|
30
61
|
// Older clients supplied only a prompt; never invent a user identity from that transcript.
|
|
@@ -33,11 +64,12 @@ function acceptedUserMessage({ request, now }, _optional = {}) {
|
|
|
33
64
|
return null;
|
|
34
65
|
if (!isAcceptedUserShape({ message }, {}))
|
|
35
66
|
throw new Error("Invalid accepted user message");
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// User input cannot claim an assistant run's status/events. Keep the same identity and payload
|
|
67
|
+
validateAcceptedMetadata({ message });
|
|
68
|
+
// User input cannot claim assistant lifecycle events. Keep the same identity and payload
|
|
39
69
|
// as its browser PUT, including the original clock and attachment order.
|
|
40
70
|
return { id: message.id, role: "user", content: message.content, createdAt: message.createdAt ?? now,
|
|
71
|
+
...acceptedReceiptFields({ message }),
|
|
72
|
+
...(message.folders ? { folders: message.folders } : {}),
|
|
41
73
|
...(message.attachments ? { attachments: message.attachments } : {}) };
|
|
42
74
|
}
|
|
43
75
|
function recoveryFields({ row, metadata }, _optional = {}) {
|
package/dist/store/sql/store.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withFolderReceipt, foldersFromReceipts } from '../../core/accepted-input.js';
|
|
1
2
|
const MESSAGE_POSITION = 'm.position';
|
|
2
3
|
const CONVERSATION_UPDATED_AT = 'c.updated_at';
|
|
3
4
|
/** One SQL query body upstreamed from the existing host adapter; no resource/schema ownership. */
|
|
@@ -84,8 +85,10 @@ function agentFields(row) {
|
|
|
84
85
|
function jsonFields(row) {
|
|
85
86
|
const events = parseJsonColumn(row.events_json);
|
|
86
87
|
const attachments = parseJsonColumn(row.attachments_json);
|
|
88
|
+
const folders = foldersFromReceipts({ events });
|
|
87
89
|
return {
|
|
88
90
|
...(events === undefined ? {} : { events }),
|
|
91
|
+
...(folders.length ? { folders } : {}),
|
|
89
92
|
...(attachments === undefined ? {} : { attachments }),
|
|
90
93
|
};
|
|
91
94
|
}
|
|
@@ -190,7 +193,8 @@ export function createSqlChatStore({ kernel, scope: inputScope }, { clock = crea
|
|
|
190
193
|
return rows.map(toMessage);
|
|
191
194
|
}
|
|
192
195
|
/** Writes and projects one message inside the borrowed transaction; null for absent/foreign IDs. */
|
|
193
|
-
function writeMessage(conversationId,
|
|
196
|
+
function writeMessage(conversationId, message) {
|
|
197
|
+
const m = withFolderReceipt({ message });
|
|
194
198
|
return kernel.transaction(async () => {
|
|
195
199
|
await kernel.lockKey(conversationLockKey(conversationId));
|
|
196
200
|
const owned = await run((db) => db
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/chat",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.19",
|
|
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": {
|
|
@@ -214,10 +214,10 @@
|
|
|
214
214
|
},
|
|
215
215
|
"dependencies": {
|
|
216
216
|
"kysely": "^0.29.6",
|
|
217
|
-
"@jini-ai/core": "^0.6.
|
|
218
|
-
"@jini-ai/
|
|
219
|
-
"@jini-ai/
|
|
220
|
-
"@jini-ai/
|
|
217
|
+
"@jini-ai/core": "^0.6.19",
|
|
218
|
+
"@jini-ai/db": "^0.6.19",
|
|
219
|
+
"@jini-ai/agentic": "^0.6.19",
|
|
220
|
+
"@jini-ai/diagnostics": "^0.6.19"
|
|
221
221
|
},
|
|
222
222
|
"peerDependencies": {
|
|
223
223
|
"react": "^18.3.0 || ^19.0.0",
|
|
@@ -261,13 +261,13 @@
|
|
|
261
261
|
"pg": "8.23.0",
|
|
262
262
|
"@ag-ui/core": "0.0.58",
|
|
263
263
|
"fast-check": "4.3.0",
|
|
264
|
-
"@jini-ai/
|
|
265
|
-
"@jini-ai/
|
|
266
|
-
"@jini-ai/
|
|
267
|
-
"@jini-ai/
|
|
268
|
-
"@jini-ai/
|
|
269
|
-
"@jini-ai/
|
|
270
|
-
"@jini-ai/
|
|
264
|
+
"@jini-ai/db": "0.6.19",
|
|
265
|
+
"@jini-ai/agentic": "^0.6.19",
|
|
266
|
+
"@jini-ai/agent-runtime": "0.6.19",
|
|
267
|
+
"@jini-ai/protocol": "^0.6.19",
|
|
268
|
+
"@jini-ai/diagnostics": "^0.6.19",
|
|
269
|
+
"@jini-ai/ui": "^0.6.19",
|
|
270
|
+
"@jini-ai/core": "^0.6.19"
|
|
271
271
|
},
|
|
272
272
|
"typesVersions": {
|
|
273
273
|
"*": {
|