Fix fast edit

This commit is contained in:
Siddharth Ganesan
2026-03-13 20:11:32 -07:00
parent d7a1353975
commit 7501ab15bb
7 changed files with 73 additions and 47 deletions

View File

@@ -7,6 +7,7 @@ import {
Calendar,
ClipboardList,
Database,
Eye,
File,
FolderCode,
Hammer,
@@ -59,6 +60,7 @@ const TOOL_ICONS: Record<MothershipToolName | SubagentName | 'mothership', IconC
debug: Bug,
edit: Pencil,
fast_edit: Pencil,
open_resource: Eye,
}
export function getAgentIcon(name: string): IconComponent {

View File

@@ -8,7 +8,6 @@ import {
reportManualRunToolStop,
} from '@/lib/copilot/client-sse/run-tool-execution'
import { MOTHERSHIP_CHAT_API_PATH } from '@/lib/copilot/constants'
import { VFS_DIR_TO_RESOURCE } from '@/lib/copilot/resource-types'
import { isWorkflowToolName } from '@/lib/copilot/workflow-tools'
import { getNextWorkflowColor } from '@/lib/workflows/colors'
import { invalidateResourceQueries } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
@@ -198,36 +197,6 @@ function ensureWorkflowInRegistry(resourceId: string, title: string, workspaceId
return true
}
function extractResourceFromReadResult(
path: string | undefined,
output: unknown
): MothershipResource | null {
if (!path) return null
const segments = path.split('/')
const resourceType = VFS_DIR_TO_RESOURCE[segments[0]]
if (!resourceType || !segments[1]) return null
const obj = output && typeof output === 'object' ? (output as Record<string, unknown>) : undefined
if (!obj) return null
let id = obj.id as string | undefined
let name = obj.name as string | undefined
if (!id && typeof obj.content === 'string') {
try {
const parsed = JSON.parse(obj.content)
id = parsed?.id as string | undefined
name = parsed?.name as string | undefined
} catch {
// content is not JSON
}
}
if (!id) return null
return { type: resourceType, id, title: name || segments[1] }
}
export interface UseChatOptions {
onResourceEvent?: () => void
}
@@ -554,18 +523,6 @@ export function useChat(
error: (parsed.error ?? getPayloadData(parsed)?.error) as string | undefined,
}
flush()
if (tc.name === 'read' && tc.status === 'success') {
const readArgs = toolArgsMap.get(id)
const resource = extractResourceFromReadResult(
readArgs?.path as string | undefined,
tc.result.output
)
if (resource) {
addResource(resource)
onResourceEventRef.current?.()
}
}
}
break

View File

@@ -73,6 +73,7 @@ export type MothershipToolName =
| 'debug'
| 'edit'
| 'fast_edit'
| 'open_resource'
/**
* Subagent identifiers dispatched via `subagent_start` SSE events.
@@ -95,6 +96,9 @@ export type SubagentName =
| 'debug'
| 'edit'
| 'fast_edit'
| 'run'
| 'agent'
| 'job'
export type ToolPhase =
| 'workspace'
@@ -181,7 +185,10 @@ export const SUBAGENT_LABELS: Record<SubagentName, string> = {
plan: 'Plan agent',
debug: 'Debug agent',
edit: 'Edit agent',
fast_edit: 'Edit agent',
fast_edit: 'Build agent',
run: 'Run agent',
agent: 'Agent manager',
job: 'Job agent',
} as const
export interface ToolUIMetadata {
@@ -227,6 +234,7 @@ export const TOOL_UI_METADATA: Partial<Record<MothershipToolName, ToolUIMetadata
debug: { title: 'Debugging', phaseLabel: 'Debug', phase: 'subagent' },
edit: { title: 'Editing workflow', phaseLabel: 'Edit', phase: 'subagent' },
fast_edit: { title: 'Editing workflow', phaseLabel: 'Edit', phase: 'subagent' },
open_resource: { title: 'Opening resource', phaseLabel: 'Resource', phase: 'resource' },
}
export interface SSEPayloadUI {

View File

@@ -992,6 +992,30 @@ const SIM_WORKFLOW_TOOL_HANDLERS: Record<
glob: (p, c) => executeVfsGlob(p, c),
read: (p, c) => executeVfsRead(p, c),
list: (p, c) => executeVfsList(p, c),
// Resource visibility
open_resource: async (p) => {
const resourceType = p.type as string | undefined
const resourceId = p.id as string | undefined
if (!resourceType || !resourceId) {
return { success: false, error: 'type and id are required' }
}
const validTypes = new Set(['workflow', 'table', 'knowledgebase', 'file'])
if (!validTypes.has(resourceType)) {
return { success: false, error: `Invalid resource type: ${resourceType}` }
}
return {
success: true,
output: { message: `Opened ${resourceType} ${resourceId} for the user` },
resources: [
{
type: resourceType as 'workflow' | 'table' | 'knowledgebase' | 'file',
id: resourceId,
title: resourceType,
},
],
}
},
}
/**

View File

@@ -25,6 +25,9 @@ export interface SSEEvent {
/** Authoritative tool call state set by the Go backend */
state?: string
data?: Record<string, unknown>
/** Parent agent that produced this event */
agent?: string
/** Subagent identifier (e.g. "build", "fast_edit") */
subagent?: string
toolCallId?: string
toolName?: string

View File

@@ -17,7 +17,6 @@ const RESOURCE_TOOL_NAMES = new Set([
'create_workflow',
'edit_workflow',
'function_execute',
'read',
'knowledge_base',
'knowledge',
])
@@ -92,8 +91,7 @@ export function extractResourcesFromToolResult(
return []
}
case 'function_execute':
case 'read': {
case 'function_execute': {
if (result.tableId) {
return [
{

View File

@@ -1185,6 +1185,18 @@ const META_mark_todo_in_progress: ToolMetadata = {
},
}
const META_open_resource: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Opening resource', icon: Eye },
[ClientToolCallState.pending]: { text: 'Opening resource', icon: Eye },
[ClientToolCallState.executing]: { text: 'Opening resource', icon: Eye },
[ClientToolCallState.success]: { text: 'Opened resource', icon: Eye },
[ClientToolCallState.error]: { text: 'Failed to open resource', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped opening resource', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted opening resource', icon: XCircle },
},
}
const META_navigate_ui: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: {
@@ -2187,6 +2199,26 @@ const META_edit: ToolMetadata = {
},
}
const META_fast_edit: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Building', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Building', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Building', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Built', icon: Wrench },
[ClientToolCallState.error]: { text: 'Failed to build', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped build', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted build', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Building',
completedLabel: 'Built',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const META_debug: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Debugging', icon: Loader2 },
@@ -2399,6 +2431,7 @@ const TOOL_METADATA_BY_ID: Record<string, ToolMetadata> = {
deploy_chat: META_deploy_chat,
deploy_mcp: META_deploy_mcp,
edit: META_edit,
fast_edit: META_fast_edit,
edit_workflow: META_edit_workflow,
get_block_outputs: META_get_block_outputs,
get_block_upstream_references: META_get_block_upstream_references,
@@ -2428,6 +2461,7 @@ const TOOL_METADATA_BY_ID: Record<string, ToolMetadata> = {
navigate_ui: META_navigate_ui,
oauth_get_auth_link: META_oauth_get_auth_link,
oauth_request_access: META_oauth_request_access,
open_resource: META_open_resource,
plan: META_plan,
read: META_read,
redeploy: META_redeploy,