Skip to content

Commit d25c243

Browse files
committed
Fix file tools
1 parent 69d69ee commit d25c243

File tree

17 files changed

+5317
-3226
lines changed

17 files changed

+5317
-3226
lines changed

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,19 @@ export function ToolCallItem({ toolName, displayTitle, status, streamingArgs }:
107107
const opMatch = streamingArgs.match(/"operation"\s*:\s*"(\w+)"/)
108108
const op = opMatch?.[1] ?? ''
109109
const verb =
110-
op === 'patch' || op === 'update' || op === 'rename'
111-
? 'Editing'
112-
: op === 'delete'
113-
? 'Deleting'
114-
: 'Writing'
110+
op === 'create'
111+
? 'Creating'
112+
: op === 'append'
113+
? 'Adding'
114+
: op === 'patch'
115+
? 'Editing'
116+
: op === 'update'
117+
? 'Writing'
118+
: op === 'rename'
119+
? 'Renaming'
120+
: op === 'delete'
121+
? 'Deleting'
122+
: 'Writing'
115123
const unescaped = titleMatch[1]
116124
.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex: string) =>
117125
String.fromCharCode(Number.parseInt(hex, 16))

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ const MARKDOWN_COMPONENTS = {
155155
},
156156
inlineCode({ children }: { children?: React.ReactNode }) {
157157
return (
158-
<code className='rounded bg-[var(--surface-5)] px-1.5 py-0.5 font-mono text-small font-[400] text-[var(--text-primary)] before:content-none after:content-none'>
158+
<code className='rounded bg-[var(--surface-5)] px-1.5 py-0.5 font-[400] font-mono text-[var(--text-primary)] text-small before:content-none after:content-none'>
159159
{children}
160160
</code>
161161
)

apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const TOOL_ICONS: Record<string, IconComponent> = {
4141
superagent: Blimp,
4242
user_table: TableIcon,
4343
workspace_file: File,
44+
edit_content: File,
4445
create_workflow: Layout,
4546
edit_workflow: Pencil,
4647
workflow: Hammer,

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

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,22 @@ export const ResourceContent = memo(function ResourceContent({
103103
const isUpdateStream = streamOperation === 'update'
104104

105105
const { data: allFiles = [] } = useWorkspaceFiles(workspaceId)
106-
const activeFileRecord = useMemo(() => {
107-
if (!isPatchStream || resource.type !== 'file') return undefined
108-
return allFiles.find((f) => f.id === resource.id)
109-
}, [isPatchStream, resource, allFiles])
106+
const previewFileId =
107+
streamingFile?.fileId ?? (resource.type === 'file' ? resource.id : undefined)
108+
const previewFileRecord = useMemo(() => {
109+
if (!previewFileId) return undefined
110+
return allFiles.find((f) => f.id === previewFileId)
111+
}, [previewFileId, allFiles])
110112

111113
const isSourceMime =
112-
activeFileRecord?.type === 'text/x-pptxgenjs' ||
113-
activeFileRecord?.type === 'text/x-docxjs' ||
114-
activeFileRecord?.type === 'text/x-pdflibjs'
114+
previewFileRecord?.type === 'text/x-pptxgenjs' ||
115+
previewFileRecord?.type === 'text/x-docxjs' ||
116+
previewFileRecord?.type === 'text/x-pdflibjs'
115117

116118
const { data: fetchedFileContent } = useWorkspaceFileContent(
117119
workspaceId,
118-
activeFileRecord?.id ?? '',
119-
activeFileRecord?.key ?? '',
120+
previewFileRecord?.id ?? '',
121+
previewFileRecord?.key ?? '',
120122
isSourceMime
121123
)
122124

@@ -125,15 +127,28 @@ export const ResourceContent = memo(function ResourceContent({
125127
if (!streamOperation) return undefined
126128

127129
if (isPatchStream) {
128-
if (!fetchedFileContent) return undefined
130+
if (fetchedFileContent === undefined) return undefined
131+
if (!shouldApplyPatchPreview(streamingFile)) return undefined
129132
return extractPatchPreview(streamingFile, fetchedFileContent)
130133
}
131134

132135
const extracted = streamingFile.content
133-
if (extracted.length === 0) return undefined
134136

135137
if (isUpdateStream) return extracted
136-
if (isWriteStream) return extracted
138+
139+
if (streamOperation === 'append') {
140+
if (streamingFile.targetKind === 'file_id') {
141+
if (fetchedFileContent === undefined) return undefined
142+
return buildAppendPreview(fetchedFileContent, extracted)
143+
}
144+
return extracted.length > 0 ? extracted : undefined
145+
}
146+
147+
if (streamOperation === 'create') {
148+
return extracted.length > 0 ? extracted : undefined
149+
}
150+
151+
if (isWriteStream) return extracted.length > 0 ? extracted : undefined
137152

138153
return undefined
139154
}, [
@@ -165,16 +180,11 @@ export const ResourceContent = memo(function ResourceContent({
165180
}
166181
}, [workspaceId, streamFileName])
167182

168-
// workspace_file preview events now carry whole-file snapshots, not deltas.
169-
// Treat every live preview as replace so the viewer shows the latest snapshot.
183+
// ResourceContent now reconstructs full-file preview text per operation,
184+
// so the viewer can always treat streaming content as a whole-file replace.
170185
const streamingFileMode: 'append' | 'replace' = 'replace'
171186

172-
// For existing file resources (not streaming-file), only pass streaming
173-
// content for patch operations where the preview splices new content into
174-
// the displayed file. Update operations re-stream the entire file from
175-
// scratch which causes visual duplication of already-visible content.
176-
const embeddedStreamingContent =
177-
resource.id !== 'streaming-file' && isUpdateStream ? undefined : streamingExtractedContent
187+
const embeddedStreamingContent = streamingExtractedContent
178188

179189
if (streamingFile && resource.id === 'streaming-file') {
180190
return (
@@ -700,3 +710,27 @@ function extractPatchPreview(
700710

701711
return undefined
702712
}
713+
714+
function shouldApplyPatchPreview(streamingFile: {
715+
content: string
716+
edit?: Record<string, unknown>
717+
}): boolean {
718+
const edit = streamingFile.edit ?? {}
719+
const strategy = typeof edit.strategy === 'string' ? edit.strategy : undefined
720+
const mode = typeof edit.mode === 'string' ? edit.mode : undefined
721+
722+
// delete_between is delete-only and can be previewed from intent metadata alone.
723+
if (strategy === 'anchored' && mode === 'delete_between') {
724+
return true
725+
}
726+
727+
// For all other patch modes, keep the visible file unchanged until
728+
// edit_content actually streams content into the target location.
729+
return streamingFile.content.length > 0
730+
}
731+
732+
function buildAppendPreview(existingContent: string, incomingContent: string): string {
733+
if (incomingContent.length === 0) return existingContent
734+
if (existingContent.length === 0) return incomingContent
735+
return `${existingContent}\n${incomingContent}`
736+
}

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

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,11 +1071,19 @@ export function useChat(
10711071
const opMatch = tc.streamingArgs.match(/"operation"\s*:\s*"(\w+)"/)
10721072
const op = opMatch?.[1] ?? ''
10731073
const verb =
1074-
op === 'patch' || op === 'update' || op === 'rename'
1075-
? 'Editing'
1076-
: op === 'delete'
1077-
? 'Deleting'
1078-
: 'Writing'
1074+
op === 'create'
1075+
? 'Creating'
1076+
: op === 'append'
1077+
? 'Adding'
1078+
: op === 'patch'
1079+
? 'Editing'
1080+
: op === 'update'
1081+
? 'Writing'
1082+
: op === 'rename'
1083+
? 'Renaming'
1084+
: op === 'delete'
1085+
? 'Deleting'
1086+
: 'Writing'
10791087
const titleMatch = tc.streamingArgs.match(/"title"\s*:\s*"([^"]*)"/)
10801088
if (titleMatch?.[1]) {
10811089
const unescaped = titleMatch[1]
@@ -1188,7 +1196,20 @@ export function useChat(
11881196
clientExecutionStartedRef.current.delete(id)
11891197
}
11901198

1191-
if (tc.name === WorkspaceFile.id) {
1199+
const workspaceFileOperation =
1200+
tc.name === WorkspaceFile.id && typeof tc.params?.operation === 'string'
1201+
? tc.params.operation
1202+
: undefined
1203+
const shouldKeepWorkspacePreviewOpen =
1204+
tc.name === WorkspaceFile.id &&
1205+
(workspaceFileOperation === 'append' ||
1206+
workspaceFileOperation === 'update' ||
1207+
workspaceFileOperation === 'patch')
1208+
1209+
if (
1210+
(tc.name === WorkspaceFile.id || tc.name === 'edit_content') &&
1211+
!shouldKeepWorkspacePreviewOpen
1212+
) {
11921213
filePreviewSessionsRef.current.delete(id)
11931214
if (activeFilePreviewToolCallIdRef.current === id) {
11941215
activeFilePreviewToolCallIdRef.current = null
@@ -1236,11 +1257,19 @@ export function useChat(
12361257
if (name === WorkspaceFile.id) {
12371258
const operation = typeof args?.operation === 'string' ? args.operation : ''
12381259
const verb =
1239-
operation === 'patch' || operation === 'update' || operation === 'rename'
1240-
? 'Editing'
1241-
: operation === 'delete'
1242-
? 'Deleting'
1243-
: 'Writing'
1260+
operation === 'create'
1261+
? 'Creating'
1262+
: operation === 'append'
1263+
? 'Adding'
1264+
: operation === 'patch'
1265+
? 'Editing'
1266+
: operation === 'update'
1267+
? 'Writing'
1268+
: operation === 'rename'
1269+
? 'Renaming'
1270+
: operation === 'delete'
1271+
? 'Deleting'
1272+
: 'Writing'
12441273
const chunkTitle = args?.title as string | undefined
12451274
const target = args ? asPayloadRecord(args.target) : undefined
12461275
const targetFileName = target?.fileName as string | undefined

apps/sim/app/workspace/[workspaceId]/home/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
CreateWorkflow,
55
Debug,
66
Deploy,
7+
EditContent,
78
EditWorkflow,
89
FunctionExecute,
910
GetPageContents,
@@ -271,6 +272,11 @@ export const TOOL_UI_METADATA: Record<string, ToolUIMetadata> = {
271272
phaseLabel: 'Resource',
272273
phase: 'resource',
273274
},
275+
[EditContent.id]: {
276+
title: 'Writing content',
277+
phaseLabel: 'Resource',
278+
phase: 'resource',
279+
},
274280
[CreateWorkflow.id]: {
275281
title: 'Creating workflow',
276282
phaseLabel: 'Resource',

0 commit comments

Comments
 (0)