Skip to content

Commit d22f367

Browse files
committed
Fix
1 parent fd4fa1c commit d22f367

7 files changed

Lines changed: 67 additions & 124 deletions

File tree

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ function TextEditor({
243243
const [savedContent, setSavedContent] = useState('')
244244
const savedContentRef = useRef('')
245245
const wasStreamingRef = useRef(false)
246+
const pendingStreamReconcileRef = useRef(false)
247+
const lastStreamedContentRef = useRef<string | null>(null)
246248

247249
useEffect(() => {
248250
if (streamingContent !== undefined) {
@@ -254,41 +256,31 @@ function TextEditor({
254256
fetchedContent.endsWith(`\n${streamingContent}`)
255257
? fetchedContent
256258
: `${fetchedContent}\n${streamingContent}`
257-
// #region agent log
258-
fetch('http://127.0.0.1:7774/ingest/b056eec6-a1ee-457f-8556-85f94314ca06', {
259-
method: 'POST',
260-
headers: { 'Content-Type': 'application/json', 'X-Debug-Session-Id': '6f10b0' },
261-
body: JSON.stringify({
262-
sessionId: '6f10b0',
263-
location: 'file-viewer.tsx:TextEditor-merge',
264-
message: 'streaming merge',
265-
data: {
266-
streamingMode,
267-
fetchedContentLen: fetchedContent?.length,
268-
streamingContentLen: streamingContent.length,
269-
nextContentLen: nextContent.length,
270-
fetchedUndefined: fetchedContent === undefined,
271-
usedReplace: streamingMode === 'replace' || fetchedContent === undefined,
272-
nextPreview: nextContent.slice(0, 200),
273-
},
274-
timestamp: Date.now(),
275-
hypothesisId: 'H2-H3',
276-
}),
277-
}).catch(() => {})
278-
// #endregion
259+
pendingStreamReconcileRef.current = true
260+
lastStreamedContentRef.current = nextContent
279261
setContent(nextContent)
280262
contentRef.current = nextContent
281263
initializedRef.current = true
282264
return
283265
}
284266

285-
if (wasStreamingRef.current) {
286-
if (fetchedContent !== undefined && fetchedContent !== savedContentRef.current) {
267+
if (wasStreamingRef.current && pendingStreamReconcileRef.current) {
268+
const lastStreamed = lastStreamedContentRef.current
269+
const hasFetchedAdvanced =
270+
fetchedContent !== undefined && fetchedContent !== savedContentRef.current
271+
const fetchedMatchesLastStream =
272+
fetchedContent !== undefined && lastStreamed !== null && fetchedContent === lastStreamed
273+
274+
if (hasFetchedAdvanced || fetchedMatchesLastStream) {
275+
pendingStreamReconcileRef.current = false
287276
wasStreamingRef.current = false
288-
setContent(fetchedContent)
277+
lastStreamedContentRef.current = null
278+
if (lastStreamed !== null && contentRef.current === lastStreamed) {
279+
setContent(fetchedContent)
280+
contentRef.current = fetchedContent
281+
}
289282
setSavedContent(fetchedContent)
290283
savedContentRef.current = fetchedContent
291-
contentRef.current = fetchedContent
292284
return
293285
}
294286
}
@@ -316,7 +308,7 @@ function TextEditor({
316308
savedContentRef.current = fetchedContent
317309
contentRef.current = fetchedContent
318310
}
319-
}, [streamingContent, fetchedContent, dataUpdatedAt, autoFocus])
311+
}, [streamingContent, fetchedContent, streamingMode, dataUpdatedAt, autoFocus])
320312

321313
const handleContentChange = useCallback((value: string) => {
322314
setContent(value)

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx

Lines changed: 2 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -120,102 +120,18 @@ export const ResourceContent = memo(function ResourceContent({
120120
isSourceMime
121121
)
122122

123-
// #region agent log
124-
if (streamingFile) {
125-
fetch('http://127.0.0.1:7774/ingest/b056eec6-a1ee-457f-8556-85f94314ca06', {
126-
method: 'POST',
127-
headers: { 'Content-Type': 'application/json', 'X-Debug-Session-Id': '6f10b0' },
128-
body: JSON.stringify({
129-
sessionId: '6f10b0',
130-
location: 'resource-content.tsx:streaming-context',
131-
message: 'streaming state',
132-
data: {
133-
resourceId: resource.id,
134-
resourceType: resource.type,
135-
streamOp: streamOperation,
136-
isPatch: isPatchStream,
137-
isWrite: isWriteStream,
138-
isUpdate: isUpdateStream,
139-
hasActiveFileRecord: !!activeFileRecord,
140-
hasFetchedContent: !!fetchedFileContent,
141-
fetchedContentLen: fetchedFileContent?.length,
142-
streamingFileContentLen: streamingFile.content.length,
143-
streamingFileName: streamingFile.fileName,
144-
streamingFileMode: isWriteStream ? 'append' : 'replace',
145-
},
146-
timestamp: Date.now(),
147-
}),
148-
}).catch(() => {})
149-
}
150-
// #endregion
151123
const streamingExtractedContent = useMemo(() => {
152124
if (!streamingFile) return undefined
153125
if (!streamOperation) return undefined
154126

155127
if (isPatchStream) {
156-
if (!fetchedFileContent) {
157-
// #region agent log
158-
fetch('http://127.0.0.1:7774/ingest/b056eec6-a1ee-457f-8556-85f94314ca06', {
159-
method: 'POST',
160-
headers: { 'Content-Type': 'application/json', 'X-Debug-Session-Id': '6f10b0' },
161-
body: JSON.stringify({
162-
sessionId: '6f10b0',
163-
location: 'resource-content.tsx:patch-no-fetched',
164-
message: 'patch but no fetchedFileContent',
165-
data: { resourceId: resource.id, activeFileRecordId: activeFileRecord?.id },
166-
timestamp: Date.now(),
167-
hypothesisId: 'H1',
168-
}),
169-
}).catch(() => {})
170-
// #endregion
171-
return undefined
172-
}
173-
const patchResult = extractPatchPreview(streamingFile, fetchedFileContent)
174-
// #region agent log
175-
fetch('http://127.0.0.1:7774/ingest/b056eec6-a1ee-457f-8556-85f94314ca06', {
176-
method: 'POST',
177-
headers: { 'Content-Type': 'application/json', 'X-Debug-Session-Id': '6f10b0' },
178-
body: JSON.stringify({
179-
sessionId: '6f10b0',
180-
location: 'resource-content.tsx:patch-result',
181-
message: 'extractPatchPreview result',
182-
data: {
183-
hasPatchResult: !!patchResult,
184-
patchResultLen: patchResult?.length,
185-
fetchedLen: fetchedFileContent.length,
186-
contentPreview: streamingFile.content.slice(0, 200),
187-
edit: streamingFile.edit,
188-
},
189-
timestamp: Date.now(),
190-
hypothesisId: 'H4',
191-
}),
192-
}).catch(() => {})
193-
// #endregion
194-
return patchResult
128+
if (!fetchedFileContent) return undefined
129+
return extractPatchPreview(streamingFile, fetchedFileContent)
195130
}
196131

197132
const extracted = streamingFile.content
198133
if (extracted.length === 0) return undefined
199134

200-
// #region agent log
201-
fetch('http://127.0.0.1:7774/ingest/b056eec6-a1ee-457f-8556-85f94314ca06', {
202-
method: 'POST',
203-
headers: { 'Content-Type': 'application/json', 'X-Debug-Session-Id': '6f10b0' },
204-
body: JSON.stringify({
205-
sessionId: '6f10b0',
206-
location: 'resource-content.tsx:write-update-content',
207-
message: 'extracted content for write/update',
208-
data: {
209-
streamOp: streamOperation,
210-
extractedLen: extracted.length,
211-
extractedPreview: extracted.slice(0, 150),
212-
},
213-
timestamp: Date.now(),
214-
hypothesisId: 'H2',
215-
}),
216-
}).catch(() => {})
217-
// #endregion
218-
219135
if (isUpdateStream) return extracted
220136
if (isWriteStream) return extracted
221137

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -958,12 +958,16 @@ export function useChat(
958958

959959
if (previewPhase === 'file_preview_content') {
960960
const content = typeof payload.content === 'string' ? payload.content : ''
961+
const isAppendOp = prevSession.operation === 'append'
962+
const prevContent = streamingFileRef.current?.content ?? ''
963+
const nextContent = isAppendOp ? prevContent + content : content
961964
const nextSession: StreamingFilePreview = {
962965
...prevSession,
963-
content,
966+
content: nextContent,
964967
}
965968
sessions.set(id, nextSession)
966969
activeFilePreviewToolCallIdRef.current = id
970+
streamingFileRef.current = nextSession
967971
setStreamingFile(nextSession)
968972
break
969973
}
@@ -1104,8 +1108,10 @@ export function useChat(
11041108
filePreviewSessionsRef.current.delete(id)
11051109
if (activeFilePreviewToolCallIdRef.current === id) {
11061110
activeFilePreviewToolCallIdRef.current = null
1107-
setStreamingFile(null)
1108-
streamingFileRef.current = null
1111+
if (!activeSubagent || activeSubagent !== FileTool.id) {
1112+
setStreamingFile(null)
1113+
streamingFileRef.current = null
1114+
}
11091115
}
11101116
const fileResource = extractedResources.find((r) => r.type === 'file')
11111117
if (fileResource) {
@@ -1118,7 +1124,7 @@ export function useChat(
11181124
})
11191125
setActiveResourceId(fileResource.id)
11201126
invalidateResourceQueries(queryClient, workspaceId, 'file', fileResource.id)
1121-
} else {
1127+
} else if (!activeSubagent || activeSubagent !== FileTool.id) {
11221128
setResources((rs) => rs.filter((r) => r.id !== 'streaming-file'))
11231129
}
11241130
}
@@ -1335,7 +1341,7 @@ export function useChat(
13351341
if (isPendingPause) {
13361342
break
13371343
}
1338-
if (streamingFileRef.current) {
1344+
if (streamingFileRef.current && !activeFilePreviewToolCallIdRef.current) {
13391345
setStreamingFile(null)
13401346
streamingFileRef.current = null
13411347
const lastFileResource = resourcesRef.current.find(

apps/sim/lib/copilot/request/handlers/handlers.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ vi.mock('@/lib/copilot/tool-executor', () => ({
2424
isSimExecuted,
2525
executeTool,
2626
ensureHandlersRegistered,
27+
getToolEntry: vi.fn().mockReturnValue(undefined),
2728
}))
2829

2930
vi.mock('@/lib/copilot/async-runs/repository', async () => {

apps/sim/lib/copilot/tools/server/files/workspace-file.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,22 @@ export const workspaceFileServerTool: BaseServerTool<WorkspaceFileArgs, Workspac
152152
throw new Error('Authentication required')
153153
}
154154

155-
const normalized = params
155+
const raw = params as Record<string, unknown>
156+
const nested = raw.args as Record<string, unknown> | undefined
157+
const normalized: WorkspaceFileArgs =
158+
params.operation && params.target
159+
? params
160+
: nested && typeof nested === 'object'
161+
? {
162+
operation: (nested.operation ?? raw.operation) as WorkspaceFileOperation,
163+
target: (nested.target ?? raw.target) as WorkspaceFileTarget | undefined,
164+
title: (nested.title ?? raw.title) as string | undefined,
165+
content: (nested.content ?? raw.content) as string | undefined,
166+
contentType: (nested.contentType ?? raw.contentType) as string | undefined,
167+
newName: (nested.newName ?? raw.newName) as string | undefined,
168+
edit: (nested.edit ?? raw.edit) as WorkspaceFileEdit | undefined,
169+
}
170+
: params
156171
const { operation } = normalized
157172
const workspaceId = context.workspaceId
158173

apps/sim/lib/copilot/tools/server/router.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,25 @@ export async function routeExecution(
159159

160160
assertServerToolNotAborted(context)
161161

162-
// Validate input if tool declares a schema; otherwise fall back to the
163-
// generated JSON schema contract emitted from Go.
162+
// Go injects chatId/workspaceId and may wrap the model's args inside a
163+
// nested "args" object. Unwrap that before validation so the generated
164+
// JSON Schema sees the flat tool contract shape.
165+
let normalizedPayload = payload ?? {}
166+
if (
167+
normalizedPayload &&
168+
typeof normalizedPayload === 'object' &&
169+
!Array.isArray(normalizedPayload)
170+
) {
171+
const raw = normalizedPayload as Record<string, unknown>
172+
if (raw.args && typeof raw.args === 'object' && !raw.operation) {
173+
const nested = raw.args as Record<string, unknown>
174+
normalizedPayload = { ...nested, ...raw, args: undefined }
175+
}
176+
}
177+
164178
const args = tool.inputSchema
165-
? tool.inputSchema.parse(payload ?? {})
166-
: validateGeneratedToolPayload(toolName, 'parameters', payload ?? {})
179+
? tool.inputSchema.parse(normalizedPayload)
180+
: validateGeneratedToolPayload(toolName, 'parameters', normalizedPayload)
167181

168182
assertServerToolNotAborted(context)
169183

bun.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)