Skip to content

Commit 862f3a7

Browse files
rafavallsclaudetlgimenes
authored
feat(agent-connections): improve agent detail UX, prompt template, and interactive prompt improvement (#2792)
* feat(agent-connections): show resource counts in connection footer Display resource counts (N tools, N prompts, N resources) or "All" in the connection footer instead of just a number. This shows which resources each connection has on the same line as the config icon. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * feat(agent-connections): adjust footer layout and instance selector size Move resource counts to the left side of the footer and make the instance selector smaller with a more compact design, matching the Registry selection style. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * feat(agent-connections): add instructions template and improve dialog UX Added "[+ Prompt template]" button to auto-populate instruction guidance when the field is empty. Extracted agent capabilities and connection dialogs into separate components for better maintainability. Fixed instance selector height to use proper sizing. Added decopilot endpoint for AI-powered prompt improvement. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * fix(agent-connections): remove unused exports to fix knip CI check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(agent-connections): replace improve-prompt endpoint with decopilot chat Send /writing-prompts message via createThreadAndSend instead of calling a dedicated backend endpoint, making prompt improvement interactive. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(automations): add improve prompt button to automation instructions Same pattern as agent detail — sends /writing-prompts via decopilot chat with the current instructions extracted from the tiptap editor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(automations): use ghost variant for improve prompt button Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(automations): revert improve button to outline variant Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(automations): update improve prompt message format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(automations): set plan mode on improve prompt and extract normalizeMessages Pass toolApprovalLevel: "plan" in createThreadAndSend for the improve prompt button on both automation and agent detail pages. Extract shared message normalization logic into normalize-messages.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com> Co-authored-by: gimenes <tlgimenes@gmail.com>
1 parent b486b0a commit 862f3a7

9 files changed

Lines changed: 1926 additions & 858 deletions

File tree

apps/mesh/src/tools/automations/create.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
requireAuth,
1212
requireOrganization,
1313
} from "../../core/mesh-context";
14+
import { normalizeMessages } from "./normalize-messages";
1415

1516
export const AUTOMATION_CREATE = defineTool({
1617
name: "AUTOMATION_CREATE",
@@ -94,16 +95,7 @@ export const AUTOMATION_CREATE = defineTool({
9495
throw new Error("Unable to determine user identity");
9596
}
9697

97-
// Normalize string messages to array format
98-
const normalizedMessages =
99-
typeof input.messages === "string"
100-
? [
101-
{
102-
role: "user" as const,
103-
parts: [{ type: "text", text: input.messages }],
104-
},
105-
]
106-
: input.messages;
98+
const normalizedMessages = normalizeMessages(input.messages);
10799

108100
// Auto-resolve models from credentials when not provided
109101
let models = input.models;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* Shared message normalization for automation create/update tools.
3+
*
4+
* Handles two concerns:
5+
* 1. LLMs sometimes pass the messages array as a JSON-stringified string —
6+
* detect and parse it to avoid double-serialization.
7+
* 2. Messages may lack `metadata.tiptapDoc` — generate it from the first
8+
* text part so the UI editor can render the content.
9+
*/
10+
11+
type MessagePart = Record<string, unknown>;
12+
13+
type Message = {
14+
id?: string;
15+
role: "user" | "assistant" | "system";
16+
parts: MessagePart[];
17+
metadata?: unknown;
18+
[key: string]: unknown;
19+
};
20+
21+
function buildTiptapDoc(text: string) {
22+
return {
23+
type: "doc",
24+
content: [
25+
{
26+
type: "paragraph",
27+
content: [
28+
{
29+
type: "text",
30+
text,
31+
},
32+
],
33+
},
34+
],
35+
};
36+
}
37+
38+
function ensureTiptapDoc(messages: Message[]): Message[] {
39+
return messages.map((msg) => {
40+
const meta = msg.metadata as Record<string, unknown> | undefined;
41+
if (meta?.tiptapDoc) {
42+
return msg;
43+
}
44+
45+
const firstTextPart = msg.parts.find(
46+
(p) => p.type === "text" && typeof p.text === "string",
47+
);
48+
if (!firstTextPart?.text || typeof firstTextPart.text !== "string") {
49+
return msg;
50+
}
51+
52+
return {
53+
...msg,
54+
metadata: {
55+
...meta,
56+
tiptapDoc: buildTiptapDoc(firstTextPart.text),
57+
},
58+
};
59+
});
60+
}
61+
62+
/**
63+
* Normalize messages input from LLM tool calls:
64+
* - If string is a JSON-serialized message array, parse it
65+
* - If string is plain text, wrap in a user message
66+
* - Ensure all messages have `metadata.tiptapDoc`
67+
*/
68+
export function normalizeMessages(messages: string | Message[]): Message[] {
69+
let normalized: Message[];
70+
71+
if (typeof messages === "string") {
72+
try {
73+
const parsed = JSON.parse(messages);
74+
if (
75+
Array.isArray(parsed) &&
76+
parsed.length > 0 &&
77+
parsed[0]?.role &&
78+
Array.isArray(parsed[0]?.parts)
79+
) {
80+
normalized = parsed;
81+
} else {
82+
normalized = [
83+
{
84+
role: "user" as const,
85+
parts: [{ type: "text", text: messages }],
86+
},
87+
];
88+
}
89+
} catch {
90+
normalized = [
91+
{
92+
role: "user" as const,
93+
parts: [{ type: "text", text: messages }],
94+
},
95+
];
96+
}
97+
} else {
98+
normalized = messages;
99+
}
100+
101+
return ensureTiptapDoc(normalized);
102+
}

apps/mesh/src/tools/automations/update.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { z } from "zod";
99
import { defineTool } from "../../core/define-tool";
1010
import { requireAuth, requireOrganization } from "../../core/mesh-context";
1111
import { configureTriggerOnMcp } from "./configure-trigger";
12+
import { normalizeMessages } from "./normalize-messages";
1213

1314
export const AUTOMATION_UPDATE = defineTool({
1415
name: "AUTOMATION_UPDATE",
@@ -108,15 +109,7 @@ export const AUTOMATION_UPDATE = defineTool({
108109
if (input.agent !== undefined)
109110
updateData.agent = JSON.stringify(input.agent);
110111
if (input.messages !== undefined) {
111-
const normalizedMessages =
112-
typeof input.messages === "string"
113-
? [
114-
{
115-
role: "user" as const,
116-
parts: [{ type: "text", text: input.messages }],
117-
},
118-
]
119-
: input.messages;
112+
const normalizedMessages = normalizeMessages(input.messages);
120113
updateData.messages = JSON.stringify(normalizedMessages);
121114
}
122115
if (input.models !== undefined)

0 commit comments

Comments
 (0)