@ottocode/web-sdk 0.1.228 → 0.1.231
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/components/chat/ChatInput.d.ts +1 -2
- package/dist/components/chat/ChatInput.d.ts.map +1 -1
- package/dist/components/chat/ChatInputContainer.d.ts.map +1 -1
- package/dist/components/chat/ConfigModal.d.ts.map +1 -1
- package/dist/components/chat/ConfigSelector.d.ts.map +1 -1
- package/dist/components/git/GitDiffPanel.d.ts.map +1 -1
- package/dist/components/index.js +3139 -2344
- package/dist/components/index.js.map +39 -37
- package/dist/components/messages/ActionToolBox.d.ts +8 -0
- package/dist/components/messages/ActionToolBox.d.ts.map +1 -0
- package/dist/components/messages/AssistantMessageGroup.d.ts.map +1 -1
- package/dist/components/messages/CompactActivityGroup.d.ts.map +1 -1
- package/dist/components/messages/MessageThread.d.ts.map +1 -1
- package/dist/components/messages/compactActivity.d.ts +1 -0
- package/dist/components/messages/compactActivity.d.ts.map +1 -1
- package/dist/components/onboarding/steps/DefaultsStep.d.ts.map +1 -1
- package/dist/components/research/ResearchSidebar.d.ts.map +1 -1
- package/dist/components/session-files/SessionFilesSidebar.d.ts.map +1 -1
- package/dist/components/sessions/SessionHeader.d.ts.map +1 -1
- package/dist/components/sessions/SessionItem.d.ts.map +1 -1
- package/dist/components/sessions/SessionListContainer.d.ts.map +1 -1
- package/dist/components/sessions/session-time.d.ts +9 -0
- package/dist/components/sessions/session-time.d.ts.map +1 -0
- package/dist/components/settings/SettingsSidebar.d.ts.map +1 -1
- package/dist/components/terminals/TerminalViewer.d.ts.map +1 -1
- package/dist/hooks/index.js +289 -60
- package/dist/hooks/index.js.map +11 -11
- package/dist/hooks/useConfig.d.ts +22 -1
- package/dist/hooks/useConfig.d.ts.map +1 -1
- package/dist/hooks/useMessages.d.ts +6 -1
- package/dist/hooks/useMessages.d.ts.map +1 -1
- package/dist/hooks/usePreferences.d.ts +4 -1
- package/dist/hooks/usePreferences.d.ts.map +1 -1
- package/dist/hooks/useProviderUsage.d.ts.map +1 -1
- package/dist/hooks/useResearch.d.ts +1 -1
- package/dist/hooks/useResearch.d.ts.map +1 -1
- package/dist/hooks/useSessionFiles.d.ts +1 -1
- package/dist/hooks/useSessionFiles.d.ts.map +1 -1
- package/dist/hooks/useSessionStream.d.ts +1 -1
- package/dist/hooks/useSessionStream.d.ts.map +1 -1
- package/dist/index.js +3151 -2356
- package/dist/index.js.map +39 -37
- package/dist/lib/api-client/config.d.ts +6 -0
- package/dist/lib/api-client/config.d.ts.map +1 -1
- package/dist/lib/api-client/index.d.ts +6 -0
- package/dist/lib/api-client/index.d.ts.map +1 -1
- package/dist/lib/index.js +3 -1
- package/dist/lib/index.js.map +4 -4
- package/dist/lib/sse-client.d.ts.map +1 -1
- package/dist/types/api.d.ts +3 -0
- package/dist/types/api.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/hooks/index.js
CHANGED
|
@@ -924,6 +924,26 @@ function useUpdateDefaults() {
|
|
|
924
924
|
const queryClient = useQueryClient();
|
|
925
925
|
return useMutation({
|
|
926
926
|
mutationFn: (data) => apiClient.updateDefaults(data),
|
|
927
|
+
onMutate: async (data) => {
|
|
928
|
+
await queryClient.cancelQueries({ queryKey: ["config"] });
|
|
929
|
+
const previousConfig = queryClient.getQueryData(["config"]);
|
|
930
|
+
if (previousConfig) {
|
|
931
|
+
const defaultUpdates = Object.fromEntries(Object.entries(data).filter(([key, value]) => key !== "scope" && value !== undefined));
|
|
932
|
+
queryClient.setQueryData(["config"], {
|
|
933
|
+
...previousConfig,
|
|
934
|
+
defaults: {
|
|
935
|
+
...previousConfig.defaults,
|
|
936
|
+
...defaultUpdates
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
return { previousConfig };
|
|
941
|
+
},
|
|
942
|
+
onError: (_error, _data, context) => {
|
|
943
|
+
if (context?.previousConfig) {
|
|
944
|
+
queryClient.setQueryData(["config"], context.previousConfig);
|
|
945
|
+
}
|
|
946
|
+
},
|
|
927
947
|
onSuccess: () => {
|
|
928
948
|
queryClient.invalidateQueries({ queryKey: ["config"] });
|
|
929
949
|
}
|
|
@@ -932,27 +952,27 @@ function useUpdateDefaults() {
|
|
|
932
952
|
// src/hooks/usePreferences.ts
|
|
933
953
|
import { useCallback, useMemo, useSyncExternalStore } from "react";
|
|
934
954
|
var STORAGE_KEY = "otto-preferences";
|
|
935
|
-
var
|
|
955
|
+
var DEFAULT_STORED_PREFERENCES = {
|
|
936
956
|
vimMode: false,
|
|
937
957
|
compactThread: true
|
|
938
958
|
};
|
|
939
959
|
function resolveInitialPreferences() {
|
|
940
960
|
if (typeof window === "undefined") {
|
|
941
|
-
return
|
|
961
|
+
return DEFAULT_STORED_PREFERENCES;
|
|
942
962
|
}
|
|
943
963
|
try {
|
|
944
964
|
const stored = window.localStorage.getItem(STORAGE_KEY);
|
|
945
965
|
if (stored) {
|
|
946
966
|
const parsed = JSON.parse(stored);
|
|
947
967
|
return {
|
|
948
|
-
|
|
949
|
-
|
|
968
|
+
vimMode: typeof parsed.vimMode === "boolean" ? parsed.vimMode : DEFAULT_STORED_PREFERENCES.vimMode,
|
|
969
|
+
compactThread: typeof parsed.compactThread === "boolean" ? parsed.compactThread : DEFAULT_STORED_PREFERENCES.compactThread
|
|
950
970
|
};
|
|
951
971
|
}
|
|
952
972
|
} catch (error) {
|
|
953
973
|
console.warn("Failed to load preferences", error);
|
|
954
974
|
}
|
|
955
|
-
return
|
|
975
|
+
return DEFAULT_STORED_PREFERENCES;
|
|
956
976
|
}
|
|
957
977
|
var preferences = resolveInitialPreferences();
|
|
958
978
|
var listeners = new Set;
|
|
@@ -960,7 +980,7 @@ function getSnapshot() {
|
|
|
960
980
|
return preferences;
|
|
961
981
|
}
|
|
962
982
|
function getServerSnapshot() {
|
|
963
|
-
return
|
|
983
|
+
return DEFAULT_STORED_PREFERENCES;
|
|
964
984
|
}
|
|
965
985
|
function subscribe(listener) {
|
|
966
986
|
listeners.add(listener);
|
|
@@ -984,10 +1004,31 @@ function updateStore(updates) {
|
|
|
984
1004
|
}
|
|
985
1005
|
function usePreferences() {
|
|
986
1006
|
const currentPreferences = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1007
|
+
const { data: config2 } = useConfig();
|
|
1008
|
+
const updateDefaults = useUpdateDefaults();
|
|
987
1009
|
const updatePreferences = useCallback((updates) => {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1010
|
+
const localUpdates = {};
|
|
1011
|
+
if (updates.vimMode !== undefined) {
|
|
1012
|
+
localUpdates.vimMode = updates.vimMode;
|
|
1013
|
+
}
|
|
1014
|
+
if (updates.compactThread !== undefined) {
|
|
1015
|
+
localUpdates.compactThread = updates.compactThread;
|
|
1016
|
+
}
|
|
1017
|
+
if (Object.keys(localUpdates).length > 0) {
|
|
1018
|
+
updateStore(localUpdates);
|
|
1019
|
+
}
|
|
1020
|
+
if (updates.fullWidthContent !== undefined && updates.fullWidthContent !== config2?.defaults?.fullWidthContent) {
|
|
1021
|
+
updateDefaults.mutate({
|
|
1022
|
+
fullWidthContent: updates.fullWidthContent,
|
|
1023
|
+
scope: "global"
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
}, [config2?.defaults?.fullWidthContent, updateDefaults]);
|
|
1027
|
+
const resolvedPreferences = useMemo(() => ({
|
|
1028
|
+
...currentPreferences,
|
|
1029
|
+
fullWidthContent: config2?.defaults?.fullWidthContent ?? false
|
|
1030
|
+
}), [currentPreferences, config2?.defaults?.fullWidthContent]);
|
|
1031
|
+
return useMemo(() => ({ preferences: resolvedPreferences, updatePreferences }), [resolvedPreferences, updatePreferences]);
|
|
991
1032
|
}
|
|
992
1033
|
// src/hooks/useFiles.ts
|
|
993
1034
|
import { useQuery as useQuery2 } from "@tanstack/react-query";
|
|
@@ -1633,7 +1674,8 @@ function useDeleteSession() {
|
|
|
1633
1674
|
}
|
|
1634
1675
|
|
|
1635
1676
|
// src/hooks/useMessages.ts
|
|
1636
|
-
function useMessages(sessionId) {
|
|
1677
|
+
function useMessages(sessionId, options = {}) {
|
|
1678
|
+
const { enabled = true, staleTime = 15000 } = options;
|
|
1637
1679
|
return useQuery4({
|
|
1638
1680
|
queryKey: ["messages", sessionId],
|
|
1639
1681
|
queryFn: () => {
|
|
@@ -1642,7 +1684,9 @@ function useMessages(sessionId) {
|
|
|
1642
1684
|
}
|
|
1643
1685
|
return apiClient.getMessages(sessionId);
|
|
1644
1686
|
},
|
|
1645
|
-
enabled: !!sessionId
|
|
1687
|
+
enabled: !!sessionId && enabled,
|
|
1688
|
+
staleTime,
|
|
1689
|
+
refetchOnWindowFocus: false
|
|
1646
1690
|
});
|
|
1647
1691
|
}
|
|
1648
1692
|
function useSendMessage(sessionId) {
|
|
@@ -1670,8 +1714,10 @@ class SSEClient {
|
|
|
1670
1714
|
}
|
|
1671
1715
|
this.abortController = new AbortController;
|
|
1672
1716
|
this.running = true;
|
|
1717
|
+
const isTunnel = !url.includes("localhost") && !url.includes("127.0.0.1");
|
|
1673
1718
|
try {
|
|
1674
1719
|
const response = await fetch(url, {
|
|
1720
|
+
method: isTunnel ? "POST" : "GET",
|
|
1675
1721
|
headers: { Accept: "text/event-stream" },
|
|
1676
1722
|
signal: this.abortController.signal
|
|
1677
1723
|
});
|
|
@@ -1787,11 +1833,10 @@ var useToolApprovalStore = create9((set) => ({
|
|
|
1787
1833
|
}));
|
|
1788
1834
|
|
|
1789
1835
|
// src/hooks/useSessionStream.ts
|
|
1790
|
-
function useSessionStream(sessionId) {
|
|
1836
|
+
function useSessionStream(sessionId, enabled = true) {
|
|
1791
1837
|
const queryClient = useQueryClient5();
|
|
1792
1838
|
const clientRef = useRef(null);
|
|
1793
1839
|
const assistantMessageIdRef = useRef(null);
|
|
1794
|
-
const lastInvalidationRef = useRef(0);
|
|
1795
1840
|
const {
|
|
1796
1841
|
addPendingApproval,
|
|
1797
1842
|
removePendingApproval,
|
|
@@ -1799,8 +1844,7 @@ function useSessionStream(sessionId) {
|
|
|
1799
1844
|
setPendingApprovals
|
|
1800
1845
|
} = useToolApprovalStore();
|
|
1801
1846
|
useEffect(() => {
|
|
1802
|
-
if (!sessionId) {
|
|
1803
|
-
console.log("[useSessionStream] No sessionId, skipping");
|
|
1847
|
+
if (!sessionId || !enabled) {
|
|
1804
1848
|
return;
|
|
1805
1849
|
}
|
|
1806
1850
|
assistantMessageIdRef.current = null;
|
|
@@ -1847,6 +1891,27 @@ function useSessionStream(sessionId) {
|
|
|
1847
1891
|
}
|
|
1848
1892
|
return "";
|
|
1849
1893
|
};
|
|
1894
|
+
const getToolEventCallId = (payload) => {
|
|
1895
|
+
if (typeof payload?.callId === "string")
|
|
1896
|
+
return payload.callId;
|
|
1897
|
+
return typeof payload?.toolCallId === "string" ? payload.toolCallId : null;
|
|
1898
|
+
};
|
|
1899
|
+
const getToolEventName = (payload) => {
|
|
1900
|
+
if (typeof payload?.name === "string")
|
|
1901
|
+
return payload.name;
|
|
1902
|
+
return typeof payload?.toolName === "string" ? payload.toolName : null;
|
|
1903
|
+
};
|
|
1904
|
+
const getToolEventArgs = (payload) => payload?.args ?? payload?.input;
|
|
1905
|
+
const getToolInputDelta = (payload) => {
|
|
1906
|
+
if (typeof payload?.delta === "string")
|
|
1907
|
+
return payload.delta;
|
|
1908
|
+
return typeof payload?.inputTextDelta === "string" ? payload.inputTextDelta : null;
|
|
1909
|
+
};
|
|
1910
|
+
const getToolOutputDelta = (payload) => {
|
|
1911
|
+
if (typeof payload?.delta === "string")
|
|
1912
|
+
return payload.delta;
|
|
1913
|
+
return typeof payload?.outputTextDelta === "string" ? payload.outputTextDelta : null;
|
|
1914
|
+
};
|
|
1850
1915
|
const getOptimisticPartIndex = (parts, stepIndex) => {
|
|
1851
1916
|
if (typeof stepIndex !== "number") {
|
|
1852
1917
|
return parts.length;
|
|
@@ -1979,8 +2044,8 @@ function useSessionStream(sessionId) {
|
|
|
1979
2044
|
const upsertEphemeralToolCall = (payload) => {
|
|
1980
2045
|
if (!payload)
|
|
1981
2046
|
return;
|
|
1982
|
-
const callId =
|
|
1983
|
-
const name =
|
|
2047
|
+
const callId = getToolEventCallId(payload);
|
|
2048
|
+
const name = getToolEventName(payload);
|
|
1984
2049
|
if (!name)
|
|
1985
2050
|
return;
|
|
1986
2051
|
queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
|
|
@@ -2004,7 +2069,7 @@ function useSessionStream(sessionId) {
|
|
|
2004
2069
|
if (partIndex === -1 && !callId) {
|
|
2005
2070
|
partIndex = parts.findIndex((part) => part.ephemeral && part.toolName === name);
|
|
2006
2071
|
}
|
|
2007
|
-
const args = payload
|
|
2072
|
+
const args = getToolEventArgs(payload);
|
|
2008
2073
|
const stepIndex = typeof payload.stepIndex === "number" ? payload.stepIndex : null;
|
|
2009
2074
|
const contentJsonBase = { name };
|
|
2010
2075
|
if (callId)
|
|
@@ -2054,15 +2119,161 @@ function useSessionStream(sessionId) {
|
|
|
2054
2119
|
return nextMessages;
|
|
2055
2120
|
});
|
|
2056
2121
|
};
|
|
2122
|
+
const accumulateToolInputDelta = (payload, delta) => {
|
|
2123
|
+
if (!payload)
|
|
2124
|
+
return;
|
|
2125
|
+
const callId = getToolEventCallId(payload);
|
|
2126
|
+
const name = getToolEventName(payload);
|
|
2127
|
+
if (!name)
|
|
2128
|
+
return;
|
|
2129
|
+
queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
|
|
2130
|
+
if (!oldMessages)
|
|
2131
|
+
return oldMessages;
|
|
2132
|
+
const nextMessages = [...oldMessages];
|
|
2133
|
+
let targetIndex = resolveAssistantTargetIndex(nextMessages);
|
|
2134
|
+
if (typeof payload.messageId === "string") {
|
|
2135
|
+
const explicitIndex = nextMessages.findIndex((message) => message.id === payload.messageId);
|
|
2136
|
+
if (explicitIndex !== -1)
|
|
2137
|
+
targetIndex = explicitIndex;
|
|
2138
|
+
}
|
|
2139
|
+
if (targetIndex === -1)
|
|
2140
|
+
return oldMessages;
|
|
2141
|
+
const targetMessage = nextMessages[targetIndex];
|
|
2142
|
+
const parts = targetMessage.parts ? [...targetMessage.parts] : [];
|
|
2143
|
+
let partIndex = -1;
|
|
2144
|
+
if (callId) {
|
|
2145
|
+
partIndex = parts.findIndex((part) => part.toolCallId === callId && part.ephemeral);
|
|
2146
|
+
}
|
|
2147
|
+
if (partIndex === -1 && !callId) {
|
|
2148
|
+
partIndex = parts.findIndex((part) => part.ephemeral && part.toolName === name);
|
|
2149
|
+
}
|
|
2150
|
+
const stepIndex = typeof payload.stepIndex === "number" ? payload.stepIndex : null;
|
|
2151
|
+
if (partIndex === -1) {
|
|
2152
|
+
const contentJsonBase = {
|
|
2153
|
+
name,
|
|
2154
|
+
_streamedInput: delta
|
|
2155
|
+
};
|
|
2156
|
+
if (callId)
|
|
2157
|
+
contentJsonBase.callId = callId;
|
|
2158
|
+
const newPart = {
|
|
2159
|
+
id: callId ? `ephemeral-tool-call-${callId}` : `ephemeral-tool-call-${name}-${Date.now()}`,
|
|
2160
|
+
messageId: targetMessage.id,
|
|
2161
|
+
index: getOptimisticPartIndex(parts, stepIndex),
|
|
2162
|
+
stepIndex,
|
|
2163
|
+
type: "tool_call",
|
|
2164
|
+
content: JSON.stringify(contentJsonBase),
|
|
2165
|
+
contentJson: contentJsonBase,
|
|
2166
|
+
agent: targetMessage.agent,
|
|
2167
|
+
provider: targetMessage.provider,
|
|
2168
|
+
model: targetMessage.model,
|
|
2169
|
+
startedAt: Date.now(),
|
|
2170
|
+
completedAt: null,
|
|
2171
|
+
toolName: name,
|
|
2172
|
+
toolCallId: callId,
|
|
2173
|
+
toolDurationMs: null,
|
|
2174
|
+
ephemeral: true
|
|
2175
|
+
};
|
|
2176
|
+
parts.push(newPart);
|
|
2177
|
+
} else {
|
|
2178
|
+
const existing = parts[partIndex];
|
|
2179
|
+
const prev = typeof existing.contentJson?._streamedInput === "string" ? existing.contentJson._streamedInput : "";
|
|
2180
|
+
const nextContentJson = {
|
|
2181
|
+
...typeof existing.contentJson === "object" && !Array.isArray(existing.contentJson) ? existing.contentJson : {},
|
|
2182
|
+
_streamedInput: prev + delta
|
|
2183
|
+
};
|
|
2184
|
+
parts[partIndex] = {
|
|
2185
|
+
...existing,
|
|
2186
|
+
content: JSON.stringify(nextContentJson),
|
|
2187
|
+
contentJson: nextContentJson,
|
|
2188
|
+
stepIndex: stepIndex ?? existing.stepIndex ?? null
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
nextMessages[targetIndex] = { ...targetMessage, parts };
|
|
2192
|
+
return nextMessages;
|
|
2193
|
+
});
|
|
2194
|
+
};
|
|
2195
|
+
const accumulateToolOutputDelta = (payload, delta) => {
|
|
2196
|
+
if (!payload)
|
|
2197
|
+
return;
|
|
2198
|
+
const callId = getToolEventCallId(payload);
|
|
2199
|
+
const name = getToolEventName(payload);
|
|
2200
|
+
if (!name)
|
|
2201
|
+
return;
|
|
2202
|
+
queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
|
|
2203
|
+
if (!oldMessages)
|
|
2204
|
+
return oldMessages;
|
|
2205
|
+
const nextMessages = [...oldMessages];
|
|
2206
|
+
let targetIndex = resolveAssistantTargetIndex(nextMessages);
|
|
2207
|
+
if (typeof payload.messageId === "string") {
|
|
2208
|
+
const explicitIndex = nextMessages.findIndex((message) => message.id === payload.messageId);
|
|
2209
|
+
if (explicitIndex !== -1)
|
|
2210
|
+
targetIndex = explicitIndex;
|
|
2211
|
+
}
|
|
2212
|
+
if (targetIndex === -1)
|
|
2213
|
+
return oldMessages;
|
|
2214
|
+
const targetMessage = nextMessages[targetIndex];
|
|
2215
|
+
const parts = targetMessage.parts ? [...targetMessage.parts] : [];
|
|
2216
|
+
let partIndex = -1;
|
|
2217
|
+
if (callId) {
|
|
2218
|
+
partIndex = parts.findIndex((part) => part.toolCallId === callId && part.ephemeral);
|
|
2219
|
+
}
|
|
2220
|
+
if (partIndex === -1 && !callId) {
|
|
2221
|
+
partIndex = parts.findIndex((part) => part.ephemeral && part.toolName === name);
|
|
2222
|
+
}
|
|
2223
|
+
const stepIndex = typeof payload.stepIndex === "number" ? payload.stepIndex : null;
|
|
2224
|
+
if (partIndex === -1) {
|
|
2225
|
+
const contentJsonBase = {
|
|
2226
|
+
name,
|
|
2227
|
+
_streamedOutput: delta
|
|
2228
|
+
};
|
|
2229
|
+
if (callId)
|
|
2230
|
+
contentJsonBase.callId = callId;
|
|
2231
|
+
const newPart = {
|
|
2232
|
+
id: callId ? `ephemeral-tool-call-${callId}` : `ephemeral-tool-call-${name}-${Date.now()}`,
|
|
2233
|
+
messageId: targetMessage.id,
|
|
2234
|
+
index: getOptimisticPartIndex(parts, stepIndex),
|
|
2235
|
+
stepIndex,
|
|
2236
|
+
type: "tool_call",
|
|
2237
|
+
content: JSON.stringify(contentJsonBase),
|
|
2238
|
+
contentJson: contentJsonBase,
|
|
2239
|
+
agent: targetMessage.agent,
|
|
2240
|
+
provider: targetMessage.provider,
|
|
2241
|
+
model: targetMessage.model,
|
|
2242
|
+
startedAt: Date.now(),
|
|
2243
|
+
completedAt: null,
|
|
2244
|
+
toolName: name,
|
|
2245
|
+
toolCallId: callId,
|
|
2246
|
+
toolDurationMs: null,
|
|
2247
|
+
ephemeral: true
|
|
2248
|
+
};
|
|
2249
|
+
parts.push(newPart);
|
|
2250
|
+
} else {
|
|
2251
|
+
const existing = parts[partIndex];
|
|
2252
|
+
const prev = typeof existing.contentJson?._streamedOutput === "string" ? existing.contentJson._streamedOutput : "";
|
|
2253
|
+
const nextContentJson = {
|
|
2254
|
+
...typeof existing.contentJson === "object" && !Array.isArray(existing.contentJson) ? existing.contentJson : {},
|
|
2255
|
+
_streamedOutput: prev + delta
|
|
2256
|
+
};
|
|
2257
|
+
parts[partIndex] = {
|
|
2258
|
+
...existing,
|
|
2259
|
+
content: JSON.stringify(nextContentJson),
|
|
2260
|
+
contentJson: nextContentJson,
|
|
2261
|
+
stepIndex: stepIndex ?? existing.stepIndex ?? null
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
nextMessages[targetIndex] = { ...targetMessage, parts };
|
|
2265
|
+
return nextMessages;
|
|
2266
|
+
});
|
|
2267
|
+
};
|
|
2057
2268
|
const resolveEphemeralToolCall = (payload) => {
|
|
2058
|
-
const callId =
|
|
2269
|
+
const callId = getToolEventCallId(payload);
|
|
2059
2270
|
if (!callId)
|
|
2060
2271
|
return;
|
|
2061
|
-
const payloadName =
|
|
2272
|
+
const payloadName = getToolEventName(payload);
|
|
2062
2273
|
const payloadStepIndex = typeof payload?.stepIndex === "number" ? payload.stepIndex : null;
|
|
2063
2274
|
const payloadResult = payload?.result;
|
|
2064
2275
|
const payloadArtifact = payload?.artifact;
|
|
2065
|
-
const payloadArgs = payload
|
|
2276
|
+
const payloadArgs = getToolEventArgs(payload);
|
|
2066
2277
|
queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
|
|
2067
2278
|
if (!oldMessages)
|
|
2068
2279
|
return oldMessages;
|
|
@@ -2110,7 +2321,7 @@ function useSessionStream(sessionId) {
|
|
|
2110
2321
|
});
|
|
2111
2322
|
};
|
|
2112
2323
|
const removeEphemeralToolCall = (payload) => {
|
|
2113
|
-
const callId =
|
|
2324
|
+
const callId = getToolEventCallId(payload);
|
|
2114
2325
|
if (!callId)
|
|
2115
2326
|
return;
|
|
2116
2327
|
queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
|
|
@@ -2169,20 +2380,6 @@ function useSessionStream(sessionId) {
|
|
|
2169
2380
|
return nextMessages;
|
|
2170
2381
|
});
|
|
2171
2382
|
};
|
|
2172
|
-
const throttledInvalidate = () => {
|
|
2173
|
-
const now = Date.now();
|
|
2174
|
-
if (now - lastInvalidationRef.current < 500) {
|
|
2175
|
-
return;
|
|
2176
|
-
}
|
|
2177
|
-
lastInvalidationRef.current = now;
|
|
2178
|
-
queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
|
|
2179
|
-
};
|
|
2180
|
-
const invalidatingEvents = new Set([
|
|
2181
|
-
"message.completed",
|
|
2182
|
-
"message.updated",
|
|
2183
|
-
"finish-step",
|
|
2184
|
-
"error"
|
|
2185
|
-
]);
|
|
2186
2383
|
const unsubscribe = client2.on("*", (event) => {
|
|
2187
2384
|
const payload = event.payload;
|
|
2188
2385
|
switch (event.type) {
|
|
@@ -2196,6 +2393,26 @@ function useSessionStream(sessionId) {
|
|
|
2196
2393
|
const agent = typeof payload?.agent === "string" ? payload.agent : "";
|
|
2197
2394
|
const provider = typeof payload?.provider === "string" ? payload.provider : "";
|
|
2198
2395
|
const model = typeof payload?.model === "string" ? payload.model : "";
|
|
2396
|
+
const content = typeof payload?.content === "string" ? payload.content : null;
|
|
2397
|
+
const userParts = role === "user" && content ? [
|
|
2398
|
+
{
|
|
2399
|
+
id: `${id}-text`,
|
|
2400
|
+
messageId: id,
|
|
2401
|
+
index: 0,
|
|
2402
|
+
stepIndex: null,
|
|
2403
|
+
type: "text",
|
|
2404
|
+
content: JSON.stringify({ text: content }),
|
|
2405
|
+
contentJson: { text: content },
|
|
2406
|
+
agent,
|
|
2407
|
+
provider,
|
|
2408
|
+
model,
|
|
2409
|
+
startedAt: Date.now(),
|
|
2410
|
+
completedAt: Date.now(),
|
|
2411
|
+
toolName: null,
|
|
2412
|
+
toolCallId: null,
|
|
2413
|
+
toolDurationMs: null
|
|
2414
|
+
}
|
|
2415
|
+
] : [];
|
|
2199
2416
|
queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
|
|
2200
2417
|
if (!oldMessages)
|
|
2201
2418
|
return oldMessages;
|
|
@@ -2205,7 +2422,7 @@ function useSessionStream(sessionId) {
|
|
|
2205
2422
|
id,
|
|
2206
2423
|
sessionId,
|
|
2207
2424
|
role,
|
|
2208
|
-
status: "pending",
|
|
2425
|
+
status: role === "user" ? "complete" : "pending",
|
|
2209
2426
|
agent,
|
|
2210
2427
|
provider,
|
|
2211
2428
|
model,
|
|
@@ -2216,13 +2433,12 @@ function useSessionStream(sessionId) {
|
|
|
2216
2433
|
completionTokens: null,
|
|
2217
2434
|
totalTokens: null,
|
|
2218
2435
|
error: null,
|
|
2219
|
-
parts:
|
|
2436
|
+
parts: userParts
|
|
2220
2437
|
};
|
|
2221
2438
|
const next = [...oldMessages, newMessage];
|
|
2222
2439
|
next.sort((a, b) => a.createdAt - b.createdAt);
|
|
2223
2440
|
return next;
|
|
2224
2441
|
});
|
|
2225
|
-
throttledInvalidate();
|
|
2226
2442
|
}
|
|
2227
2443
|
break;
|
|
2228
2444
|
}
|
|
@@ -2241,13 +2457,21 @@ function useSessionStream(sessionId) {
|
|
|
2241
2457
|
}
|
|
2242
2458
|
markMessageCompleted(payload);
|
|
2243
2459
|
clearEphemeralForMessage(id);
|
|
2460
|
+
queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
|
|
2244
2461
|
queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
|
|
2245
2462
|
break;
|
|
2246
2463
|
}
|
|
2247
2464
|
case "tool.delta": {
|
|
2248
2465
|
const channel = typeof payload?.channel === "string" ? payload.channel : null;
|
|
2249
|
-
|
|
2250
|
-
|
|
2466
|
+
const delta = channel === "output" ? getToolOutputDelta(payload) : getToolInputDelta(payload);
|
|
2467
|
+
if (channel === "input" || channel == null && delta) {
|
|
2468
|
+
if (delta) {
|
|
2469
|
+
accumulateToolInputDelta(payload, delta);
|
|
2470
|
+
} else {
|
|
2471
|
+
upsertEphemeralToolCall(payload);
|
|
2472
|
+
}
|
|
2473
|
+
} else if (channel === "output" && delta) {
|
|
2474
|
+
accumulateToolOutputDelta(payload, delta);
|
|
2251
2475
|
}
|
|
2252
2476
|
break;
|
|
2253
2477
|
}
|
|
@@ -2296,6 +2520,7 @@ function useSessionStream(sessionId) {
|
|
|
2296
2520
|
if (messageId) {
|
|
2297
2521
|
clearEphemeralForMessage(messageId);
|
|
2298
2522
|
}
|
|
2523
|
+
queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
|
|
2299
2524
|
break;
|
|
2300
2525
|
}
|
|
2301
2526
|
case "message.updated": {
|
|
@@ -2330,9 +2555,6 @@ function useSessionStream(sessionId) {
|
|
|
2330
2555
|
default:
|
|
2331
2556
|
break;
|
|
2332
2557
|
}
|
|
2333
|
-
if (invalidatingEvents.has(event.type)) {
|
|
2334
|
-
throttledInvalidate();
|
|
2335
|
-
}
|
|
2336
2558
|
if (event.type === "finish-step") {
|
|
2337
2559
|
const now = Date.now();
|
|
2338
2560
|
if (now - lastSessionInvalidation >= 2000) {
|
|
@@ -2350,6 +2572,7 @@ function useSessionStream(sessionId) {
|
|
|
2350
2572
|
queryClient,
|
|
2351
2573
|
addPendingApproval,
|
|
2352
2574
|
removePendingApproval,
|
|
2575
|
+
enabled,
|
|
2353
2576
|
setPendingApprovals,
|
|
2354
2577
|
updatePendingApproval
|
|
2355
2578
|
]);
|
|
@@ -3240,13 +3463,13 @@ function useFileUpload(options = {}) {
|
|
|
3240
3463
|
}
|
|
3241
3464
|
// src/hooks/useSessionFiles.ts
|
|
3242
3465
|
import { useQuery as useQuery5 } from "@tanstack/react-query";
|
|
3243
|
-
function useSessionFiles(sessionId) {
|
|
3466
|
+
function useSessionFiles(sessionId, enabled = true) {
|
|
3244
3467
|
const isExpanded = useSessionFilesStore((state) => state.isExpanded);
|
|
3245
3468
|
return useQuery5({
|
|
3246
3469
|
queryKey: ["session", sessionId, "files"],
|
|
3247
3470
|
queryFn: () => sessionId ? apiClient.getSessionFiles(sessionId) : null,
|
|
3248
|
-
enabled: !!sessionId,
|
|
3249
|
-
refetchInterval: isExpanded ? 5000 : false,
|
|
3471
|
+
enabled: !!sessionId && enabled,
|
|
3472
|
+
refetchInterval: isExpanded && enabled ? 5000 : false,
|
|
3250
3473
|
retry: 1,
|
|
3251
3474
|
staleTime: 3000
|
|
3252
3475
|
});
|
|
@@ -3438,11 +3661,11 @@ class ResearchApiClient {
|
|
|
3438
3661
|
}
|
|
3439
3662
|
}
|
|
3440
3663
|
var researchApi = new ResearchApiClient;
|
|
3441
|
-
function useResearchSessions(parentSessionId) {
|
|
3664
|
+
function useResearchSessions(parentSessionId, enabled = true) {
|
|
3442
3665
|
return useQuery8({
|
|
3443
3666
|
queryKey: ["research", "sessions", parentSessionId],
|
|
3444
3667
|
queryFn: () => researchApi.listResearchSessions(parentSessionId),
|
|
3445
|
-
enabled: !!parentSessionId,
|
|
3668
|
+
enabled: !!parentSessionId && enabled,
|
|
3446
3669
|
staleTime: 30000
|
|
3447
3670
|
});
|
|
3448
3671
|
}
|
|
@@ -4395,7 +4618,7 @@ function useTunnelStream() {
|
|
|
4395
4618
|
return { connect };
|
|
4396
4619
|
}
|
|
4397
4620
|
// src/hooks/useProviderUsage.ts
|
|
4398
|
-
import { useEffect as useEffect13, useCallback as useCallback10 } from "react";
|
|
4621
|
+
import { useEffect as useEffect13, useCallback as useCallback10, useRef as useRef6 } from "react";
|
|
4399
4622
|
|
|
4400
4623
|
// src/stores/usageStore.ts
|
|
4401
4624
|
import { create as create18 } from "zustand";
|
|
@@ -4415,16 +4638,22 @@ var useUsageStore = create18((set) => ({
|
|
|
4415
4638
|
// src/hooks/useProviderUsage.ts
|
|
4416
4639
|
var POLL_INTERVAL = 60000;
|
|
4417
4640
|
var STALE_THRESHOLD = 30000;
|
|
4641
|
+
var inflight = new Set;
|
|
4418
4642
|
function useProviderUsage(provider, authType) {
|
|
4419
4643
|
const setUsage = useUsageStore((s) => s.setUsage);
|
|
4420
4644
|
const setLoading = useUsageStore((s) => s.setLoading);
|
|
4421
4645
|
const setLastFetched = useUsageStore((s) => s.setLastFetched);
|
|
4422
4646
|
const usage = useUsageStore((s) => provider ? s.usage[provider] : undefined);
|
|
4423
|
-
const lastFetched = useUsageStore((s) => provider ? s.lastFetched[provider] : 0);
|
|
4424
4647
|
const isOAuthProvider = authType === "oauth" && (provider === "anthropic" || provider === "openai");
|
|
4425
4648
|
const fetchUsage = useCallback10(async () => {
|
|
4426
4649
|
if (!provider || !isOAuthProvider)
|
|
4427
4650
|
return;
|
|
4651
|
+
if (inflight.has(provider))
|
|
4652
|
+
return;
|
|
4653
|
+
const last = useUsageStore.getState().lastFetched[provider] ?? 0;
|
|
4654
|
+
if (last && Date.now() - last < STALE_THRESHOLD)
|
|
4655
|
+
return;
|
|
4656
|
+
inflight.add(provider);
|
|
4428
4657
|
setLoading(provider, true);
|
|
4429
4658
|
try {
|
|
4430
4659
|
const data = await apiClient.getProviderUsage(provider);
|
|
@@ -4432,18 +4661,18 @@ function useProviderUsage(provider, authType) {
|
|
|
4432
4661
|
setLastFetched(provider, Date.now());
|
|
4433
4662
|
} catch {} finally {
|
|
4434
4663
|
setLoading(provider, false);
|
|
4664
|
+
inflight.delete(provider);
|
|
4435
4665
|
}
|
|
4436
4666
|
}, [provider, isOAuthProvider, setUsage, setLoading, setLastFetched]);
|
|
4667
|
+
const fetchRef = useRef6(fetchUsage);
|
|
4668
|
+
fetchRef.current = fetchUsage;
|
|
4437
4669
|
useEffect13(() => {
|
|
4438
4670
|
if (!isOAuthProvider)
|
|
4439
4671
|
return;
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
fetchUsage();
|
|
4443
|
-
}
|
|
4444
|
-
const interval = setInterval(fetchUsage, POLL_INTERVAL);
|
|
4672
|
+
fetchRef.current();
|
|
4673
|
+
const interval = setInterval(() => fetchRef.current(), POLL_INTERVAL);
|
|
4445
4674
|
return () => clearInterval(interval);
|
|
4446
|
-
}, [isOAuthProvider,
|
|
4675
|
+
}, [isOAuthProvider, provider]);
|
|
4447
4676
|
return {
|
|
4448
4677
|
usage,
|
|
4449
4678
|
fetchUsage,
|
|
@@ -4481,7 +4710,7 @@ function useGitDiffFullFile(file, staged = false, enabled = false) {
|
|
|
4481
4710
|
}
|
|
4482
4711
|
// src/hooks/useMCP.ts
|
|
4483
4712
|
import { useQuery as useQuery12, useMutation as useMutation8, useQueryClient as useQueryClient10 } from "@tanstack/react-query";
|
|
4484
|
-
import { useEffect as useEffect14, useRef as
|
|
4713
|
+
import { useEffect as useEffect14, useRef as useRef7, useCallback as useCallback11 } from "react";
|
|
4485
4714
|
import {
|
|
4486
4715
|
listMcpServers,
|
|
4487
4716
|
startMcpServer,
|
|
@@ -4647,7 +4876,7 @@ function useCopilotDevicePoller() {
|
|
|
4647
4876
|
const setCopilotDevice = useMCPStore((s) => s.setCopilotDevice);
|
|
4648
4877
|
const setLoading = useMCPStore((s) => s.setLoading);
|
|
4649
4878
|
const queryClient = useQueryClient10();
|
|
4650
|
-
const timerRef =
|
|
4879
|
+
const timerRef = useRef7(null);
|
|
4651
4880
|
const stopPolling = useCallback11(() => {
|
|
4652
4881
|
if (timerRef.current) {
|
|
4653
4882
|
clearInterval(timerRef.current);
|
|
@@ -4818,4 +5047,4 @@ export {
|
|
|
4818
5047
|
sessionsQueryKey
|
|
4819
5048
|
};
|
|
4820
5049
|
|
|
4821
|
-
//# debugId=
|
|
5050
|
+
//# debugId=E3072251705A9AC764756E2164756E21
|