@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.
Files changed (52) hide show
  1. package/dist/components/chat/ChatInput.d.ts +1 -2
  2. package/dist/components/chat/ChatInput.d.ts.map +1 -1
  3. package/dist/components/chat/ChatInputContainer.d.ts.map +1 -1
  4. package/dist/components/chat/ConfigModal.d.ts.map +1 -1
  5. package/dist/components/chat/ConfigSelector.d.ts.map +1 -1
  6. package/dist/components/git/GitDiffPanel.d.ts.map +1 -1
  7. package/dist/components/index.js +3139 -2344
  8. package/dist/components/index.js.map +39 -37
  9. package/dist/components/messages/ActionToolBox.d.ts +8 -0
  10. package/dist/components/messages/ActionToolBox.d.ts.map +1 -0
  11. package/dist/components/messages/AssistantMessageGroup.d.ts.map +1 -1
  12. package/dist/components/messages/CompactActivityGroup.d.ts.map +1 -1
  13. package/dist/components/messages/MessageThread.d.ts.map +1 -1
  14. package/dist/components/messages/compactActivity.d.ts +1 -0
  15. package/dist/components/messages/compactActivity.d.ts.map +1 -1
  16. package/dist/components/onboarding/steps/DefaultsStep.d.ts.map +1 -1
  17. package/dist/components/research/ResearchSidebar.d.ts.map +1 -1
  18. package/dist/components/session-files/SessionFilesSidebar.d.ts.map +1 -1
  19. package/dist/components/sessions/SessionHeader.d.ts.map +1 -1
  20. package/dist/components/sessions/SessionItem.d.ts.map +1 -1
  21. package/dist/components/sessions/SessionListContainer.d.ts.map +1 -1
  22. package/dist/components/sessions/session-time.d.ts +9 -0
  23. package/dist/components/sessions/session-time.d.ts.map +1 -0
  24. package/dist/components/settings/SettingsSidebar.d.ts.map +1 -1
  25. package/dist/components/terminals/TerminalViewer.d.ts.map +1 -1
  26. package/dist/hooks/index.js +289 -60
  27. package/dist/hooks/index.js.map +11 -11
  28. package/dist/hooks/useConfig.d.ts +22 -1
  29. package/dist/hooks/useConfig.d.ts.map +1 -1
  30. package/dist/hooks/useMessages.d.ts +6 -1
  31. package/dist/hooks/useMessages.d.ts.map +1 -1
  32. package/dist/hooks/usePreferences.d.ts +4 -1
  33. package/dist/hooks/usePreferences.d.ts.map +1 -1
  34. package/dist/hooks/useProviderUsage.d.ts.map +1 -1
  35. package/dist/hooks/useResearch.d.ts +1 -1
  36. package/dist/hooks/useResearch.d.ts.map +1 -1
  37. package/dist/hooks/useSessionFiles.d.ts +1 -1
  38. package/dist/hooks/useSessionFiles.d.ts.map +1 -1
  39. package/dist/hooks/useSessionStream.d.ts +1 -1
  40. package/dist/hooks/useSessionStream.d.ts.map +1 -1
  41. package/dist/index.js +3151 -2356
  42. package/dist/index.js.map +39 -37
  43. package/dist/lib/api-client/config.d.ts +6 -0
  44. package/dist/lib/api-client/config.d.ts.map +1 -1
  45. package/dist/lib/api-client/index.d.ts +6 -0
  46. package/dist/lib/api-client/index.d.ts.map +1 -1
  47. package/dist/lib/index.js +3 -1
  48. package/dist/lib/index.js.map +4 -4
  49. package/dist/lib/sse-client.d.ts.map +1 -1
  50. package/dist/types/api.d.ts +3 -0
  51. package/dist/types/api.d.ts.map +1 -1
  52. package/package.json +3 -3
@@ -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 DEFAULT_PREFERENCES = {
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 DEFAULT_PREFERENCES;
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
- ...DEFAULT_PREFERENCES,
949
- ...parsed
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 DEFAULT_PREFERENCES;
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 DEFAULT_PREFERENCES;
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
- updateStore(updates);
989
- }, []);
990
- return useMemo(() => ({ preferences: currentPreferences, updatePreferences }), [currentPreferences, updatePreferences]);
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 = typeof payload.callId === "string" ? payload.callId : null;
1983
- const name = typeof payload.name === "string" ? payload.name : null;
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.args;
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 = typeof payload?.callId === "string" ? payload.callId : null;
2269
+ const callId = getToolEventCallId(payload);
2059
2270
  if (!callId)
2060
2271
  return;
2061
- const payloadName = typeof payload?.name === "string" ? payload.name : null;
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?.args;
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 = typeof payload?.callId === "string" ? payload.callId : null;
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
- if (channel === "input") {
2250
- upsertEphemeralToolCall(payload);
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
- const isStale = !lastFetched || Date.now() - lastFetched > STALE_THRESHOLD;
4441
- if (isStale) {
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, fetchUsage, lastFetched]);
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 useRef6, useCallback as useCallback11 } from "react";
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 = useRef6(null);
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=381FB663C2D4B88E64756E2164756E21
5050
+ //# debugId=E3072251705A9AC764756E2164756E21