@anuma/sdk 6.3.1 → 6.4.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.
- package/dist/expo/index.cjs +190 -4
- package/dist/expo/index.d.mts +48 -0
- package/dist/expo/index.d.ts +48 -0
- package/dist/expo/index.mjs +190 -4
- package/dist/react/index.cjs +141 -30
- package/dist/react/index.d.mts +40 -0
- package/dist/react/index.d.ts +40 -0
- package/dist/react/index.mjs +141 -30
- package/package.json +1 -1
package/dist/expo/index.cjs
CHANGED
|
@@ -14092,6 +14092,153 @@ async function cleanupConversationSummary(database, conversationId) {
|
|
|
14092
14092
|
}
|
|
14093
14093
|
}
|
|
14094
14094
|
|
|
14095
|
+
// src/lib/chat/toolResultMessage.ts
|
|
14096
|
+
var HEADER = "[Tool Execution Results]\n\n";
|
|
14097
|
+
var FOOTER = "\n\nBased on these results, continue with the task.";
|
|
14098
|
+
var ENTRY_SEPARATOR = "\n\n";
|
|
14099
|
+
var TOOL_RESULT_FOOTER_LINE = FOOTER.trim();
|
|
14100
|
+
var MAX_PERSISTED_TOOL_RESULT_CHARS = 1e5;
|
|
14101
|
+
function buildToolResultContent(results) {
|
|
14102
|
+
const entries = results.map((r) => `Tool "${r.name}" returned: ${JSON.stringify(r.result)}`);
|
|
14103
|
+
const framing = HEADER.length + FOOTER.length + ENTRY_SEPARATOR.length * Math.max(0, entries.length - 1);
|
|
14104
|
+
const summary = capEntries(entries, MAX_PERSISTED_TOOL_RESULT_CHARS - framing).join(
|
|
14105
|
+
ENTRY_SEPARATOR
|
|
14106
|
+
);
|
|
14107
|
+
return `${HEADER}${summary}${FOOTER}`;
|
|
14108
|
+
}
|
|
14109
|
+
function capToolResultEntries(entries, budget) {
|
|
14110
|
+
return capEntries(entries, budget);
|
|
14111
|
+
}
|
|
14112
|
+
function capEntries(entries, budget) {
|
|
14113
|
+
const smallestFirst = entries.map((_, index) => index).sort((a, b) => entries[a].length - entries[b].length);
|
|
14114
|
+
const capped = [...entries];
|
|
14115
|
+
let remaining = budget;
|
|
14116
|
+
let unassigned = entries.length;
|
|
14117
|
+
for (const index of smallestFirst) {
|
|
14118
|
+
capped[index] = truncateEntry(entries[index], Math.floor(remaining / unassigned));
|
|
14119
|
+
remaining -= capped[index].length;
|
|
14120
|
+
unassigned--;
|
|
14121
|
+
}
|
|
14122
|
+
return capped;
|
|
14123
|
+
}
|
|
14124
|
+
function truncateEntry(entry, allowance) {
|
|
14125
|
+
if (entry.length <= allowance) return entry;
|
|
14126
|
+
const keep = allowance - truncationMarker(entry.length).length;
|
|
14127
|
+
if (keep <= 0) return entry.slice(0, Math.max(0, allowance));
|
|
14128
|
+
return `${entry.slice(0, keep)}${truncationMarker(entry.length - keep)}`;
|
|
14129
|
+
}
|
|
14130
|
+
function truncationMarker(omitted) {
|
|
14131
|
+
return `
|
|
14132
|
+
|
|
14133
|
+
... (tool output truncated, ${omitted} characters omitted)`;
|
|
14134
|
+
}
|
|
14135
|
+
|
|
14136
|
+
// src/lib/chat/toolResults.ts
|
|
14137
|
+
var TOOL_RESULTS_PREFIX = "[Tool Execution Results]";
|
|
14138
|
+
var DISPLAY_CARD_PLACEHOLDER = "[displayed a card to the user with the tool result]";
|
|
14139
|
+
var TOOL_LINE = /^Tool "([^"]+)" returned: (.*)$/;
|
|
14140
|
+
var TOOL_RESULT_ORIGIN = "tool_result";
|
|
14141
|
+
function isToolResultsRow(row) {
|
|
14142
|
+
if (row.origin === TOOL_RESULT_ORIGIN) return true;
|
|
14143
|
+
if (typeof row.origin === "string") return false;
|
|
14144
|
+
if (row.role !== "user" || !row.content.startsWith(TOOL_RESULTS_PREFIX)) return false;
|
|
14145
|
+
return parseToolResultSegments(row.content).length > 0;
|
|
14146
|
+
}
|
|
14147
|
+
function parseToolResultSegments(content) {
|
|
14148
|
+
const segments = [];
|
|
14149
|
+
for (const raw of content.split("\n")) {
|
|
14150
|
+
const line = raw.trim();
|
|
14151
|
+
const match = TOOL_LINE.exec(line);
|
|
14152
|
+
if (match) {
|
|
14153
|
+
segments.push({ name: match[1], line });
|
|
14154
|
+
continue;
|
|
14155
|
+
}
|
|
14156
|
+
const current = segments[segments.length - 1];
|
|
14157
|
+
if (!current || line === "" || line === TOOL_RESULT_FOOTER_LINE) continue;
|
|
14158
|
+
if (line === TOOL_RESULTS_PREFIX) continue;
|
|
14159
|
+
current.line = `${current.line}
|
|
14160
|
+
${line}`;
|
|
14161
|
+
}
|
|
14162
|
+
return segments;
|
|
14163
|
+
}
|
|
14164
|
+
function prepareToolResultsForReplay(rows, options) {
|
|
14165
|
+
if (options.fold) {
|
|
14166
|
+
return foldToolResultsRows(rows, {
|
|
14167
|
+
exclude: options.exclude,
|
|
14168
|
+
placeholder: options.placeholder
|
|
14169
|
+
});
|
|
14170
|
+
}
|
|
14171
|
+
return rows.filter((row) => !isToolResultsRow(row));
|
|
14172
|
+
}
|
|
14173
|
+
function foldToolResultsRows(rows, options) {
|
|
14174
|
+
const exclude = new Set(options?.exclude ?? []);
|
|
14175
|
+
const out = [];
|
|
14176
|
+
const pending = [];
|
|
14177
|
+
const assistantIndexById = /* @__PURE__ */ new Map();
|
|
14178
|
+
for (const row of rows) {
|
|
14179
|
+
if (isToolResultsRow(row)) {
|
|
14180
|
+
pending.push({ row, insertionPoint: out.length });
|
|
14181
|
+
continue;
|
|
14182
|
+
}
|
|
14183
|
+
out.push(row);
|
|
14184
|
+
if (row.role === "assistant" && row.uniqueId) {
|
|
14185
|
+
assistantIndexById.set(row.uniqueId, out.length - 1);
|
|
14186
|
+
}
|
|
14187
|
+
}
|
|
14188
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
14189
|
+
for (const { row, insertionPoint } of pending) {
|
|
14190
|
+
const target = resolveFoldTarget(out, row, insertionPoint, assistantIndexById);
|
|
14191
|
+
if (target === void 0) continue;
|
|
14192
|
+
const group = byTarget.get(target);
|
|
14193
|
+
if (group) group.push(row);
|
|
14194
|
+
else byTarget.set(target, [row]);
|
|
14195
|
+
}
|
|
14196
|
+
for (const [target, groupedRows] of byTarget) {
|
|
14197
|
+
const assistant = out[target];
|
|
14198
|
+
const kept = groupedRows.flatMap((row) => parseToolResultSegments(row.content)).filter((segment) => !exclude.has(segment.name));
|
|
14199
|
+
if (kept.length > 0) {
|
|
14200
|
+
out[target] = { ...assistant, content: appendToolResults(assistant.content, kept) };
|
|
14201
|
+
} else if (!assistant.content.trim() && options?.placeholder) {
|
|
14202
|
+
out[target] = { ...assistant, content: options.placeholder };
|
|
14203
|
+
}
|
|
14204
|
+
}
|
|
14205
|
+
return out;
|
|
14206
|
+
}
|
|
14207
|
+
function resolveFoldTarget(out, row, insertionPoint, assistantIndexById) {
|
|
14208
|
+
const linked = row.parentMessageId ? assistantIndexById.get(row.parentMessageId) : void 0;
|
|
14209
|
+
if (linked !== void 0) return linked;
|
|
14210
|
+
let previous;
|
|
14211
|
+
for (let i = insertionPoint - 1; i >= 0; i--) {
|
|
14212
|
+
if (out[i].role === "assistant") {
|
|
14213
|
+
previous = i;
|
|
14214
|
+
break;
|
|
14215
|
+
}
|
|
14216
|
+
}
|
|
14217
|
+
let next;
|
|
14218
|
+
for (let i = insertionPoint; i < out.length; i++) {
|
|
14219
|
+
if (out[i].role === "assistant") {
|
|
14220
|
+
next = i;
|
|
14221
|
+
break;
|
|
14222
|
+
}
|
|
14223
|
+
}
|
|
14224
|
+
if (previous === void 0) return next;
|
|
14225
|
+
if (next === void 0) return previous;
|
|
14226
|
+
return next - insertionPoint < insertionPoint - previous ? next : previous;
|
|
14227
|
+
}
|
|
14228
|
+
var MAX_FOLDED_APPENDIX_CHARS = 2e4;
|
|
14229
|
+
function appendToolResults(assistantContent, kept) {
|
|
14230
|
+
const framing = TOOL_RESULTS_PREFIX.length + 1 + (kept.length - 1);
|
|
14231
|
+
const capped = capToolResultEntries(
|
|
14232
|
+
kept.map((segment) => segment.line),
|
|
14233
|
+
MAX_FOLDED_APPENDIX_CHARS - framing
|
|
14234
|
+
);
|
|
14235
|
+
const appendix = `${TOOL_RESULTS_PREFIX}
|
|
14236
|
+
${capped.join("\n")}`;
|
|
14237
|
+
return assistantContent.trim() ? `${assistantContent}
|
|
14238
|
+
|
|
14239
|
+
${appendix}` : appendix;
|
|
14240
|
+
}
|
|
14241
|
+
|
|
14095
14242
|
// src/lib/db/chat/lazyDecrypt.ts
|
|
14096
14243
|
init_useEncryption();
|
|
14097
14244
|
init_encryption_utils();
|
|
@@ -19749,11 +19896,17 @@ function useChatStorage(options) {
|
|
|
19749
19896
|
onPiiRedacted,
|
|
19750
19897
|
nerDetector,
|
|
19751
19898
|
onServerToolCall,
|
|
19752
|
-
onToolCallArgumentsDelta
|
|
19899
|
+
onToolCallArgumentsDelta,
|
|
19900
|
+
toolResultsHistoryExclude,
|
|
19901
|
+
foldToolResultsInHistory
|
|
19753
19902
|
} = options;
|
|
19754
19903
|
const [currentConversationId, setCurrentConversationId] = (0, import_react4.useState)(
|
|
19755
19904
|
initialConversationId || null
|
|
19756
19905
|
);
|
|
19906
|
+
const toolResultsHistoryExcludeRef = (0, import_react4.useRef)(toolResultsHistoryExclude);
|
|
19907
|
+
toolResultsHistoryExcludeRef.current = toolResultsHistoryExclude;
|
|
19908
|
+
const foldToolResultsInHistoryRef = (0, import_react4.useRef)(foldToolResultsInHistory);
|
|
19909
|
+
foldToolResultsInHistoryRef.current = foldToolResultsInHistory;
|
|
19757
19910
|
const nerDetectorRef = (0, import_react4.useRef)(nerDetector);
|
|
19758
19911
|
nerDetectorRef.current = nerDetector;
|
|
19759
19912
|
const resolvedPiiRedaction = (0, import_react4.useMemo)(
|
|
@@ -20621,7 +20774,13 @@ function useChatStorage(options) {
|
|
|
20621
20774
|
}
|
|
20622
20775
|
if (includeHistory) {
|
|
20623
20776
|
const validMessages = storedMessages.filter((msg) => !msg.error);
|
|
20624
|
-
const
|
|
20777
|
+
const replayableMessages = prepareToolResultsForReplay(validMessages, {
|
|
20778
|
+
fold: foldToolResultsInHistoryRef.current === true,
|
|
20779
|
+
exclude: toolResultsHistoryExcludeRef.current,
|
|
20780
|
+
placeholder: DISPLAY_CARD_PLACEHOLDER
|
|
20781
|
+
});
|
|
20782
|
+
const limitedMessages = replayableMessages.slice(-maxHistoryMessages);
|
|
20783
|
+
const foldedHistory = limitedMessages;
|
|
20625
20784
|
if (summarizeHistory && !getTokenRef.current) {
|
|
20626
20785
|
getLogger().warn(
|
|
20627
20786
|
"[summarize] summarizeHistory is enabled but getToken is not provided \u2014 summarization will be skipped"
|
|
@@ -20631,7 +20790,7 @@ function useChatStorage(options) {
|
|
|
20631
20790
|
const { messagesToConvert, summarySystemMessage } = await maybeSummarizeHistory({
|
|
20632
20791
|
database,
|
|
20633
20792
|
conversationId: convId,
|
|
20634
|
-
messages:
|
|
20793
|
+
messages: foldedHistory,
|
|
20635
20794
|
summarizeHistory,
|
|
20636
20795
|
summaryTokenThreshold,
|
|
20637
20796
|
summaryMinWindowMessages,
|
|
@@ -21001,6 +21160,7 @@ function useChatStorage(options) {
|
|
|
21001
21160
|
uniqueId: effectiveAssistantUniqueId
|
|
21002
21161
|
};
|
|
21003
21162
|
let storedAssistantMessage;
|
|
21163
|
+
let assistantMsgQueueId;
|
|
21004
21164
|
try {
|
|
21005
21165
|
const assistantMsgResult = await writeOrQueue(
|
|
21006
21166
|
"createMessage",
|
|
@@ -21010,6 +21170,7 @@ function useChatStorage(options) {
|
|
|
21010
21170
|
userMsgQueueId ? [userMsgQueueId] : []
|
|
21011
21171
|
);
|
|
21012
21172
|
storedAssistantMessage = assistantMsgResult.result;
|
|
21173
|
+
assistantMsgQueueId = assistantMsgResult.queueId;
|
|
21013
21174
|
if (!assistantMsgResult.queued) {
|
|
21014
21175
|
void embedMessageAsync(storedAssistantMessage, maskForCall);
|
|
21015
21176
|
}
|
|
@@ -21020,6 +21181,29 @@ function useChatStorage(options) {
|
|
|
21020
21181
|
userMessage: storedUserMessage
|
|
21021
21182
|
};
|
|
21022
21183
|
}
|
|
21184
|
+
const autoToolResults = result.autoExecutedToolResults;
|
|
21185
|
+
let storedToolResultsMessage;
|
|
21186
|
+
if (autoToolResults && autoToolResults.length > 0) {
|
|
21187
|
+
const toolResultsOpts = {
|
|
21188
|
+
conversationId: convId,
|
|
21189
|
+
role: "user",
|
|
21190
|
+
content: buildToolResultContent(autoToolResults),
|
|
21191
|
+
model: "",
|
|
21192
|
+
parentMessageId: storedAssistantMessage.uniqueId,
|
|
21193
|
+
origin: TOOL_RESULT_ORIGIN
|
|
21194
|
+
};
|
|
21195
|
+
try {
|
|
21196
|
+
const toolResultsWrite = await writeOrQueue(
|
|
21197
|
+
"createMessage",
|
|
21198
|
+
toolResultsOpts,
|
|
21199
|
+
() => createMessageOp(storageCtx, toolResultsOpts),
|
|
21200
|
+
() => makeSyntheticStoredMessage(toolResultsOpts),
|
|
21201
|
+
assistantMsgQueueId ? [assistantMsgQueueId] : []
|
|
21202
|
+
);
|
|
21203
|
+
storedToolResultsMessage = toolResultsWrite.result;
|
|
21204
|
+
} catch {
|
|
21205
|
+
}
|
|
21206
|
+
}
|
|
21023
21207
|
const refreshGetToken = getTokenRef.current;
|
|
21024
21208
|
if (refreshGetToken) {
|
|
21025
21209
|
Promise.resolve().then(() => shouldRefreshTools(getToolsChecksum(responseData), serverToolsConfig?.cache)).then((refresh) => {
|
|
@@ -21039,7 +21223,9 @@ function useChatStorage(options) {
|
|
|
21039
21223
|
data: responseData,
|
|
21040
21224
|
error: null,
|
|
21041
21225
|
userMessage: storedUserMessage,
|
|
21042
|
-
assistantMessage: storedAssistantMessage
|
|
21226
|
+
assistantMessage: storedAssistantMessage,
|
|
21227
|
+
autoExecutedToolResults: autoToolResults,
|
|
21228
|
+
toolResultsMessage: storedToolResultsMessage
|
|
21043
21229
|
};
|
|
21044
21230
|
},
|
|
21045
21231
|
[
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -3777,6 +3777,40 @@ interface BaseUseChatStorageOptions {
|
|
|
3777
3777
|
* Defaults to the hardcoded MCP_R2_DOMAIN from clientConfig.
|
|
3778
3778
|
*/
|
|
3779
3779
|
mcpR2Domain?: string;
|
|
3780
|
+
/**
|
|
3781
|
+
* Tool names whose persisted results must never be replayed to the model.
|
|
3782
|
+
*
|
|
3783
|
+
* A turn's auto-executed tool results are stored as a synthetic
|
|
3784
|
+
* `[Tool Execution Results]` row and, on a replayed send, folded back onto the
|
|
3785
|
+
* assistant turn they belong to — that is what lets a follow-up question about a
|
|
3786
|
+
* tool's output work after a reload. Name a tool here when its payload exists for
|
|
3787
|
+
* the RENDERER rather than the model: a display card can carry data the model was
|
|
3788
|
+
* deliberately never given (People Nearby's card holds third parties' snapped
|
|
3789
|
+
* coordinates, which the search result strips), and replaying it would hand that
|
|
3790
|
+
* data straight back.
|
|
3791
|
+
*
|
|
3792
|
+
* Hook-level rather than per-send on purpose: an exclusion that has to be
|
|
3793
|
+
* remembered at every call site is one bad send away from leaking.
|
|
3794
|
+
*/
|
|
3795
|
+
toolResultsHistoryExclude?: string[];
|
|
3796
|
+
/**
|
|
3797
|
+
* Fold persisted `[Tool Execution Results]` rows onto the assistant turn that produced them
|
|
3798
|
+
* when replaying stored history, instead of dropping them.
|
|
3799
|
+
*
|
|
3800
|
+
* **Defaults to `false`, and that default is deliberate.** Folding is the better behaviour —
|
|
3801
|
+
* it is what lets a follow-up about a tool's output work after a reload — but it moves the
|
|
3802
|
+
* payload from a `role: "user"` row onto an `assistant` row. Any consumer that scrubs these
|
|
3803
|
+
* rows by checking `role === "user"` plus the content prefix (which is how both apps did it
|
|
3804
|
+
* before this option existed) stops catching them the moment folding turns on, and starts
|
|
3805
|
+
* replaying whatever the row held. Opting in is therefore a statement that the caller has
|
|
3806
|
+
* checked its own filters and set {@link toolResultsHistoryExclude} for any payload that must
|
|
3807
|
+
* not reach the model.
|
|
3808
|
+
*
|
|
3809
|
+
* With it off, rows are dropped from the replayed history rather than sent verbatim. Verbatim
|
|
3810
|
+
* would put two consecutive `user` turns on the wire, and the model answers the previous turn
|
|
3811
|
+
* instead of the new prompt.
|
|
3812
|
+
*/
|
|
3813
|
+
foldToolResultsInHistory?: boolean;
|
|
3780
3814
|
/**
|
|
3781
3815
|
* Pre-processors run after the last user message is received but before
|
|
3782
3816
|
* the first LLM request. Each receives the prompt text and a shared
|
|
@@ -4126,6 +4160,20 @@ interface BaseSendMessageSuccessResult {
|
|
|
4126
4160
|
error: null;
|
|
4127
4161
|
userMessage: StoredMessage;
|
|
4128
4162
|
assistantMessage: StoredMessage;
|
|
4163
|
+
/** Results from tools the SDK auto-executed this turn (e.g. display tools). */
|
|
4164
|
+
autoExecutedToolResults?: {
|
|
4165
|
+
name: string;
|
|
4166
|
+
result: unknown;
|
|
4167
|
+
}[];
|
|
4168
|
+
/**
|
|
4169
|
+
* The synthetic `[Tool Execution Results]` row those results were persisted as.
|
|
4170
|
+
*
|
|
4171
|
+
* Returned so a caller can key its transient overlay on the id the SDK actually wrote instead of
|
|
4172
|
+
* deriving one and hoping the two agree — that guesswork is what mobile's per-card persist
|
|
4173
|
+
* workarounds existed to do (#5519). Absent when the turn executed no tools, or when the row write
|
|
4174
|
+
* failed (non-fatal: the assistant reply is already stored).
|
|
4175
|
+
*/
|
|
4176
|
+
toolResultsMessage?: StoredMessage;
|
|
4129
4177
|
}
|
|
4130
4178
|
interface BaseSendMessageSkippedResult {
|
|
4131
4179
|
data: ApiResponse;
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -3777,6 +3777,40 @@ interface BaseUseChatStorageOptions {
|
|
|
3777
3777
|
* Defaults to the hardcoded MCP_R2_DOMAIN from clientConfig.
|
|
3778
3778
|
*/
|
|
3779
3779
|
mcpR2Domain?: string;
|
|
3780
|
+
/**
|
|
3781
|
+
* Tool names whose persisted results must never be replayed to the model.
|
|
3782
|
+
*
|
|
3783
|
+
* A turn's auto-executed tool results are stored as a synthetic
|
|
3784
|
+
* `[Tool Execution Results]` row and, on a replayed send, folded back onto the
|
|
3785
|
+
* assistant turn they belong to — that is what lets a follow-up question about a
|
|
3786
|
+
* tool's output work after a reload. Name a tool here when its payload exists for
|
|
3787
|
+
* the RENDERER rather than the model: a display card can carry data the model was
|
|
3788
|
+
* deliberately never given (People Nearby's card holds third parties' snapped
|
|
3789
|
+
* coordinates, which the search result strips), and replaying it would hand that
|
|
3790
|
+
* data straight back.
|
|
3791
|
+
*
|
|
3792
|
+
* Hook-level rather than per-send on purpose: an exclusion that has to be
|
|
3793
|
+
* remembered at every call site is one bad send away from leaking.
|
|
3794
|
+
*/
|
|
3795
|
+
toolResultsHistoryExclude?: string[];
|
|
3796
|
+
/**
|
|
3797
|
+
* Fold persisted `[Tool Execution Results]` rows onto the assistant turn that produced them
|
|
3798
|
+
* when replaying stored history, instead of dropping them.
|
|
3799
|
+
*
|
|
3800
|
+
* **Defaults to `false`, and that default is deliberate.** Folding is the better behaviour —
|
|
3801
|
+
* it is what lets a follow-up about a tool's output work after a reload — but it moves the
|
|
3802
|
+
* payload from a `role: "user"` row onto an `assistant` row. Any consumer that scrubs these
|
|
3803
|
+
* rows by checking `role === "user"` plus the content prefix (which is how both apps did it
|
|
3804
|
+
* before this option existed) stops catching them the moment folding turns on, and starts
|
|
3805
|
+
* replaying whatever the row held. Opting in is therefore a statement that the caller has
|
|
3806
|
+
* checked its own filters and set {@link toolResultsHistoryExclude} for any payload that must
|
|
3807
|
+
* not reach the model.
|
|
3808
|
+
*
|
|
3809
|
+
* With it off, rows are dropped from the replayed history rather than sent verbatim. Verbatim
|
|
3810
|
+
* would put two consecutive `user` turns on the wire, and the model answers the previous turn
|
|
3811
|
+
* instead of the new prompt.
|
|
3812
|
+
*/
|
|
3813
|
+
foldToolResultsInHistory?: boolean;
|
|
3780
3814
|
/**
|
|
3781
3815
|
* Pre-processors run after the last user message is received but before
|
|
3782
3816
|
* the first LLM request. Each receives the prompt text and a shared
|
|
@@ -4126,6 +4160,20 @@ interface BaseSendMessageSuccessResult {
|
|
|
4126
4160
|
error: null;
|
|
4127
4161
|
userMessage: StoredMessage;
|
|
4128
4162
|
assistantMessage: StoredMessage;
|
|
4163
|
+
/** Results from tools the SDK auto-executed this turn (e.g. display tools). */
|
|
4164
|
+
autoExecutedToolResults?: {
|
|
4165
|
+
name: string;
|
|
4166
|
+
result: unknown;
|
|
4167
|
+
}[];
|
|
4168
|
+
/**
|
|
4169
|
+
* The synthetic `[Tool Execution Results]` row those results were persisted as.
|
|
4170
|
+
*
|
|
4171
|
+
* Returned so a caller can key its transient overlay on the id the SDK actually wrote instead of
|
|
4172
|
+
* deriving one and hoping the two agree — that guesswork is what mobile's per-card persist
|
|
4173
|
+
* workarounds existed to do (#5519). Absent when the turn executed no tools, or when the row write
|
|
4174
|
+
* failed (non-fatal: the assistant reply is already stored).
|
|
4175
|
+
*/
|
|
4176
|
+
toolResultsMessage?: StoredMessage;
|
|
4129
4177
|
}
|
|
4130
4178
|
interface BaseSendMessageSkippedResult {
|
|
4131
4179
|
data: ApiResponse;
|
package/dist/expo/index.mjs
CHANGED
|
@@ -4986,6 +4986,153 @@ async function cleanupConversationSummary(database, conversationId) {
|
|
|
4986
4986
|
}
|
|
4987
4987
|
}
|
|
4988
4988
|
|
|
4989
|
+
// src/lib/chat/toolResultMessage.ts
|
|
4990
|
+
var HEADER = "[Tool Execution Results]\n\n";
|
|
4991
|
+
var FOOTER = "\n\nBased on these results, continue with the task.";
|
|
4992
|
+
var ENTRY_SEPARATOR = "\n\n";
|
|
4993
|
+
var TOOL_RESULT_FOOTER_LINE = FOOTER.trim();
|
|
4994
|
+
var MAX_PERSISTED_TOOL_RESULT_CHARS = 1e5;
|
|
4995
|
+
function buildToolResultContent(results) {
|
|
4996
|
+
const entries = results.map((r) => `Tool "${r.name}" returned: ${JSON.stringify(r.result)}`);
|
|
4997
|
+
const framing = HEADER.length + FOOTER.length + ENTRY_SEPARATOR.length * Math.max(0, entries.length - 1);
|
|
4998
|
+
const summary = capEntries(entries, MAX_PERSISTED_TOOL_RESULT_CHARS - framing).join(
|
|
4999
|
+
ENTRY_SEPARATOR
|
|
5000
|
+
);
|
|
5001
|
+
return `${HEADER}${summary}${FOOTER}`;
|
|
5002
|
+
}
|
|
5003
|
+
function capToolResultEntries(entries, budget) {
|
|
5004
|
+
return capEntries(entries, budget);
|
|
5005
|
+
}
|
|
5006
|
+
function capEntries(entries, budget) {
|
|
5007
|
+
const smallestFirst = entries.map((_, index) => index).sort((a, b) => entries[a].length - entries[b].length);
|
|
5008
|
+
const capped = [...entries];
|
|
5009
|
+
let remaining = budget;
|
|
5010
|
+
let unassigned = entries.length;
|
|
5011
|
+
for (const index of smallestFirst) {
|
|
5012
|
+
capped[index] = truncateEntry(entries[index], Math.floor(remaining / unassigned));
|
|
5013
|
+
remaining -= capped[index].length;
|
|
5014
|
+
unassigned--;
|
|
5015
|
+
}
|
|
5016
|
+
return capped;
|
|
5017
|
+
}
|
|
5018
|
+
function truncateEntry(entry, allowance) {
|
|
5019
|
+
if (entry.length <= allowance) return entry;
|
|
5020
|
+
const keep = allowance - truncationMarker(entry.length).length;
|
|
5021
|
+
if (keep <= 0) return entry.slice(0, Math.max(0, allowance));
|
|
5022
|
+
return `${entry.slice(0, keep)}${truncationMarker(entry.length - keep)}`;
|
|
5023
|
+
}
|
|
5024
|
+
function truncationMarker(omitted) {
|
|
5025
|
+
return `
|
|
5026
|
+
|
|
5027
|
+
... (tool output truncated, ${omitted} characters omitted)`;
|
|
5028
|
+
}
|
|
5029
|
+
|
|
5030
|
+
// src/lib/chat/toolResults.ts
|
|
5031
|
+
var TOOL_RESULTS_PREFIX = "[Tool Execution Results]";
|
|
5032
|
+
var DISPLAY_CARD_PLACEHOLDER = "[displayed a card to the user with the tool result]";
|
|
5033
|
+
var TOOL_LINE = /^Tool "([^"]+)" returned: (.*)$/;
|
|
5034
|
+
var TOOL_RESULT_ORIGIN = "tool_result";
|
|
5035
|
+
function isToolResultsRow(row) {
|
|
5036
|
+
if (row.origin === TOOL_RESULT_ORIGIN) return true;
|
|
5037
|
+
if (typeof row.origin === "string") return false;
|
|
5038
|
+
if (row.role !== "user" || !row.content.startsWith(TOOL_RESULTS_PREFIX)) return false;
|
|
5039
|
+
return parseToolResultSegments(row.content).length > 0;
|
|
5040
|
+
}
|
|
5041
|
+
function parseToolResultSegments(content) {
|
|
5042
|
+
const segments = [];
|
|
5043
|
+
for (const raw of content.split("\n")) {
|
|
5044
|
+
const line = raw.trim();
|
|
5045
|
+
const match = TOOL_LINE.exec(line);
|
|
5046
|
+
if (match) {
|
|
5047
|
+
segments.push({ name: match[1], line });
|
|
5048
|
+
continue;
|
|
5049
|
+
}
|
|
5050
|
+
const current = segments[segments.length - 1];
|
|
5051
|
+
if (!current || line === "" || line === TOOL_RESULT_FOOTER_LINE) continue;
|
|
5052
|
+
if (line === TOOL_RESULTS_PREFIX) continue;
|
|
5053
|
+
current.line = `${current.line}
|
|
5054
|
+
${line}`;
|
|
5055
|
+
}
|
|
5056
|
+
return segments;
|
|
5057
|
+
}
|
|
5058
|
+
function prepareToolResultsForReplay(rows, options) {
|
|
5059
|
+
if (options.fold) {
|
|
5060
|
+
return foldToolResultsRows(rows, {
|
|
5061
|
+
exclude: options.exclude,
|
|
5062
|
+
placeholder: options.placeholder
|
|
5063
|
+
});
|
|
5064
|
+
}
|
|
5065
|
+
return rows.filter((row) => !isToolResultsRow(row));
|
|
5066
|
+
}
|
|
5067
|
+
function foldToolResultsRows(rows, options) {
|
|
5068
|
+
const exclude = new Set(options?.exclude ?? []);
|
|
5069
|
+
const out = [];
|
|
5070
|
+
const pending = [];
|
|
5071
|
+
const assistantIndexById = /* @__PURE__ */ new Map();
|
|
5072
|
+
for (const row of rows) {
|
|
5073
|
+
if (isToolResultsRow(row)) {
|
|
5074
|
+
pending.push({ row, insertionPoint: out.length });
|
|
5075
|
+
continue;
|
|
5076
|
+
}
|
|
5077
|
+
out.push(row);
|
|
5078
|
+
if (row.role === "assistant" && row.uniqueId) {
|
|
5079
|
+
assistantIndexById.set(row.uniqueId, out.length - 1);
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
5083
|
+
for (const { row, insertionPoint } of pending) {
|
|
5084
|
+
const target = resolveFoldTarget(out, row, insertionPoint, assistantIndexById);
|
|
5085
|
+
if (target === void 0) continue;
|
|
5086
|
+
const group = byTarget.get(target);
|
|
5087
|
+
if (group) group.push(row);
|
|
5088
|
+
else byTarget.set(target, [row]);
|
|
5089
|
+
}
|
|
5090
|
+
for (const [target, groupedRows] of byTarget) {
|
|
5091
|
+
const assistant = out[target];
|
|
5092
|
+
const kept = groupedRows.flatMap((row) => parseToolResultSegments(row.content)).filter((segment) => !exclude.has(segment.name));
|
|
5093
|
+
if (kept.length > 0) {
|
|
5094
|
+
out[target] = { ...assistant, content: appendToolResults(assistant.content, kept) };
|
|
5095
|
+
} else if (!assistant.content.trim() && options?.placeholder) {
|
|
5096
|
+
out[target] = { ...assistant, content: options.placeholder };
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5099
|
+
return out;
|
|
5100
|
+
}
|
|
5101
|
+
function resolveFoldTarget(out, row, insertionPoint, assistantIndexById) {
|
|
5102
|
+
const linked = row.parentMessageId ? assistantIndexById.get(row.parentMessageId) : void 0;
|
|
5103
|
+
if (linked !== void 0) return linked;
|
|
5104
|
+
let previous;
|
|
5105
|
+
for (let i = insertionPoint - 1; i >= 0; i--) {
|
|
5106
|
+
if (out[i].role === "assistant") {
|
|
5107
|
+
previous = i;
|
|
5108
|
+
break;
|
|
5109
|
+
}
|
|
5110
|
+
}
|
|
5111
|
+
let next;
|
|
5112
|
+
for (let i = insertionPoint; i < out.length; i++) {
|
|
5113
|
+
if (out[i].role === "assistant") {
|
|
5114
|
+
next = i;
|
|
5115
|
+
break;
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
if (previous === void 0) return next;
|
|
5119
|
+
if (next === void 0) return previous;
|
|
5120
|
+
return next - insertionPoint < insertionPoint - previous ? next : previous;
|
|
5121
|
+
}
|
|
5122
|
+
var MAX_FOLDED_APPENDIX_CHARS = 2e4;
|
|
5123
|
+
function appendToolResults(assistantContent, kept) {
|
|
5124
|
+
const framing = TOOL_RESULTS_PREFIX.length + 1 + (kept.length - 1);
|
|
5125
|
+
const capped = capToolResultEntries(
|
|
5126
|
+
kept.map((segment) => segment.line),
|
|
5127
|
+
MAX_FOLDED_APPENDIX_CHARS - framing
|
|
5128
|
+
);
|
|
5129
|
+
const appendix = `${TOOL_RESULTS_PREFIX}
|
|
5130
|
+
${capped.join("\n")}`;
|
|
5131
|
+
return assistantContent.trim() ? `${assistantContent}
|
|
5132
|
+
|
|
5133
|
+
${appendix}` : appendix;
|
|
5134
|
+
}
|
|
5135
|
+
|
|
4989
5136
|
// src/lib/db/chat/lazyDecrypt.ts
|
|
4990
5137
|
var LRU_CAPACITY = 256;
|
|
4991
5138
|
var titleCache = /* @__PURE__ */ new Map();
|
|
@@ -10533,11 +10680,17 @@ function useChatStorage(options) {
|
|
|
10533
10680
|
onPiiRedacted,
|
|
10534
10681
|
nerDetector,
|
|
10535
10682
|
onServerToolCall,
|
|
10536
|
-
onToolCallArgumentsDelta
|
|
10683
|
+
onToolCallArgumentsDelta,
|
|
10684
|
+
toolResultsHistoryExclude,
|
|
10685
|
+
foldToolResultsInHistory
|
|
10537
10686
|
} = options;
|
|
10538
10687
|
const [currentConversationId, setCurrentConversationId] = useState4(
|
|
10539
10688
|
initialConversationId || null
|
|
10540
10689
|
);
|
|
10690
|
+
const toolResultsHistoryExcludeRef = useRef4(toolResultsHistoryExclude);
|
|
10691
|
+
toolResultsHistoryExcludeRef.current = toolResultsHistoryExclude;
|
|
10692
|
+
const foldToolResultsInHistoryRef = useRef4(foldToolResultsInHistory);
|
|
10693
|
+
foldToolResultsInHistoryRef.current = foldToolResultsInHistory;
|
|
10541
10694
|
const nerDetectorRef = useRef4(nerDetector);
|
|
10542
10695
|
nerDetectorRef.current = nerDetector;
|
|
10543
10696
|
const resolvedPiiRedaction = useMemo(
|
|
@@ -11405,7 +11558,13 @@ function useChatStorage(options) {
|
|
|
11405
11558
|
}
|
|
11406
11559
|
if (includeHistory) {
|
|
11407
11560
|
const validMessages = storedMessages.filter((msg) => !msg.error);
|
|
11408
|
-
const
|
|
11561
|
+
const replayableMessages = prepareToolResultsForReplay(validMessages, {
|
|
11562
|
+
fold: foldToolResultsInHistoryRef.current === true,
|
|
11563
|
+
exclude: toolResultsHistoryExcludeRef.current,
|
|
11564
|
+
placeholder: DISPLAY_CARD_PLACEHOLDER
|
|
11565
|
+
});
|
|
11566
|
+
const limitedMessages = replayableMessages.slice(-maxHistoryMessages);
|
|
11567
|
+
const foldedHistory = limitedMessages;
|
|
11409
11568
|
if (summarizeHistory && !getTokenRef.current) {
|
|
11410
11569
|
getLogger().warn(
|
|
11411
11570
|
"[summarize] summarizeHistory is enabled but getToken is not provided \u2014 summarization will be skipped"
|
|
@@ -11415,7 +11574,7 @@ function useChatStorage(options) {
|
|
|
11415
11574
|
const { messagesToConvert, summarySystemMessage } = await maybeSummarizeHistory({
|
|
11416
11575
|
database,
|
|
11417
11576
|
conversationId: convId,
|
|
11418
|
-
messages:
|
|
11577
|
+
messages: foldedHistory,
|
|
11419
11578
|
summarizeHistory,
|
|
11420
11579
|
summaryTokenThreshold,
|
|
11421
11580
|
summaryMinWindowMessages,
|
|
@@ -11785,6 +11944,7 @@ function useChatStorage(options) {
|
|
|
11785
11944
|
uniqueId: effectiveAssistantUniqueId
|
|
11786
11945
|
};
|
|
11787
11946
|
let storedAssistantMessage;
|
|
11947
|
+
let assistantMsgQueueId;
|
|
11788
11948
|
try {
|
|
11789
11949
|
const assistantMsgResult = await writeOrQueue(
|
|
11790
11950
|
"createMessage",
|
|
@@ -11794,6 +11954,7 @@ function useChatStorage(options) {
|
|
|
11794
11954
|
userMsgQueueId ? [userMsgQueueId] : []
|
|
11795
11955
|
);
|
|
11796
11956
|
storedAssistantMessage = assistantMsgResult.result;
|
|
11957
|
+
assistantMsgQueueId = assistantMsgResult.queueId;
|
|
11797
11958
|
if (!assistantMsgResult.queued) {
|
|
11798
11959
|
void embedMessageAsync(storedAssistantMessage, maskForCall);
|
|
11799
11960
|
}
|
|
@@ -11804,6 +11965,29 @@ function useChatStorage(options) {
|
|
|
11804
11965
|
userMessage: storedUserMessage
|
|
11805
11966
|
};
|
|
11806
11967
|
}
|
|
11968
|
+
const autoToolResults = result.autoExecutedToolResults;
|
|
11969
|
+
let storedToolResultsMessage;
|
|
11970
|
+
if (autoToolResults && autoToolResults.length > 0) {
|
|
11971
|
+
const toolResultsOpts = {
|
|
11972
|
+
conversationId: convId,
|
|
11973
|
+
role: "user",
|
|
11974
|
+
content: buildToolResultContent(autoToolResults),
|
|
11975
|
+
model: "",
|
|
11976
|
+
parentMessageId: storedAssistantMessage.uniqueId,
|
|
11977
|
+
origin: TOOL_RESULT_ORIGIN
|
|
11978
|
+
};
|
|
11979
|
+
try {
|
|
11980
|
+
const toolResultsWrite = await writeOrQueue(
|
|
11981
|
+
"createMessage",
|
|
11982
|
+
toolResultsOpts,
|
|
11983
|
+
() => createMessageOp(storageCtx, toolResultsOpts),
|
|
11984
|
+
() => makeSyntheticStoredMessage(toolResultsOpts),
|
|
11985
|
+
assistantMsgQueueId ? [assistantMsgQueueId] : []
|
|
11986
|
+
);
|
|
11987
|
+
storedToolResultsMessage = toolResultsWrite.result;
|
|
11988
|
+
} catch {
|
|
11989
|
+
}
|
|
11990
|
+
}
|
|
11807
11991
|
const refreshGetToken = getTokenRef.current;
|
|
11808
11992
|
if (refreshGetToken) {
|
|
11809
11993
|
Promise.resolve().then(() => shouldRefreshTools(getToolsChecksum(responseData), serverToolsConfig?.cache)).then((refresh) => {
|
|
@@ -11823,7 +12007,9 @@ function useChatStorage(options) {
|
|
|
11823
12007
|
data: responseData,
|
|
11824
12008
|
error: null,
|
|
11825
12009
|
userMessage: storedUserMessage,
|
|
11826
|
-
assistantMessage: storedAssistantMessage
|
|
12010
|
+
assistantMessage: storedAssistantMessage,
|
|
12011
|
+
autoExecutedToolResults: autoToolResults,
|
|
12012
|
+
toolResultsMessage: storedToolResultsMessage
|
|
11827
12013
|
};
|
|
11828
12014
|
},
|
|
11829
12015
|
[
|
package/dist/react/index.cjs
CHANGED
|
@@ -14936,6 +14936,7 @@ async function cleanupConversationSummary(database, conversationId) {
|
|
|
14936
14936
|
var HEADER = "[Tool Execution Results]\n\n";
|
|
14937
14937
|
var FOOTER = "\n\nBased on these results, continue with the task.";
|
|
14938
14938
|
var ENTRY_SEPARATOR = "\n\n";
|
|
14939
|
+
var TOOL_RESULT_FOOTER_LINE = FOOTER.trim();
|
|
14939
14940
|
var MAX_PERSISTED_TOOL_RESULT_CHARS = 1e5;
|
|
14940
14941
|
function buildToolResultContent(results) {
|
|
14941
14942
|
const entries = results.map((r) => `Tool "${r.name}" returned: ${JSON.stringify(r.result)}`);
|
|
@@ -14945,6 +14946,9 @@ function buildToolResultContent(results) {
|
|
|
14945
14946
|
);
|
|
14946
14947
|
return `${HEADER}${summary}${FOOTER}`;
|
|
14947
14948
|
}
|
|
14949
|
+
function capToolResultEntries(entries, budget) {
|
|
14950
|
+
return capEntries(entries, budget);
|
|
14951
|
+
}
|
|
14948
14952
|
function capEntries(entries, budget) {
|
|
14949
14953
|
const smallestFirst = entries.map((_, index) => index).sort((a, b) => entries[a].length - entries[b].length);
|
|
14950
14954
|
const capped = [...entries];
|
|
@@ -14969,6 +14973,112 @@ function truncationMarker(omitted) {
|
|
|
14969
14973
|
... (tool output truncated, ${omitted} characters omitted)`;
|
|
14970
14974
|
}
|
|
14971
14975
|
|
|
14976
|
+
// src/lib/chat/toolResults.ts
|
|
14977
|
+
var TOOL_RESULTS_PREFIX = "[Tool Execution Results]";
|
|
14978
|
+
var DISPLAY_CARD_PLACEHOLDER = "[displayed a card to the user with the tool result]";
|
|
14979
|
+
var TOOL_LINE = /^Tool "([^"]+)" returned: (.*)$/;
|
|
14980
|
+
var TOOL_RESULT_ORIGIN = "tool_result";
|
|
14981
|
+
function isToolResultsRow(row) {
|
|
14982
|
+
if (row.origin === TOOL_RESULT_ORIGIN) return true;
|
|
14983
|
+
if (typeof row.origin === "string") return false;
|
|
14984
|
+
if (row.role !== "user" || !row.content.startsWith(TOOL_RESULTS_PREFIX)) return false;
|
|
14985
|
+
return parseToolResultSegments(row.content).length > 0;
|
|
14986
|
+
}
|
|
14987
|
+
function parseToolResultSegments(content) {
|
|
14988
|
+
const segments = [];
|
|
14989
|
+
for (const raw of content.split("\n")) {
|
|
14990
|
+
const line = raw.trim();
|
|
14991
|
+
const match = TOOL_LINE.exec(line);
|
|
14992
|
+
if (match) {
|
|
14993
|
+
segments.push({ name: match[1], line });
|
|
14994
|
+
continue;
|
|
14995
|
+
}
|
|
14996
|
+
const current = segments[segments.length - 1];
|
|
14997
|
+
if (!current || line === "" || line === TOOL_RESULT_FOOTER_LINE) continue;
|
|
14998
|
+
if (line === TOOL_RESULTS_PREFIX) continue;
|
|
14999
|
+
current.line = `${current.line}
|
|
15000
|
+
${line}`;
|
|
15001
|
+
}
|
|
15002
|
+
return segments;
|
|
15003
|
+
}
|
|
15004
|
+
function prepareToolResultsForReplay(rows, options) {
|
|
15005
|
+
if (options.fold) {
|
|
15006
|
+
return foldToolResultsRows(rows, {
|
|
15007
|
+
exclude: options.exclude,
|
|
15008
|
+
placeholder: options.placeholder
|
|
15009
|
+
});
|
|
15010
|
+
}
|
|
15011
|
+
return rows.filter((row) => !isToolResultsRow(row));
|
|
15012
|
+
}
|
|
15013
|
+
function foldToolResultsRows(rows, options) {
|
|
15014
|
+
const exclude = new Set(options?.exclude ?? []);
|
|
15015
|
+
const out = [];
|
|
15016
|
+
const pending = [];
|
|
15017
|
+
const assistantIndexById = /* @__PURE__ */ new Map();
|
|
15018
|
+
for (const row of rows) {
|
|
15019
|
+
if (isToolResultsRow(row)) {
|
|
15020
|
+
pending.push({ row, insertionPoint: out.length });
|
|
15021
|
+
continue;
|
|
15022
|
+
}
|
|
15023
|
+
out.push(row);
|
|
15024
|
+
if (row.role === "assistant" && row.uniqueId) {
|
|
15025
|
+
assistantIndexById.set(row.uniqueId, out.length - 1);
|
|
15026
|
+
}
|
|
15027
|
+
}
|
|
15028
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
15029
|
+
for (const { row, insertionPoint } of pending) {
|
|
15030
|
+
const target = resolveFoldTarget(out, row, insertionPoint, assistantIndexById);
|
|
15031
|
+
if (target === void 0) continue;
|
|
15032
|
+
const group = byTarget.get(target);
|
|
15033
|
+
if (group) group.push(row);
|
|
15034
|
+
else byTarget.set(target, [row]);
|
|
15035
|
+
}
|
|
15036
|
+
for (const [target, groupedRows] of byTarget) {
|
|
15037
|
+
const assistant = out[target];
|
|
15038
|
+
const kept = groupedRows.flatMap((row) => parseToolResultSegments(row.content)).filter((segment) => !exclude.has(segment.name));
|
|
15039
|
+
if (kept.length > 0) {
|
|
15040
|
+
out[target] = { ...assistant, content: appendToolResults(assistant.content, kept) };
|
|
15041
|
+
} else if (!assistant.content.trim() && options?.placeholder) {
|
|
15042
|
+
out[target] = { ...assistant, content: options.placeholder };
|
|
15043
|
+
}
|
|
15044
|
+
}
|
|
15045
|
+
return out;
|
|
15046
|
+
}
|
|
15047
|
+
function resolveFoldTarget(out, row, insertionPoint, assistantIndexById) {
|
|
15048
|
+
const linked = row.parentMessageId ? assistantIndexById.get(row.parentMessageId) : void 0;
|
|
15049
|
+
if (linked !== void 0) return linked;
|
|
15050
|
+
let previous;
|
|
15051
|
+
for (let i = insertionPoint - 1; i >= 0; i--) {
|
|
15052
|
+
if (out[i].role === "assistant") {
|
|
15053
|
+
previous = i;
|
|
15054
|
+
break;
|
|
15055
|
+
}
|
|
15056
|
+
}
|
|
15057
|
+
let next;
|
|
15058
|
+
for (let i = insertionPoint; i < out.length; i++) {
|
|
15059
|
+
if (out[i].role === "assistant") {
|
|
15060
|
+
next = i;
|
|
15061
|
+
break;
|
|
15062
|
+
}
|
|
15063
|
+
}
|
|
15064
|
+
if (previous === void 0) return next;
|
|
15065
|
+
if (next === void 0) return previous;
|
|
15066
|
+
return next - insertionPoint < insertionPoint - previous ? next : previous;
|
|
15067
|
+
}
|
|
15068
|
+
var MAX_FOLDED_APPENDIX_CHARS = 2e4;
|
|
15069
|
+
function appendToolResults(assistantContent, kept) {
|
|
15070
|
+
const framing = TOOL_RESULTS_PREFIX.length + 1 + (kept.length - 1);
|
|
15071
|
+
const capped = capToolResultEntries(
|
|
15072
|
+
kept.map((segment) => segment.line),
|
|
15073
|
+
MAX_FOLDED_APPENDIX_CHARS - framing
|
|
15074
|
+
);
|
|
15075
|
+
const appendix = `${TOOL_RESULTS_PREFIX}
|
|
15076
|
+
${capped.join("\n")}`;
|
|
15077
|
+
return assistantContent.trim() ? `${assistantContent}
|
|
15078
|
+
|
|
15079
|
+
${appendix}` : appendix;
|
|
15080
|
+
}
|
|
15081
|
+
|
|
14972
15082
|
// src/lib/db/chat/lazyDecrypt.ts
|
|
14973
15083
|
init_useEncryption();
|
|
14974
15084
|
init_encryption_utils();
|
|
@@ -22324,11 +22434,17 @@ function useChatStorage(options) {
|
|
|
22324
22434
|
preProcessors,
|
|
22325
22435
|
piiRedaction,
|
|
22326
22436
|
onPiiRedacted,
|
|
22327
|
-
nerDetector
|
|
22437
|
+
nerDetector,
|
|
22438
|
+
toolResultsHistoryExclude,
|
|
22439
|
+
foldToolResultsInHistory
|
|
22328
22440
|
} = options;
|
|
22329
22441
|
const [currentConversationId, setCurrentConversationId] = (0, import_react5.useState)(
|
|
22330
22442
|
initialConversationId || null
|
|
22331
22443
|
);
|
|
22444
|
+
const toolResultsHistoryExcludeRef = (0, import_react5.useRef)(toolResultsHistoryExclude);
|
|
22445
|
+
toolResultsHistoryExcludeRef.current = toolResultsHistoryExclude;
|
|
22446
|
+
const foldToolResultsInHistoryRef = (0, import_react5.useRef)(foldToolResultsInHistory);
|
|
22447
|
+
foldToolResultsInHistoryRef.current = foldToolResultsInHistory;
|
|
22332
22448
|
const nerDetectorRef = (0, import_react5.useRef)(nerDetector);
|
|
22333
22449
|
nerDetectorRef.current = nerDetector;
|
|
22334
22450
|
const resolvedPiiRedaction = (0, import_react5.useMemo)(
|
|
@@ -23336,7 +23452,12 @@ function useChatStorage(options) {
|
|
|
23336
23452
|
}
|
|
23337
23453
|
if (includeHistory) {
|
|
23338
23454
|
const validMessages = storedMessages.filter((msg) => !msg.error);
|
|
23339
|
-
const
|
|
23455
|
+
const replayableMessages = prepareToolResultsForReplay(validMessages, {
|
|
23456
|
+
fold: foldToolResultsInHistoryRef.current === true,
|
|
23457
|
+
exclude: toolResultsHistoryExcludeRef.current,
|
|
23458
|
+
placeholder: DISPLAY_CARD_PLACEHOLDER
|
|
23459
|
+
});
|
|
23460
|
+
const limitedMessages = replayableMessages.slice(-maxHistoryMessages);
|
|
23340
23461
|
if (!fileContextForRequest) {
|
|
23341
23462
|
for (let i = limitedMessages.length - 1; i >= 0; i--) {
|
|
23342
23463
|
const msg = limitedMessages[i];
|
|
@@ -23346,6 +23467,7 @@ function useChatStorage(options) {
|
|
|
23346
23467
|
}
|
|
23347
23468
|
}
|
|
23348
23469
|
}
|
|
23470
|
+
const foldedHistory = limitedMessages;
|
|
23349
23471
|
let encryptionKey;
|
|
23350
23472
|
if (walletAddress && hasEncryptionKey(walletAddress) && isOPFSSupported()) {
|
|
23351
23473
|
try {
|
|
@@ -23362,7 +23484,7 @@ function useChatStorage(options) {
|
|
|
23362
23484
|
const { messagesToConvert, summarySystemMessage } = await maybeSummarizeHistory({
|
|
23363
23485
|
database,
|
|
23364
23486
|
conversationId: convId,
|
|
23365
|
-
messages:
|
|
23487
|
+
messages: foldedHistory,
|
|
23366
23488
|
summarizeHistory,
|
|
23367
23489
|
summaryTokenThreshold,
|
|
23368
23490
|
summaryMinWindowMessages,
|
|
@@ -23820,37 +23942,25 @@ function useChatStorage(options) {
|
|
|
23820
23942
|
}
|
|
23821
23943
|
}
|
|
23822
23944
|
const autoToolResults = result.autoExecutedToolResults;
|
|
23945
|
+
let storedToolResultsMessage;
|
|
23823
23946
|
if (autoToolResults && autoToolResults.length > 0) {
|
|
23824
|
-
const
|
|
23947
|
+
const toolResultsOpts = {
|
|
23948
|
+
conversationId: convId,
|
|
23949
|
+
role: "user",
|
|
23950
|
+
content: buildToolResultContent(autoToolResults),
|
|
23951
|
+
model: "",
|
|
23952
|
+
parentMessageId: storedAssistantMessage.uniqueId,
|
|
23953
|
+
origin: TOOL_RESULT_ORIGIN
|
|
23954
|
+
};
|
|
23825
23955
|
try {
|
|
23826
|
-
await writeOrQueue(
|
|
23956
|
+
const toolResultsWrite = await writeOrQueue(
|
|
23827
23957
|
"createMessage",
|
|
23828
|
-
|
|
23829
|
-
|
|
23830
|
-
|
|
23831
|
-
content: toolResultContent,
|
|
23832
|
-
model: "",
|
|
23833
|
-
parentMessageId: storedAssistantMessage.uniqueId,
|
|
23834
|
-
origin: "tool_result"
|
|
23835
|
-
},
|
|
23836
|
-
() => createMessageOp(storageCtx, {
|
|
23837
|
-
conversationId: convId,
|
|
23838
|
-
role: "user",
|
|
23839
|
-
content: toolResultContent,
|
|
23840
|
-
model: "",
|
|
23841
|
-
parentMessageId: storedAssistantMessage.uniqueId,
|
|
23842
|
-
origin: "tool_result"
|
|
23843
|
-
}),
|
|
23844
|
-
() => makeSyntheticStoredMessage({
|
|
23845
|
-
conversationId: convId,
|
|
23846
|
-
role: "user",
|
|
23847
|
-
content: toolResultContent,
|
|
23848
|
-
model: "",
|
|
23849
|
-
parentMessageId: storedAssistantMessage.uniqueId,
|
|
23850
|
-
origin: "tool_result"
|
|
23851
|
-
}),
|
|
23958
|
+
toolResultsOpts,
|
|
23959
|
+
() => createMessageOp(storageCtx, toolResultsOpts),
|
|
23960
|
+
() => makeSyntheticStoredMessage(toolResultsOpts),
|
|
23852
23961
|
assistantMsgQueueId ? [assistantMsgQueueId] : []
|
|
23853
23962
|
);
|
|
23963
|
+
storedToolResultsMessage = toolResultsWrite.result;
|
|
23854
23964
|
} catch {
|
|
23855
23965
|
}
|
|
23856
23966
|
}
|
|
@@ -23873,7 +23983,8 @@ function useChatStorage(options) {
|
|
|
23873
23983
|
error: null,
|
|
23874
23984
|
userMessage: storedUserMessage,
|
|
23875
23985
|
assistantMessage: storedAssistantMessage,
|
|
23876
|
-
autoExecutedToolResults:
|
|
23986
|
+
autoExecutedToolResults: autoToolResults,
|
|
23987
|
+
toolResultsMessage: storedToolResultsMessage
|
|
23877
23988
|
};
|
|
23878
23989
|
},
|
|
23879
23990
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally omitting stable refs and config values that don't change identity
|
package/dist/react/index.d.mts
CHANGED
|
@@ -5220,6 +5220,40 @@ interface BaseUseChatStorageOptions {
|
|
|
5220
5220
|
* Defaults to the hardcoded MCP_R2_DOMAIN from clientConfig.
|
|
5221
5221
|
*/
|
|
5222
5222
|
mcpR2Domain?: string;
|
|
5223
|
+
/**
|
|
5224
|
+
* Tool names whose persisted results must never be replayed to the model.
|
|
5225
|
+
*
|
|
5226
|
+
* A turn's auto-executed tool results are stored as a synthetic
|
|
5227
|
+
* `[Tool Execution Results]` row and, on a replayed send, folded back onto the
|
|
5228
|
+
* assistant turn they belong to — that is what lets a follow-up question about a
|
|
5229
|
+
* tool's output work after a reload. Name a tool here when its payload exists for
|
|
5230
|
+
* the RENDERER rather than the model: a display card can carry data the model was
|
|
5231
|
+
* deliberately never given (People Nearby's card holds third parties' snapped
|
|
5232
|
+
* coordinates, which the search result strips), and replaying it would hand that
|
|
5233
|
+
* data straight back.
|
|
5234
|
+
*
|
|
5235
|
+
* Hook-level rather than per-send on purpose: an exclusion that has to be
|
|
5236
|
+
* remembered at every call site is one bad send away from leaking.
|
|
5237
|
+
*/
|
|
5238
|
+
toolResultsHistoryExclude?: string[];
|
|
5239
|
+
/**
|
|
5240
|
+
* Fold persisted `[Tool Execution Results]` rows onto the assistant turn that produced them
|
|
5241
|
+
* when replaying stored history, instead of dropping them.
|
|
5242
|
+
*
|
|
5243
|
+
* **Defaults to `false`, and that default is deliberate.** Folding is the better behaviour —
|
|
5244
|
+
* it is what lets a follow-up about a tool's output work after a reload — but it moves the
|
|
5245
|
+
* payload from a `role: "user"` row onto an `assistant` row. Any consumer that scrubs these
|
|
5246
|
+
* rows by checking `role === "user"` plus the content prefix (which is how both apps did it
|
|
5247
|
+
* before this option existed) stops catching them the moment folding turns on, and starts
|
|
5248
|
+
* replaying whatever the row held. Opting in is therefore a statement that the caller has
|
|
5249
|
+
* checked its own filters and set {@link toolResultsHistoryExclude} for any payload that must
|
|
5250
|
+
* not reach the model.
|
|
5251
|
+
*
|
|
5252
|
+
* With it off, rows are dropped from the replayed history rather than sent verbatim. Verbatim
|
|
5253
|
+
* would put two consecutive `user` turns on the wire, and the model answers the previous turn
|
|
5254
|
+
* instead of the new prompt.
|
|
5255
|
+
*/
|
|
5256
|
+
foldToolResultsInHistory?: boolean;
|
|
5223
5257
|
/**
|
|
5224
5258
|
* Pre-processors run after the last user message is received but before
|
|
5225
5259
|
* the first LLM request. Each receives the prompt text and a shared
|
|
@@ -10354,6 +10388,12 @@ type SendMessageWithStorageResult = {
|
|
|
10354
10388
|
name: string;
|
|
10355
10389
|
result: unknown;
|
|
10356
10390
|
}[];
|
|
10391
|
+
/**
|
|
10392
|
+
* The synthetic `[Tool Execution Results]` row those results were persisted as, so a caller can
|
|
10393
|
+
* key its transient overlay on the id the SDK actually wrote instead of deriving one (#5519).
|
|
10394
|
+
* Absent when no tool ran, or when that (non-fatal) write failed.
|
|
10395
|
+
*/
|
|
10396
|
+
toolResultsMessage?: StoredMessage;
|
|
10357
10397
|
} | {
|
|
10358
10398
|
data: ApiResponse;
|
|
10359
10399
|
error: null;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -5220,6 +5220,40 @@ interface BaseUseChatStorageOptions {
|
|
|
5220
5220
|
* Defaults to the hardcoded MCP_R2_DOMAIN from clientConfig.
|
|
5221
5221
|
*/
|
|
5222
5222
|
mcpR2Domain?: string;
|
|
5223
|
+
/**
|
|
5224
|
+
* Tool names whose persisted results must never be replayed to the model.
|
|
5225
|
+
*
|
|
5226
|
+
* A turn's auto-executed tool results are stored as a synthetic
|
|
5227
|
+
* `[Tool Execution Results]` row and, on a replayed send, folded back onto the
|
|
5228
|
+
* assistant turn they belong to — that is what lets a follow-up question about a
|
|
5229
|
+
* tool's output work after a reload. Name a tool here when its payload exists for
|
|
5230
|
+
* the RENDERER rather than the model: a display card can carry data the model was
|
|
5231
|
+
* deliberately never given (People Nearby's card holds third parties' snapped
|
|
5232
|
+
* coordinates, which the search result strips), and replaying it would hand that
|
|
5233
|
+
* data straight back.
|
|
5234
|
+
*
|
|
5235
|
+
* Hook-level rather than per-send on purpose: an exclusion that has to be
|
|
5236
|
+
* remembered at every call site is one bad send away from leaking.
|
|
5237
|
+
*/
|
|
5238
|
+
toolResultsHistoryExclude?: string[];
|
|
5239
|
+
/**
|
|
5240
|
+
* Fold persisted `[Tool Execution Results]` rows onto the assistant turn that produced them
|
|
5241
|
+
* when replaying stored history, instead of dropping them.
|
|
5242
|
+
*
|
|
5243
|
+
* **Defaults to `false`, and that default is deliberate.** Folding is the better behaviour —
|
|
5244
|
+
* it is what lets a follow-up about a tool's output work after a reload — but it moves the
|
|
5245
|
+
* payload from a `role: "user"` row onto an `assistant` row. Any consumer that scrubs these
|
|
5246
|
+
* rows by checking `role === "user"` plus the content prefix (which is how both apps did it
|
|
5247
|
+
* before this option existed) stops catching them the moment folding turns on, and starts
|
|
5248
|
+
* replaying whatever the row held. Opting in is therefore a statement that the caller has
|
|
5249
|
+
* checked its own filters and set {@link toolResultsHistoryExclude} for any payload that must
|
|
5250
|
+
* not reach the model.
|
|
5251
|
+
*
|
|
5252
|
+
* With it off, rows are dropped from the replayed history rather than sent verbatim. Verbatim
|
|
5253
|
+
* would put two consecutive `user` turns on the wire, and the model answers the previous turn
|
|
5254
|
+
* instead of the new prompt.
|
|
5255
|
+
*/
|
|
5256
|
+
foldToolResultsInHistory?: boolean;
|
|
5223
5257
|
/**
|
|
5224
5258
|
* Pre-processors run after the last user message is received but before
|
|
5225
5259
|
* the first LLM request. Each receives the prompt text and a shared
|
|
@@ -10354,6 +10388,12 @@ type SendMessageWithStorageResult = {
|
|
|
10354
10388
|
name: string;
|
|
10355
10389
|
result: unknown;
|
|
10356
10390
|
}[];
|
|
10391
|
+
/**
|
|
10392
|
+
* The synthetic `[Tool Execution Results]` row those results were persisted as, so a caller can
|
|
10393
|
+
* key its transient overlay on the id the SDK actually wrote instead of deriving one (#5519).
|
|
10394
|
+
* Absent when no tool ran, or when that (non-fatal) write failed.
|
|
10395
|
+
*/
|
|
10396
|
+
toolResultsMessage?: StoredMessage;
|
|
10357
10397
|
} | {
|
|
10358
10398
|
data: ApiResponse;
|
|
10359
10399
|
error: null;
|
package/dist/react/index.mjs
CHANGED
|
@@ -5350,6 +5350,7 @@ async function cleanupConversationSummary(database, conversationId) {
|
|
|
5350
5350
|
var HEADER = "[Tool Execution Results]\n\n";
|
|
5351
5351
|
var FOOTER = "\n\nBased on these results, continue with the task.";
|
|
5352
5352
|
var ENTRY_SEPARATOR = "\n\n";
|
|
5353
|
+
var TOOL_RESULT_FOOTER_LINE = FOOTER.trim();
|
|
5353
5354
|
var MAX_PERSISTED_TOOL_RESULT_CHARS = 1e5;
|
|
5354
5355
|
function buildToolResultContent(results) {
|
|
5355
5356
|
const entries = results.map((r) => `Tool "${r.name}" returned: ${JSON.stringify(r.result)}`);
|
|
@@ -5359,6 +5360,9 @@ function buildToolResultContent(results) {
|
|
|
5359
5360
|
);
|
|
5360
5361
|
return `${HEADER}${summary}${FOOTER}`;
|
|
5361
5362
|
}
|
|
5363
|
+
function capToolResultEntries(entries, budget) {
|
|
5364
|
+
return capEntries(entries, budget);
|
|
5365
|
+
}
|
|
5362
5366
|
function capEntries(entries, budget) {
|
|
5363
5367
|
const smallestFirst = entries.map((_, index) => index).sort((a, b) => entries[a].length - entries[b].length);
|
|
5364
5368
|
const capped = [...entries];
|
|
@@ -5383,6 +5387,112 @@ function truncationMarker(omitted) {
|
|
|
5383
5387
|
... (tool output truncated, ${omitted} characters omitted)`;
|
|
5384
5388
|
}
|
|
5385
5389
|
|
|
5390
|
+
// src/lib/chat/toolResults.ts
|
|
5391
|
+
var TOOL_RESULTS_PREFIX = "[Tool Execution Results]";
|
|
5392
|
+
var DISPLAY_CARD_PLACEHOLDER = "[displayed a card to the user with the tool result]";
|
|
5393
|
+
var TOOL_LINE = /^Tool "([^"]+)" returned: (.*)$/;
|
|
5394
|
+
var TOOL_RESULT_ORIGIN = "tool_result";
|
|
5395
|
+
function isToolResultsRow(row) {
|
|
5396
|
+
if (row.origin === TOOL_RESULT_ORIGIN) return true;
|
|
5397
|
+
if (typeof row.origin === "string") return false;
|
|
5398
|
+
if (row.role !== "user" || !row.content.startsWith(TOOL_RESULTS_PREFIX)) return false;
|
|
5399
|
+
return parseToolResultSegments(row.content).length > 0;
|
|
5400
|
+
}
|
|
5401
|
+
function parseToolResultSegments(content) {
|
|
5402
|
+
const segments = [];
|
|
5403
|
+
for (const raw of content.split("\n")) {
|
|
5404
|
+
const line = raw.trim();
|
|
5405
|
+
const match = TOOL_LINE.exec(line);
|
|
5406
|
+
if (match) {
|
|
5407
|
+
segments.push({ name: match[1], line });
|
|
5408
|
+
continue;
|
|
5409
|
+
}
|
|
5410
|
+
const current = segments[segments.length - 1];
|
|
5411
|
+
if (!current || line === "" || line === TOOL_RESULT_FOOTER_LINE) continue;
|
|
5412
|
+
if (line === TOOL_RESULTS_PREFIX) continue;
|
|
5413
|
+
current.line = `${current.line}
|
|
5414
|
+
${line}`;
|
|
5415
|
+
}
|
|
5416
|
+
return segments;
|
|
5417
|
+
}
|
|
5418
|
+
function prepareToolResultsForReplay(rows, options) {
|
|
5419
|
+
if (options.fold) {
|
|
5420
|
+
return foldToolResultsRows(rows, {
|
|
5421
|
+
exclude: options.exclude,
|
|
5422
|
+
placeholder: options.placeholder
|
|
5423
|
+
});
|
|
5424
|
+
}
|
|
5425
|
+
return rows.filter((row) => !isToolResultsRow(row));
|
|
5426
|
+
}
|
|
5427
|
+
function foldToolResultsRows(rows, options) {
|
|
5428
|
+
const exclude = new Set(options?.exclude ?? []);
|
|
5429
|
+
const out = [];
|
|
5430
|
+
const pending = [];
|
|
5431
|
+
const assistantIndexById = /* @__PURE__ */ new Map();
|
|
5432
|
+
for (const row of rows) {
|
|
5433
|
+
if (isToolResultsRow(row)) {
|
|
5434
|
+
pending.push({ row, insertionPoint: out.length });
|
|
5435
|
+
continue;
|
|
5436
|
+
}
|
|
5437
|
+
out.push(row);
|
|
5438
|
+
if (row.role === "assistant" && row.uniqueId) {
|
|
5439
|
+
assistantIndexById.set(row.uniqueId, out.length - 1);
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
5443
|
+
for (const { row, insertionPoint } of pending) {
|
|
5444
|
+
const target = resolveFoldTarget(out, row, insertionPoint, assistantIndexById);
|
|
5445
|
+
if (target === void 0) continue;
|
|
5446
|
+
const group = byTarget.get(target);
|
|
5447
|
+
if (group) group.push(row);
|
|
5448
|
+
else byTarget.set(target, [row]);
|
|
5449
|
+
}
|
|
5450
|
+
for (const [target, groupedRows] of byTarget) {
|
|
5451
|
+
const assistant = out[target];
|
|
5452
|
+
const kept = groupedRows.flatMap((row) => parseToolResultSegments(row.content)).filter((segment) => !exclude.has(segment.name));
|
|
5453
|
+
if (kept.length > 0) {
|
|
5454
|
+
out[target] = { ...assistant, content: appendToolResults(assistant.content, kept) };
|
|
5455
|
+
} else if (!assistant.content.trim() && options?.placeholder) {
|
|
5456
|
+
out[target] = { ...assistant, content: options.placeholder };
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
return out;
|
|
5460
|
+
}
|
|
5461
|
+
function resolveFoldTarget(out, row, insertionPoint, assistantIndexById) {
|
|
5462
|
+
const linked = row.parentMessageId ? assistantIndexById.get(row.parentMessageId) : void 0;
|
|
5463
|
+
if (linked !== void 0) return linked;
|
|
5464
|
+
let previous;
|
|
5465
|
+
for (let i = insertionPoint - 1; i >= 0; i--) {
|
|
5466
|
+
if (out[i].role === "assistant") {
|
|
5467
|
+
previous = i;
|
|
5468
|
+
break;
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
let next;
|
|
5472
|
+
for (let i = insertionPoint; i < out.length; i++) {
|
|
5473
|
+
if (out[i].role === "assistant") {
|
|
5474
|
+
next = i;
|
|
5475
|
+
break;
|
|
5476
|
+
}
|
|
5477
|
+
}
|
|
5478
|
+
if (previous === void 0) return next;
|
|
5479
|
+
if (next === void 0) return previous;
|
|
5480
|
+
return next - insertionPoint < insertionPoint - previous ? next : previous;
|
|
5481
|
+
}
|
|
5482
|
+
var MAX_FOLDED_APPENDIX_CHARS = 2e4;
|
|
5483
|
+
function appendToolResults(assistantContent, kept) {
|
|
5484
|
+
const framing = TOOL_RESULTS_PREFIX.length + 1 + (kept.length - 1);
|
|
5485
|
+
const capped = capToolResultEntries(
|
|
5486
|
+
kept.map((segment) => segment.line),
|
|
5487
|
+
MAX_FOLDED_APPENDIX_CHARS - framing
|
|
5488
|
+
);
|
|
5489
|
+
const appendix = `${TOOL_RESULTS_PREFIX}
|
|
5490
|
+
${capped.join("\n")}`;
|
|
5491
|
+
return assistantContent.trim() ? `${assistantContent}
|
|
5492
|
+
|
|
5493
|
+
${appendix}` : appendix;
|
|
5494
|
+
}
|
|
5495
|
+
|
|
5386
5496
|
// src/lib/db/chat/lazyDecrypt.ts
|
|
5387
5497
|
var LRU_CAPACITY = 256;
|
|
5388
5498
|
var titleCache = /* @__PURE__ */ new Map();
|
|
@@ -12612,11 +12722,17 @@ function useChatStorage(options) {
|
|
|
12612
12722
|
preProcessors,
|
|
12613
12723
|
piiRedaction,
|
|
12614
12724
|
onPiiRedacted,
|
|
12615
|
-
nerDetector
|
|
12725
|
+
nerDetector,
|
|
12726
|
+
toolResultsHistoryExclude,
|
|
12727
|
+
foldToolResultsInHistory
|
|
12616
12728
|
} = options;
|
|
12617
12729
|
const [currentConversationId, setCurrentConversationId] = useState4(
|
|
12618
12730
|
initialConversationId || null
|
|
12619
12731
|
);
|
|
12732
|
+
const toolResultsHistoryExcludeRef = useRef4(toolResultsHistoryExclude);
|
|
12733
|
+
toolResultsHistoryExcludeRef.current = toolResultsHistoryExclude;
|
|
12734
|
+
const foldToolResultsInHistoryRef = useRef4(foldToolResultsInHistory);
|
|
12735
|
+
foldToolResultsInHistoryRef.current = foldToolResultsInHistory;
|
|
12620
12736
|
const nerDetectorRef = useRef4(nerDetector);
|
|
12621
12737
|
nerDetectorRef.current = nerDetector;
|
|
12622
12738
|
const resolvedPiiRedaction = useMemo3(
|
|
@@ -13624,7 +13740,12 @@ function useChatStorage(options) {
|
|
|
13624
13740
|
}
|
|
13625
13741
|
if (includeHistory) {
|
|
13626
13742
|
const validMessages = storedMessages.filter((msg) => !msg.error);
|
|
13627
|
-
const
|
|
13743
|
+
const replayableMessages = prepareToolResultsForReplay(validMessages, {
|
|
13744
|
+
fold: foldToolResultsInHistoryRef.current === true,
|
|
13745
|
+
exclude: toolResultsHistoryExcludeRef.current,
|
|
13746
|
+
placeholder: DISPLAY_CARD_PLACEHOLDER
|
|
13747
|
+
});
|
|
13748
|
+
const limitedMessages = replayableMessages.slice(-maxHistoryMessages);
|
|
13628
13749
|
if (!fileContextForRequest) {
|
|
13629
13750
|
for (let i = limitedMessages.length - 1; i >= 0; i--) {
|
|
13630
13751
|
const msg = limitedMessages[i];
|
|
@@ -13634,6 +13755,7 @@ function useChatStorage(options) {
|
|
|
13634
13755
|
}
|
|
13635
13756
|
}
|
|
13636
13757
|
}
|
|
13758
|
+
const foldedHistory = limitedMessages;
|
|
13637
13759
|
let encryptionKey;
|
|
13638
13760
|
if (walletAddress && hasEncryptionKey(walletAddress) && isOPFSSupported()) {
|
|
13639
13761
|
try {
|
|
@@ -13650,7 +13772,7 @@ function useChatStorage(options) {
|
|
|
13650
13772
|
const { messagesToConvert, summarySystemMessage } = await maybeSummarizeHistory({
|
|
13651
13773
|
database,
|
|
13652
13774
|
conversationId: convId,
|
|
13653
|
-
messages:
|
|
13775
|
+
messages: foldedHistory,
|
|
13654
13776
|
summarizeHistory,
|
|
13655
13777
|
summaryTokenThreshold,
|
|
13656
13778
|
summaryMinWindowMessages,
|
|
@@ -14108,37 +14230,25 @@ function useChatStorage(options) {
|
|
|
14108
14230
|
}
|
|
14109
14231
|
}
|
|
14110
14232
|
const autoToolResults = result.autoExecutedToolResults;
|
|
14233
|
+
let storedToolResultsMessage;
|
|
14111
14234
|
if (autoToolResults && autoToolResults.length > 0) {
|
|
14112
|
-
const
|
|
14235
|
+
const toolResultsOpts = {
|
|
14236
|
+
conversationId: convId,
|
|
14237
|
+
role: "user",
|
|
14238
|
+
content: buildToolResultContent(autoToolResults),
|
|
14239
|
+
model: "",
|
|
14240
|
+
parentMessageId: storedAssistantMessage.uniqueId,
|
|
14241
|
+
origin: TOOL_RESULT_ORIGIN
|
|
14242
|
+
};
|
|
14113
14243
|
try {
|
|
14114
|
-
await writeOrQueue(
|
|
14244
|
+
const toolResultsWrite = await writeOrQueue(
|
|
14115
14245
|
"createMessage",
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
content: toolResultContent,
|
|
14120
|
-
model: "",
|
|
14121
|
-
parentMessageId: storedAssistantMessage.uniqueId,
|
|
14122
|
-
origin: "tool_result"
|
|
14123
|
-
},
|
|
14124
|
-
() => createMessageOp(storageCtx, {
|
|
14125
|
-
conversationId: convId,
|
|
14126
|
-
role: "user",
|
|
14127
|
-
content: toolResultContent,
|
|
14128
|
-
model: "",
|
|
14129
|
-
parentMessageId: storedAssistantMessage.uniqueId,
|
|
14130
|
-
origin: "tool_result"
|
|
14131
|
-
}),
|
|
14132
|
-
() => makeSyntheticStoredMessage({
|
|
14133
|
-
conversationId: convId,
|
|
14134
|
-
role: "user",
|
|
14135
|
-
content: toolResultContent,
|
|
14136
|
-
model: "",
|
|
14137
|
-
parentMessageId: storedAssistantMessage.uniqueId,
|
|
14138
|
-
origin: "tool_result"
|
|
14139
|
-
}),
|
|
14246
|
+
toolResultsOpts,
|
|
14247
|
+
() => createMessageOp(storageCtx, toolResultsOpts),
|
|
14248
|
+
() => makeSyntheticStoredMessage(toolResultsOpts),
|
|
14140
14249
|
assistantMsgQueueId ? [assistantMsgQueueId] : []
|
|
14141
14250
|
);
|
|
14251
|
+
storedToolResultsMessage = toolResultsWrite.result;
|
|
14142
14252
|
} catch {
|
|
14143
14253
|
}
|
|
14144
14254
|
}
|
|
@@ -14161,7 +14271,8 @@ function useChatStorage(options) {
|
|
|
14161
14271
|
error: null,
|
|
14162
14272
|
userMessage: storedUserMessage,
|
|
14163
14273
|
assistantMessage: storedAssistantMessage,
|
|
14164
|
-
autoExecutedToolResults:
|
|
14274
|
+
autoExecutedToolResults: autoToolResults,
|
|
14275
|
+
toolResultsMessage: storedToolResultsMessage
|
|
14165
14276
|
};
|
|
14166
14277
|
},
|
|
14167
14278
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally omitting stable refs and config values that don't change identity
|