diff --git a/apps/rowboat/app/lib/components/markdown-content.tsx b/apps/rowboat/app/lib/components/markdown-content.tsx
index 724f36b2..16c7a73e 100644
--- a/apps/rowboat/app/lib/components/markdown-content.tsx
+++ b/apps/rowboat/app/lib/components/markdown-content.tsx
@@ -105,8 +105,6 @@ export default function MarkdownContent({
return (
{displayLabel} (!)
diff --git a/apps/rowboat/app/lib/components/mentions_editor.tsx b/apps/rowboat/app/lib/components/mentions_editor.tsx
index 35b0aef3..451a6f2b 100644
--- a/apps/rowboat/app/lib/components/mentions_editor.tsx
+++ b/apps/rowboat/app/lib/components/mentions_editor.tsx
@@ -29,7 +29,7 @@ Quill.register('modules/mention', Mention);
function markdownToParts(markdown: string, atValues: Match[]): (string | Match)[] {
// Regex match for pattern [@type:name](#type:something) where type is tool/prompt/agent
- const mentionRegex = /\[@(tool|prompt|agent):([^\]]+)\]\(#mention\)/g;
+ const mentionRegex = /\[@(tool|prompt|agent|variable):([^\]]+)\]\(#mention\)/g;
const parts: (string | Match)[] = [];
let lastIndex = 0;
diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
index dcd4ce64..a1217f86 100644
--- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
+++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
@@ -1254,9 +1254,27 @@ export function WorkflowEditor({
dispatch({ type: "update_pipeline", name, pipeline });
}
- function handleDeleteAgent(name: string) {
+ async function handleDeleteAgent(name: string) {
if (window.confirm(`Are you sure you want to delete the agent "${name}"?`)) {
- dispatch({ type: "delete_agent", name });
+ // Optimistically update UI (guard will show modal in live mode)
+ dispatchGuarded({ type: "delete_agent", name });
+ // Persist immediately to avoid debounce races overwriting local state
+ if (!isLive) {
+ try {
+ const remainingAgents = state.present.workflow.agents.filter(a => a.name !== name);
+ const toSave = {
+ ...state.present.workflow,
+ agents: remainingAgents,
+ // If startAgent was deleted, set to first remaining or ''
+ startAgent: state.present.workflow.startAgent === name
+ ? (remainingAgents[0]?.name || '')
+ : state.present.workflow.startAgent,
+ } as z.infer;
+ await saveWorkflow(projectId, toSave);
+ } catch (e) {
+ console.error('Failed to persist agent deletion', e);
+ }
+ }
}
}
@@ -1264,9 +1282,22 @@ export function WorkflowEditor({
dispatch({ type: "update_tool", name, tool });
}
- function handleDeleteTool(name: string) {
+ async function handleDeleteTool(name: string) {
if (window.confirm(`Are you sure you want to delete the tool "${name}"?`)) {
- dispatch({ type: "delete_tool", name });
+ // Optimistically update UI (guard will show modal in live mode)
+ dispatchGuarded({ type: "delete_tool", name });
+ // Persist immediately to avoid debounce races that can re-add the tool
+ if (!isLive) {
+ try {
+ const toSave = {
+ ...state.present.workflow,
+ tools: state.present.workflow.tools.filter(t => t.name !== name),
+ } as z.infer;
+ await saveWorkflow(projectId, toSave);
+ } catch (e) {
+ console.error('Failed to persist tool deletion', e);
+ }
+ }
}
}
@@ -1283,9 +1314,22 @@ export function WorkflowEditor({
dispatch({ type: "update_prompt_no_select", name, prompt });
}
- function handleDeletePrompt(name: string) {
+ async function handleDeletePrompt(name: string) {
if (window.confirm(`Are you sure you want to delete the prompt "${name}"?`)) {
- dispatch({ type: "delete_prompt", name });
+ // Optimistically update UI (guard will show modal in live mode)
+ dispatchGuarded({ type: "delete_prompt", name });
+ // Persist immediately to avoid debounce races overwriting local state
+ if (!isLive) {
+ try {
+ const toSave = {
+ ...state.present.workflow,
+ prompts: state.present.workflow.prompts.filter(p => p.name !== name),
+ } as z.infer;
+ await saveWorkflow(projectId, toSave);
+ } catch (e) {
+ console.error('Failed to persist prompt deletion', e);
+ }
+ }
}
}