Skip to content

Commit 6277e3e

Browse files
committed
fix(vfs): fix 500 char limit in vfs for skills and custom tools
1 parent 368fe86 commit 6277e3e

2 files changed

Lines changed: 63 additions & 27 deletions

File tree

apps/sim/lib/copilot/vfs/serializers.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { truncate } from '@sim/utils/string'
21
import { getCopilotToolDescription } from '@/lib/copilot/tools/descriptions'
32
import { isHosted } from '@/lib/core/config/env-flags'
43
import { isSubBlockHidden } from '@/lib/workflows/subblocks/visibility'
@@ -669,7 +668,7 @@ export function serializeCustomTool(tool: {
669668
id: tool.id,
670669
title: tool.title,
671670
schema: tool.schema,
672-
codePreview: truncate(tool.code, 500),
671+
code: tool.code,
673672
},
674673
null,
675674
2
@@ -716,7 +715,7 @@ export function serializeSkill(s: {
716715
id: s.id,
717716
name: s.name,
718717
description: s.description,
719-
contentPreview: truncate(s.content, 500),
718+
content: s.content,
720719
createdAt: s.createdAt.toISOString(),
721720
},
722721
null,

apps/sim/lib/copilot/vfs/workspace-vfs.ts

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { db } from '@sim/db'
33
import {
44
chat as chatTable,
55
copilotChats,
6+
customTools as customToolsTable,
67
document,
78
jobExecutionLogs,
89
knowledgeConnector,
910
mcpServers as mcpServersTable,
11+
skill as skillTable,
1012
workflowDeploymentVersion,
1113
workflowExecutionLogs,
1214
workflowFolder,
@@ -16,7 +18,7 @@ import {
1618
} from '@sim/db/schema'
1719
import { createLogger } from '@sim/logger'
1820
import { toError } from '@sim/utils/errors'
19-
import { and, desc, eq, isNotNull, isNull, ne, sql } from 'drizzle-orm'
21+
import { and, desc, eq, isNotNull, isNull, ne, or, sql } from 'drizzle-orm'
2022
import { listApiKeys } from '@/lib/api-key/service'
2123
import {
2224
buildWorkspaceContextMd,
@@ -109,13 +111,13 @@ import {
109111
type WorkspaceFileRecord,
110112
} from '@/lib/uploads/contexts/workspace/workspace-file-manager'
111113
import { listCustomBlocksWithInputsForWorkspace } from '@/lib/workflows/custom-blocks/operations'
112-
import { listCustomTools } from '@/lib/workflows/custom-tools/operations'
114+
import { getCustomToolById } from '@/lib/workflows/custom-tools/operations'
113115
import {
114116
loadWorkflowDeploymentSnapshot,
115117
loadWorkflowFromNormalizedTables,
116118
} from '@/lib/workflows/persistence/utils'
117119
import { sanitizeForCopilot } from '@/lib/workflows/sanitization/json-sanitizer'
118-
import { listSkills } from '@/lib/workflows/skills/operations'
120+
import { getSkillById } from '@/lib/workflows/skills/operations'
119121
import { listFolders, listWorkflows } from '@/lib/workflows/utils'
120122
import {
121123
assertActiveWorkspaceAccess,
@@ -1878,25 +1880,47 @@ export class WorkspaceVFS {
18781880
}
18791881

18801882
/**
1881-
* Materialize custom tools using the shared listCustomTools function.
1883+
* Advertise custom tools in the VFS without eagerly loading their code.
1884+
* Paths are registered as lazy so glob/WORKSPACE.md see them, but full
1885+
* schema+code is fetched only when read (or a grep whose scope touches them).
18821886
*/
18831887
private async materializeCustomTools(
18841888
workspaceId: string,
18851889
userId: string
18861890
): Promise<NonNullable<WorkspaceMdData['customTools']>> {
18871891
try {
1888-
const toolRows = await listCustomTools({ userId, workspaceId })
1892+
// Metadata only — tool code can be large; keep it out of the eager map.
1893+
// Visibility matches listCustomTools: workspace tools + legacy user-owned.
1894+
const toolRows = await db
1895+
.select({
1896+
id: customToolsTable.id,
1897+
title: customToolsTable.title,
1898+
})
1899+
.from(customToolsTable)
1900+
.where(
1901+
or(
1902+
eq(customToolsTable.workspaceId, workspaceId),
1903+
and(isNull(customToolsTable.workspaceId), eq(customToolsTable.userId, userId))
1904+
)
1905+
)
1906+
.orderBy(desc(customToolsTable.createdAt))
18891907

18901908
for (const tool of toolRows) {
18911909
const safeName = sanitizeName(tool.title)
1892-
const serialized = serializeCustomTool({
1893-
id: tool.id,
1894-
title: tool.title,
1895-
schema: tool.schema,
1896-
code: tool.code,
1897-
})
1898-
this.files.set(`custom-tools/${safeName}.json`, serialized)
1899-
this.files.set(`agent/custom-tools/${safeName}.json`, serialized)
1910+
const toolId = tool.id
1911+
const load = async () => {
1912+
const full = await getCustomToolById({ toolId, userId, workspaceId })
1913+
if (!full) return null
1914+
return serializeCustomTool({
1915+
id: full.id,
1916+
title: full.title,
1917+
schema: full.schema,
1918+
code: full.code,
1919+
})
1920+
}
1921+
// Legacy alias + canonical agent/ path — each resolves independently on read.
1922+
this.registerLazy(`custom-tools/${safeName}.json`, load)
1923+
this.registerLazy(`agent/custom-tools/${safeName}.json`, load)
19001924
}
19011925

19021926
return toolRows.map((t) => ({ id: t.id, name: t.title }))
@@ -1995,26 +2019,39 @@ export class WorkspaceVFS {
19952019
}
19962020

19972021
/**
1998-
* Materialize workspace skills using the shared listSkills function.
2022+
* Advertise workspace skills in the VFS without eagerly loading their bodies.
2023+
* Paths are registered as lazy so glob/WORKSPACE.md see them, but full content
2024+
* is fetched only when read (or a grep whose scope touches the path) resolves them.
19992025
*/
20002026
private async materializeSkills(
20012027
workspaceId: string
20022028
): Promise<NonNullable<WorkspaceMdData['skills']>> {
20032029
try {
2004-
const skillRows = await listSkills({ workspaceId, includeBuiltins: false })
2030+
// Metadata only — skill bodies can be large; keep them out of the eager map.
2031+
const skillRows = await db
2032+
.select({
2033+
id: skillTable.id,
2034+
name: skillTable.name,
2035+
description: skillTable.description,
2036+
})
2037+
.from(skillTable)
2038+
.where(eq(skillTable.workspaceId, workspaceId))
2039+
.orderBy(desc(skillTable.createdAt))
20052040

20062041
for (const s of skillRows) {
20072042
const safeName = sanitizeName(s.name)
2008-
this.files.set(
2009-
`agent/skills/${safeName}.json`,
2010-
serializeSkill({
2011-
id: s.id,
2012-
name: s.name,
2013-
description: s.description,
2014-
content: s.content,
2015-
createdAt: s.createdAt,
2043+
const skillId = s.id
2044+
this.registerLazy(`agent/skills/${safeName}.json`, async () => {
2045+
const full = await getSkillById({ skillId, workspaceId })
2046+
if (!full) return null
2047+
return serializeSkill({
2048+
id: full.id,
2049+
name: full.name,
2050+
description: full.description,
2051+
content: full.content,
2052+
createdAt: full.createdAt,
20162053
})
2017-
)
2054+
})
20182055
}
20192056

20202057
return skillRows.map((s) => ({ id: s.id, name: s.name, description: s.description }))

0 commit comments

Comments
 (0)