@jskit-ai/assistant-core 0.2.7 → 0.2.9
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/examples/conversation/package.json +5 -5
- package/package.json +18 -7
- package/src/client/browser.js +21 -0
- package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
- package/src/client/conversation/AssistantComposerActions.vue +1 -1
- package/src/client/conversation/AssistantConversationElement.vue +14 -52
- package/src/client/conversation/AssistantGoalControl.vue +13 -10
- package/src/client/conversation/AssistantPromptInput.vue +6 -91
- package/src/client/conversation/AssistantTranscript.css +505 -0
- package/src/client/conversation/AssistantTranscript.vue +24 -1197
- package/src/client/conversation/attachmentFiles.js +158 -0
- package/src/client/conversation/attachmentQueueItems.js +121 -0
- package/src/client/conversation/attachmentUploadRows.js +265 -0
- package/src/client/conversation/conversationDisplay.js +54 -0
- package/src/client/conversation/messageDelivery.js +13 -9
- package/src/client/conversation/retainedConversation.js +14 -3
- package/src/client/conversation/submitText.js +13 -2
- package/src/client/conversation/transcriptEntries.js +225 -0
- package/src/client/conversation/useAssistantAttachments.js +86 -388
- package/src/client/conversation/useAssistantQuestions.js +16 -3
- package/src/client/conversation/useAssistantSuggestions.js +16 -2
- package/src/client/conversation/useScrollToBottom.js +17 -19
- package/src/client/conversation/useTextareaAutoGrow.js +115 -0
- package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
- package/src/client/lib/assistantApi.js +192 -161
- package/src/client/types.js +110 -0
- package/src/server/conversation/attachments.js +21 -6
- package/src/server/conversation/boundedToolLoop.js +1 -0
- package/src/server/conversation/claudeClient.js +71 -20
- package/src/server/conversation/claudeCommandRun.js +191 -0
- package/src/server/conversation/claudeHistory.js +7 -0
- package/src/server/conversation/claudeNativeTurn.js +708 -0
- package/src/server/conversation/claudeProcess.js +186 -112
- package/src/server/conversation/claudeProcessStart.js +214 -0
- package/src/server/conversation/claudePromptDelivery.js +183 -0
- package/src/server/conversation/claudeSessionCleanup.js +248 -0
- package/src/server/conversation/claudeTools.js +15 -3
- package/src/server/conversation/claudeTurn.js +212 -1388
- package/src/server/conversation/codexAccountReader.js +168 -0
- package/src/server/conversation/codexAuthPreflight.js +5 -4
- package/src/server/conversation/codexClient.js +18 -6
- package/src/server/conversation/codexConfiguration.js +8 -844
- package/src/server/conversation/codexConversationStops.js +377 -0
- package/src/server/conversation/codexConversationStore.js +119 -85
- package/src/server/conversation/codexConversationTurns.js +378 -0
- package/src/server/conversation/codexConversations.js +134 -866
- package/src/server/conversation/codexDetachedConversation.js +203 -0
- package/src/server/conversation/codexDetachedTurn.js +145 -104
- package/src/server/conversation/codexEvents.js +5 -200
- package/src/server/conversation/codexGoalCommands.js +14 -4
- package/src/server/conversation/codexHelperExecution.js +57 -57
- package/src/server/conversation/codexHelperThreadLedger.js +30 -288
- package/src/server/conversation/codexHelperThreadRecord.js +304 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
- package/src/server/conversation/codexHelperThreads.js +94 -93
- package/src/server/conversation/codexHistoryAdapter.js +13 -8
- package/src/server/conversation/codexIsolation.js +672 -0
- package/src/server/conversation/codexLiveItems.js +379 -0
- package/src/server/conversation/codexMessageCommands.js +68 -286
- package/src/server/conversation/codexMessageDelivery.js +46 -37
- package/src/server/conversation/codexModelCatalog.js +86 -7
- package/src/server/conversation/codexNotificationObserver.js +81 -88
- package/src/server/conversation/codexNotificationQueue.js +21 -10
- package/src/server/conversation/codexObservationControl.js +105 -89
- package/src/server/conversation/codexOutput.js +84 -419
- package/src/server/conversation/codexProcess.js +29 -1053
- package/src/server/conversation/codexProcessLeader.js +1 -0
- package/src/server/conversation/codexPromptDispatch.js +241 -0
- package/src/server/conversation/codexProvider.js +147 -1807
- package/src/server/conversation/codexProviderAuth.js +348 -0
- package/src/server/conversation/codexProviderInvalidation.js +371 -0
- package/src/server/conversation/codexProviderInventories.js +437 -0
- package/src/server/conversation/codexProviderOwner.js +149 -957
- package/src/server/conversation/codexProviderRelease.js +465 -0
- package/src/server/conversation/codexProviderSelection.js +26 -15
- package/src/server/conversation/codexProviderThreads.js +698 -0
- package/src/server/conversation/codexProviderTurns.js +453 -0
- package/src/server/conversation/codexReasoning.js +32 -23
- package/src/server/conversation/codexRenewalCommands.js +18 -6
- package/src/server/conversation/codexRenewalThreads.js +7 -22
- package/src/server/conversation/codexResultSettlement.js +70 -88
- package/src/server/conversation/codexRunJournal.js +91 -97
- package/src/server/conversation/codexRuntime.js +426 -0
- package/src/server/conversation/codexRuntimeInventory.js +113 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
- package/src/server/conversation/codexRuntimeStart.js +431 -0
- package/src/server/conversation/codexRuntimeStop.js +324 -0
- package/src/server/conversation/codexSessionCleanup.js +25 -10
- package/src/server/conversation/codexSessionReconciliation.js +21 -7
- package/src/server/conversation/codexThreadReadiness.js +49 -52
- package/src/server/conversation/codexThreadSnapshot.js +210 -0
- package/src/server/conversation/codexThreadStatus.js +48 -62
- package/src/server/conversation/codexTurn.js +171 -55
- package/src/server/conversation/codexTurnActivity.js +210 -0
- package/src/server/conversation/codexTurnControl.js +44 -48
- package/src/server/conversation/codexTurnEndings.js +610 -0
- package/src/server/conversation/codexTurnRecovery.js +97 -848
- package/src/server/conversation/codexTurnState.js +138 -111
- package/src/server/conversation/completedResponses.js +227 -0
- package/src/server/conversation/configuration.js +2 -2
- package/src/server/conversation/continuity.js +43 -6
- package/src/server/conversation/conversationDelivery.js +394 -0
- package/src/server/conversation/conversationEntries.js +365 -0
- package/src/server/conversation/conversationEvents.js +192 -0
- package/src/server/conversation/conversationGoals.js +187 -0
- package/src/server/conversation/conversationHandles.js +237 -0
- package/src/server/conversation/conversationReads.js +206 -0
- package/src/server/conversation/conversationRefusals.js +58 -0
- package/src/server/conversation/conversationReplacement.js +254 -0
- package/src/server/conversation/conversationSending.js +273 -0
- package/src/server/conversation/conversationStopping.js +121 -0
- package/src/server/conversation/conversationTurnDelivery.js +413 -0
- package/src/server/conversation/conversationTurns.js +511 -0
- package/src/server/conversation/deliveryTiming.js +25 -3
- package/src/server/conversation/fileStorage.js +11 -6
- package/src/server/conversation/hookBridge.js +14 -3
- package/src/server/conversation/index.js +1 -1
- package/src/server/conversation/localExecution.js +82 -625
- package/src/server/conversation/nativeCommandCallbacks.js +10 -0
- package/src/server/conversation/nativeConversationLifecycle.js +379 -0
- package/src/server/conversation/nativeHistoryExport.js +27 -18
- package/src/server/conversation/normalize.js +9 -0
- package/src/server/conversation/openCodeClient.js +295 -366
- package/src/server/conversation/openCodeNativeHistory.js +141 -0
- package/src/server/conversation/openCodeNativeSession.js +79 -0
- package/src/server/conversation/openCodeProcess.js +109 -53
- package/src/server/conversation/openCodeProcessOwner.js +565 -0
- package/src/server/conversation/openCodeRuntime.js +111 -1814
- package/src/server/conversation/openCodeSessionTurns.js +547 -0
- package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
- package/src/server/conversation/openCodeTools.js +28 -14
- package/src/server/conversation/openCodeTurn.js +80 -74
- package/src/server/conversation/openCodeTurnMonitor.js +388 -0
- package/src/server/conversation/processIdentity.js +582 -0
- package/src/server/conversation/provider.js +21 -6
- package/src/server/conversation/providers/api.js +268 -107
- package/src/server/conversation/providers/claude.js +29 -6
- package/src/server/conversation/providers/claudeDriver.js +325 -250
- package/src/server/conversation/providers/codex.js +69 -33
- package/src/server/conversation/providers/codexDriver.js +503 -1081
- package/src/server/conversation/providers/codexDriverConnection.js +331 -0
- package/src/server/conversation/providers/codexDriverTurns.js +464 -0
- package/src/server/conversation/providers/opencode.js +30 -4
- package/src/server/conversation/providers/opencodeDriver.js +321 -659
- package/src/server/conversation/providers/opencodeStandalone.js +475 -0
- package/src/server/conversation/providers/scoped.js +64 -19
- package/src/server/conversation/runtime.js +171 -2863
- package/src/server/conversation/storage.js +91 -50
- package/src/server/conversation/streams.js +15 -2
- package/src/server/conversation/structuredOutput.js +7 -11
- package/src/server/conversation/systemPrompt.js +13 -1
- package/src/server/conversation/tools.js +47 -15
- package/src/server/conversation/transcript.js +49 -20
- package/src/server/hosts/claudeTurn.js +2 -4
- package/src/server/hosts/codexConfiguration.js +6 -8
- package/src/server/hosts/codexEvents.js +4 -2
- package/src/server/hosts/codexProcess.js +1 -1
- package/src/server/hosts/codexProvider.js +11 -9
- package/src/server/hosts/codexTurn.js +3 -3
- package/src/server/hosts/openCodeClient.js +4 -2
- package/src/server/index.js +0 -2
- package/src/server/lib/aiConnectionClient.js +35 -25
- package/src/server/lib/ndjson.js +1 -0
- package/src/server/lib/providers/anthropicClient.js +15 -6
- package/src/server/lib/providers/common.js +10 -6
- package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
- package/src/server/lib/serviceToolCatalog.js +29 -283
- package/src/server/lib/serviceToolEntries.js +270 -0
- package/src/server/types.js +78 -0
- package/src/shared/assistantResource.js +0 -23
- package/src/shared/conversation/answerChoiceSugar.js +2 -14
- package/src/shared/conversation/messageInputSugar.js +18 -0
- package/src/shared/conversation/numberedQuestionSugar.js +6 -16
- package/src/shared/conversation/pagination.js +1 -1
- package/src/shared/conversation/questions.js +1 -0
- package/src/shared/conversation/streaming.js +1 -0
- package/src/shared/index.js +0 -1
- package/src/testing/conversationStorageContract.js +20 -0
- package/test/aiConnectionClient.test.js +42 -1
- package/test/apiConversationDriver.test.js +217 -38
- package/test/assistantBoundedToolLoop.test.js +1 -1
- package/test/claudeConversationSession.test.js +15 -11
- package/test/claudeInstructionResume.test.js +1 -1
- package/test/claudeProcess.test.js +9 -9
- package/test/claudeTurn.test.js +21 -8
- package/test/codexAccountChange.test.js +5 -3
- package/test/codexConversationSession.test.js +22 -21
- package/test/codexEvents.test.js +3 -1
- package/test/codexHelperDeletion.test.js +3 -7
- package/test/codexIsolation.test.js +6 -6
- package/test/codexModelCatalog.test.js +6 -7
- package/test/codexProcess.test.js +3 -3
- package/test/codexThreadSelection.test.js +4 -1
- package/test/codexTurn.test.js +29 -20
- package/test/conversationApiFacilities.test.js +11 -9
- package/test/conversationAttachments.test.js +3 -3
- package/test/conversationConfiguration.test.js +4 -4
- package/test/conversationNativeStorage.test.js +11 -10
- package/test/conversationReplacement.test.js +13 -13
- package/test/conversationRuntime.test.js +9 -5
- package/test/conversationSession.test.js +78 -22
- package/test/conversationTools.test.js +47 -26
- package/test/fileConversationStorage.test.js +10 -3
- package/test/fixtures/codexHelperThreadLedger.js +3 -3
- package/test/frameworkActions.test.js +33 -18
- package/test/localConversationExecution.test.js +4 -4
- package/test/localExecution.test.js +1 -1
- package/test/openCodeConnectionChange.test.js +3 -2
- package/test/openCodeRuntime.test.js +8 -7
- package/test/openCodeTools.test.js +1 -1
- package/test/opencodeConversationSession.test.js +4 -3
- package/test/serviceToolCatalog.test.js +3 -8
- package/src/server/lib/assistantCompletion.js +0 -190
- package/src/server/lib/assistantToolLoop.js +0 -259
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { effectScope, shallowReactive } from "vue";
|
|
2
2
|
|
|
3
|
+
// Each application's retained conversations, by target key; the entry goes when its last reader releases it.
|
|
3
4
|
const applicationConversations = new WeakMap();
|
|
4
5
|
|
|
5
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Views and voice retain the same readers and delivery state for an exact target. The runtime is created once per
|
|
8
|
+
* application and key, as `createRuntime(readers, retain)`: `readers` holds every current reader, and `retain()`
|
|
9
|
+
* retains the same runtime again for another reader, so the runtime lists it in its result.
|
|
10
|
+
* @template Runtime
|
|
11
|
+
* @param {import("vue").App} app
|
|
12
|
+
* @param {string} key
|
|
13
|
+
* @param {(readers: Map<symbol, { active: boolean }>, retain: () => { runtime: Runtime, release: () => void }) => Runtime} createRuntime
|
|
14
|
+
* @param {{ active: boolean }} [reader]
|
|
15
|
+
* @returns {{ runtime: Runtime, release: () => void }}
|
|
16
|
+
*/
|
|
6
17
|
function retainAssistantConversation(app, key, createRuntime, reader = { active: true }) {
|
|
7
18
|
let conversations = applicationConversations.get(app);
|
|
8
19
|
if (!conversations) { conversations = new Map(); applicationConversations.set(app, conversations); }
|
|
@@ -10,10 +21,10 @@ function retainAssistantConversation(app, key, createRuntime, reader = { active:
|
|
|
10
21
|
if (!entry) {
|
|
11
22
|
const scope = effectScope(true);
|
|
12
23
|
const readers = shallowReactive(new Map());
|
|
24
|
+
const retain = () => retainAssistantConversation(app, key, createRuntime);
|
|
13
25
|
try {
|
|
14
|
-
const runtime = app.runWithContext(() => scope.run(() => createRuntime(readers)));
|
|
26
|
+
const runtime = app.runWithContext(() => scope.run(() => createRuntime(readers, retain)));
|
|
15
27
|
entry = { scope, readers, runtime };
|
|
16
|
-
runtime.retain = () => retainAssistantConversation(app, key, createRuntime);
|
|
17
28
|
conversations.set(key, entry);
|
|
18
29
|
} catch (error) { scope.stop(); throw error; }
|
|
19
30
|
}
|
|
@@ -3,8 +3,19 @@
|
|
|
3
3
|
const CANNOT_SEND_YET = "Your words are in the chat box, but this conversation cannot send yet.";
|
|
4
4
|
const submissionError = (message, { messageCode }) => Object.assign(new Error(message), { messageCode, messageParams: {} });
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/** Without an afterDraftChange, the composer's state is read again as soon as the draft is written. */
|
|
7
|
+
async function readStateAtOnce() {}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A companion's text enters the same draft and send action as keyboard input.
|
|
11
|
+
* @param {{
|
|
12
|
+
* getState: () => { active: boolean, canSend: boolean, draft: string, id: unknown, turnActive?: boolean },
|
|
13
|
+
* setDraft: (draft: string) => void,
|
|
14
|
+
* submit: () => unknown,
|
|
15
|
+
* afterDraftChange?: () => Promise<void>
|
|
16
|
+
* }} composer
|
|
17
|
+
*/
|
|
18
|
+
export function createAssistantTextSubmission({ getState, setDraft, submit, afterDraftChange = readStateAtOnce }) {
|
|
8
19
|
let revision = 0;
|
|
9
20
|
return async function submitText(text, { sendImmediately = true, signal, timeoutMs = 2000 } = {}) {
|
|
10
21
|
const request = ++revision;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { parseNumberedQuestionPrompt } from "../../shared/conversation/numberedQuestionSugar.js";
|
|
3
|
+
import { parseLongTextReviewBlocks } from "../../shared/conversation/longTextBlocks.js";
|
|
4
|
+
import { normalizeThinkingMessageText } from "../../shared/conversation/thinkingText.js";
|
|
5
|
+
import { useJskitText } from "../i18n.js";
|
|
6
|
+
|
|
7
|
+
const USER_MESSAGE_COLLAPSE_MIN_CHARACTERS = 360;
|
|
8
|
+
const USER_MESSAGE_PREVIEW_MAX_CHARACTERS = 280;
|
|
9
|
+
const USER_MESSAGE_PREVIEW_MAX_LINES = 4;
|
|
10
|
+
const DISPLAY_MESSAGE_CACHE_LIMIT = 500;
|
|
11
|
+
const TIME_FORMAT = Object.freeze({
|
|
12
|
+
year: "numeric",
|
|
13
|
+
month: "short",
|
|
14
|
+
day: "2-digit",
|
|
15
|
+
hour: "2-digit",
|
|
16
|
+
minute: "2-digit"
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/** The collapsed preview of a long user message, or "" when it is short enough to show whole. */
|
|
20
|
+
function userMessagePreviewText(value = "") {
|
|
21
|
+
const text = String(value || "");
|
|
22
|
+
const characters = [...text];
|
|
23
|
+
const lines = text.split(/\r\n?|\n/u);
|
|
24
|
+
if (
|
|
25
|
+
characters.length <= USER_MESSAGE_COLLAPSE_MIN_CHARACTERS &&
|
|
26
|
+
lines.length <= USER_MESSAGE_PREVIEW_MAX_LINES
|
|
27
|
+
) {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
const firstLines = lines.slice(0, USER_MESSAGE_PREVIEW_MAX_LINES).join("\n");
|
|
31
|
+
const preview = [...firstLines]
|
|
32
|
+
.slice(0, USER_MESSAGE_PREVIEW_MAX_CHARACTERS)
|
|
33
|
+
.join("")
|
|
34
|
+
.trimEnd();
|
|
35
|
+
return preview ? `${preview}…` : "";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function conversationAgentMessages(turn = {}) {
|
|
39
|
+
if (Array.isArray(turn.messages)) {
|
|
40
|
+
return turn.messages.filter((message) => (
|
|
41
|
+
["assistant", "commentary", "thinking"].includes(String(message?.role || "").trim())
|
|
42
|
+
));
|
|
43
|
+
}
|
|
44
|
+
return [
|
|
45
|
+
...(Array.isArray(turn.thinking) ? turn.thinking : []),
|
|
46
|
+
...(Array.isArray(turn.commentary) ? turn.commentary : []),
|
|
47
|
+
turn.assistant
|
|
48
|
+
].filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function conversationMessageKey(message = {}, index = 0) {
|
|
52
|
+
return [
|
|
53
|
+
String(message.messageId || "").trim(),
|
|
54
|
+
String(message.role || "").trim(),
|
|
55
|
+
String(message.at || "").trim(),
|
|
56
|
+
index
|
|
57
|
+
].join(":");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The transcript's entries, one per displayed message in chronological order, with consecutive reasoning grouped into
|
|
62
|
+
* one progress entry. Each displayed message is parsed once and cached by its key until `clearDisplayCache()`.
|
|
63
|
+
* @param {{ turns: any[] }} props The transcript's props.
|
|
64
|
+
*/
|
|
65
|
+
function useTranscriptEntries(props) {
|
|
66
|
+
const jskitText = useJskitText();
|
|
67
|
+
const { formatDateTime, serverMessage } = jskitText;
|
|
68
|
+
const displayMessageCache = new Map();
|
|
69
|
+
|
|
70
|
+
function displayTime(value = "") {
|
|
71
|
+
const date = new Date(String(value || ""));
|
|
72
|
+
if (Number.isNaN(date.getTime())) {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
return formatDateTime(date, TIME_FORMAT);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function displayMessage(message = null, {
|
|
79
|
+
allowNumberedQuestions = false,
|
|
80
|
+
preserveParagraphLineBreaks = false,
|
|
81
|
+
previewUserMessage = false
|
|
82
|
+
} = {}, cacheKey = "") {
|
|
83
|
+
if (!message) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const normalizedCacheKey = String(cacheKey || "").trim();
|
|
87
|
+
const cached = normalizedCacheKey ? displayMessageCache.get(normalizedCacheKey) : null;
|
|
88
|
+
// Times and status texts follow the active locale, so a locale change re-renders them.
|
|
89
|
+
const activeLocale = jskitText.locale;
|
|
90
|
+
if (
|
|
91
|
+
cached &&
|
|
92
|
+
cached.locale === activeLocale &&
|
|
93
|
+
cached.allowNumberedQuestions === allowNumberedQuestions &&
|
|
94
|
+
cached.attachments === message.attachments &&
|
|
95
|
+
cached.at === message.at &&
|
|
96
|
+
cached.messageId === message.messageId &&
|
|
97
|
+
cached.preserveParagraphLineBreaks === preserveParagraphLineBreaks &&
|
|
98
|
+
cached.previewUserMessage === previewUserMessage &&
|
|
99
|
+
cached.role === message.role &&
|
|
100
|
+
cached.text === message.text
|
|
101
|
+
) {
|
|
102
|
+
return { ...message, ...cached.value };
|
|
103
|
+
}
|
|
104
|
+
const messageText = message.textCode ? serverMessage({ message: message.text, messageCode: message.textCode }) : message.text;
|
|
105
|
+
const questionInput = allowNumberedQuestions
|
|
106
|
+
? parseNumberedQuestionPrompt(messageText)
|
|
107
|
+
: {
|
|
108
|
+
intro: "",
|
|
109
|
+
outro: "",
|
|
110
|
+
questions: []
|
|
111
|
+
};
|
|
112
|
+
const hasQuestions = questionInput.questions.length > 0;
|
|
113
|
+
const previewText = previewUserMessage ? userMessagePreviewText(message.text) : "";
|
|
114
|
+
const value = {
|
|
115
|
+
blocks: parseLongTextReviewBlocks(hasQuestions ? questionInput.intro : messageText, {
|
|
116
|
+
preserveParagraphLineBreaks
|
|
117
|
+
}),
|
|
118
|
+
outroBlocks: parseLongTextReviewBlocks(hasQuestions ? questionInput.outro : "", {
|
|
119
|
+
preserveParagraphLineBreaks
|
|
120
|
+
}),
|
|
121
|
+
previewBlocks: previewText
|
|
122
|
+
? parseLongTextReviewBlocks(previewText, { preserveParagraphLineBreaks: true })
|
|
123
|
+
: null,
|
|
124
|
+
questions: hasQuestions ? questionInput.questions : [],
|
|
125
|
+
displayAt: displayTime(message.at)
|
|
126
|
+
};
|
|
127
|
+
if (normalizedCacheKey) {
|
|
128
|
+
if (
|
|
129
|
+
displayMessageCache.size >= DISPLAY_MESSAGE_CACHE_LIMIT &&
|
|
130
|
+
!displayMessageCache.has(normalizedCacheKey)
|
|
131
|
+
) {
|
|
132
|
+
displayMessageCache.delete(displayMessageCache.keys().next().value);
|
|
133
|
+
}
|
|
134
|
+
displayMessageCache.set(normalizedCacheKey, {
|
|
135
|
+
allowNumberedQuestions,
|
|
136
|
+
attachments: message.attachments,
|
|
137
|
+
at: message.at,
|
|
138
|
+
locale: activeLocale,
|
|
139
|
+
messageId: message.messageId,
|
|
140
|
+
preserveParagraphLineBreaks,
|
|
141
|
+
previewUserMessage,
|
|
142
|
+
role: message.role,
|
|
143
|
+
text: message.text,
|
|
144
|
+
value
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return { ...message, ...value };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function displayThinkingMessage(message = null) {
|
|
151
|
+
if (!message) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const text = normalizeThinkingMessageText(message.text);
|
|
155
|
+
if (!text) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
...message,
|
|
160
|
+
text,
|
|
161
|
+
displayAt: displayTime(message.at)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Storage rows are not visual boundaries. Only an intervening message ends a
|
|
166
|
+
// reasoning group, including when history is prepended or execution continues.
|
|
167
|
+
const displayEntries = computed(() => {
|
|
168
|
+
const candidates = [];
|
|
169
|
+
for (const [index, turn] of props.turns.entries()) {
|
|
170
|
+
const turnId = String(turn.turnId || index + 1);
|
|
171
|
+
// A delivery notice belongs to its exact user message, not the previous reply.
|
|
172
|
+
const userDelivery = turn.user && turn.system?.delivery && turn.system.delivery.messageId === turn.user.messageId
|
|
173
|
+
? turn.system : null;
|
|
174
|
+
const messages = [
|
|
175
|
+
turn.system && !userDelivery && { ...turn.system, role: "system" },
|
|
176
|
+
turn.user && { ...turn.user, role: "user" },
|
|
177
|
+
...conversationAgentMessages(turn)
|
|
178
|
+
].filter(Boolean);
|
|
179
|
+
for (const [messageIndex, message] of messages.entries()) {
|
|
180
|
+
const role = String(message.role || "").trim();
|
|
181
|
+
const key = `${turnId}:${conversationMessageKey(message, messageIndex)}`;
|
|
182
|
+
const displayed = role === "thinking" ? displayThinkingMessage(message) : displayMessage(message, {
|
|
183
|
+
allowNumberedQuestions: role !== "user" && role !== "system",
|
|
184
|
+
preserveParagraphLineBreaks: role === "user",
|
|
185
|
+
previewUserMessage: role === "user"
|
|
186
|
+
}, key);
|
|
187
|
+
if (!displayed) continue;
|
|
188
|
+
candidates.push({ key, role, turn: { ...turn, turnId }, message: displayed,
|
|
189
|
+
sortAt: turn.optimistic && turn.optimistic.status !== "accepted" && turn.optimistic.id === message.messageId
|
|
190
|
+
? NaN : Date.parse(message.at),
|
|
191
|
+
delivery: role === "user" ? userDelivery : null });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Native streaming turns and saved authored turns can arrive independently.
|
|
195
|
+
// Local queued timestamps can precede an earlier message's native admission.
|
|
196
|
+
// Keep unconfirmed messages in their supplied positions, along with undated
|
|
197
|
+
// notices. Accepted local copies join chronology even before history arrives.
|
|
198
|
+
const dated = candidates.filter(entry => Number.isFinite(entry.sortAt))
|
|
199
|
+
.sort((a, b) => a.sortAt - b.sortAt)[Symbol.iterator]();
|
|
200
|
+
const entries = [];
|
|
201
|
+
for (const candidate of candidates) {
|
|
202
|
+
const entry = Number.isFinite(candidate.sortAt) ? dated.next().value : candidate;
|
|
203
|
+
const previous = entries.at(-1);
|
|
204
|
+
if (entry.role === "thinking" && previous?.role === "thinking") previous.messages.push({ ...entry.message, key: entry.key });
|
|
205
|
+
else entries.push({ ...entry, ...(entry.role === "thinking" ? { messages: [{ ...entry.message, key: entry.key }] } : {}) });
|
|
206
|
+
}
|
|
207
|
+
// Anchor a progress group to the following message, or to the transcript end.
|
|
208
|
+
// Appending progress or prepending history then preserves its expansion state.
|
|
209
|
+
for (const [index, entry] of entries.entries()) {
|
|
210
|
+
if (entry.role === "thinking") entry.key = `progress:${entries[index + 1]?.key || "tail"}`;
|
|
211
|
+
}
|
|
212
|
+
return entries;
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
function clearDisplayCache() {
|
|
216
|
+
displayMessageCache.clear();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
clearDisplayCache,
|
|
221
|
+
displayEntries
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { useTranscriptEntries, userMessagePreviewText };
|