create-supyagent-app 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/api-route/route.ts.tmpl +1 -1
- package/templates/base/src/components/chat-message.tsx +3 -2
- package/templates/base/src/components/chat.tsx +2 -2
- package/templates/base/src/components/supyagent/tool-message.tsx +48 -2
- package/templates/package-json/package.json.tmpl +1 -1
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ export async function POST(req: Request) {
|
|
|
17
17
|
|
|
18
18
|
const result = streamText({
|
|
19
19
|
model: {{aiModel}},
|
|
20
|
-
system: 'You are a helpful assistant. Use your tools when asked to interact with connected services. You can also run bash commands to help with tasks.',
|
|
20
|
+
system: 'You are a helpful assistant. Use your tools when asked to interact with connected services. You can also run bash commands to help with tasks. When a tool execution is not approved by the user, do not retry it — explain what you were trying to do and ask how to proceed.',
|
|
21
21
|
messages: await convertToModelMessages(messages),
|
|
22
22
|
tools,
|
|
23
23
|
stopWhen: stepCountIs(5),
|
|
@@ -10,6 +10,7 @@ import { cn } from "@/lib/utils";
|
|
|
10
10
|
|
|
11
11
|
interface ChatMessageProps {
|
|
12
12
|
message: UIMessage;
|
|
13
|
+
addToolApprovalResponse?: (opts: { id: string; approved: boolean }) => void;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function MarkdownContent({ text }: { text: string }) {
|
|
@@ -42,7 +43,7 @@ function CopyButton({ text }: { text: string }) {
|
|
|
42
43
|
);
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
export function ChatMessage({ message }: ChatMessageProps) {
|
|
46
|
+
export function ChatMessage({ message, addToolApprovalResponse }: ChatMessageProps) {
|
|
46
47
|
const isUser = message.role === "user";
|
|
47
48
|
const textContent = message.parts
|
|
48
49
|
.filter((part) => part.type === "text")
|
|
@@ -72,7 +73,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
if (isToolUIPart(part)) {
|
|
75
|
-
return <ToolMessage key={i} part={part} />;
|
|
76
|
+
return <ToolMessage key={i} part={part} addToolApprovalResponse={addToolApprovalResponse} />;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
return null;
|
|
@@ -23,7 +23,7 @@ export function Chat({ chatId, initialMessages }: ChatProps) {
|
|
|
23
23
|
[chatId]
|
|
24
24
|
);
|
|
25
25
|
|
|
26
|
-
const { messages, sendMessage, status, stop } = useChat({
|
|
26
|
+
const { messages, sendMessage, status, stop, addToolApprovalResponse } = useChat({
|
|
27
27
|
id: chatId,
|
|
28
28
|
transport,
|
|
29
29
|
messages: initialMessages,
|
|
@@ -102,7 +102,7 @@ export function Chat({ chatId, initialMessages }: ChatProps) {
|
|
|
102
102
|
</div>
|
|
103
103
|
)}
|
|
104
104
|
{messages.map((message) => (
|
|
105
|
-
<ChatMessage key={message.id} message={message} />
|
|
105
|
+
<ChatMessage key={message.id} message={message} addToolApprovalResponse={addToolApprovalResponse} />
|
|
106
106
|
))}
|
|
107
107
|
<div ref={bottomRef} />
|
|
108
108
|
</div>
|
|
@@ -26,9 +26,10 @@ import { getToolRenderer } from "./tool-renderers";
|
|
|
26
26
|
|
|
27
27
|
interface ToolMessageProps {
|
|
28
28
|
part: any; // ToolInvocationUIPart from AI SDK
|
|
29
|
+
addToolApprovalResponse?: (opts: { id: string; approved: boolean }) => void;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
export function ToolMessage({ part }: ToolMessageProps) {
|
|
32
|
+
export function ToolMessage({ part, addToolApprovalResponse }: ToolMessageProps) {
|
|
32
33
|
const toolName = extractToolName(part);
|
|
33
34
|
const state = extractState(part);
|
|
34
35
|
const args = extractArgs(part);
|
|
@@ -36,6 +37,8 @@ export function ToolMessage({ part }: ToolMessageProps) {
|
|
|
36
37
|
const toolState = resolveState(state);
|
|
37
38
|
const isDone = toolState === "output-available";
|
|
38
39
|
const isError = toolState === "output-error";
|
|
40
|
+
const isApprovalRequested = toolState === "approval-requested";
|
|
41
|
+
const isDenied = toolState === "output-denied";
|
|
39
42
|
|
|
40
43
|
// Extract and format result data
|
|
41
44
|
let renderedOutput: ReactNode = null;
|
|
@@ -57,7 +60,7 @@ export function ToolMessage({ part }: ToolMessageProps) {
|
|
|
57
60
|
const provider = getProviderFromToolName(toolName);
|
|
58
61
|
|
|
59
62
|
return (
|
|
60
|
-
<Tool defaultOpen={isDone}>
|
|
63
|
+
<Tool defaultOpen={isDone || isApprovalRequested}>
|
|
61
64
|
<ToolHeader
|
|
62
65
|
state={toolState}
|
|
63
66
|
title={`${getProviderLabel(provider)} · ${humanizeToolName(toolName)}${summary ? ` — ${summary}` : ""}`}
|
|
@@ -72,6 +75,43 @@ export function ToolMessage({ part }: ToolMessageProps) {
|
|
|
72
75
|
<ToolInput args={args} />
|
|
73
76
|
</div>
|
|
74
77
|
)}
|
|
78
|
+
|
|
79
|
+
{/* Approval UI */}
|
|
80
|
+
{isApprovalRequested && addToolApprovalResponse && part.approval && (
|
|
81
|
+
<div className="flex items-center gap-2 rounded-md border border-yellow-500/20 bg-yellow-500/5 p-3">
|
|
82
|
+
<p className="flex-1 text-sm text-muted-foreground">
|
|
83
|
+
This tool requires your approval to execute.
|
|
84
|
+
</p>
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
onClick={() => addToolApprovalResponse({
|
|
88
|
+
id: part.approval.id,
|
|
89
|
+
approved: false,
|
|
90
|
+
})}
|
|
91
|
+
className="rounded-md border border-border px-3 py-1.5 text-xs text-muted-foreground hover:bg-muted transition-colors"
|
|
92
|
+
>
|
|
93
|
+
Deny
|
|
94
|
+
</button>
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
onClick={() => addToolApprovalResponse({
|
|
98
|
+
id: part.approval.id,
|
|
99
|
+
approved: true,
|
|
100
|
+
})}
|
|
101
|
+
className="rounded-md bg-foreground px-3 py-1.5 text-xs text-background hover:bg-foreground/90 transition-colors"
|
|
102
|
+
>
|
|
103
|
+
Approve
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
|
|
108
|
+
{/* Denied message */}
|
|
109
|
+
{isDenied && (
|
|
110
|
+
<p className="text-sm text-muted-foreground italic">
|
|
111
|
+
Execution was denied by user.
|
|
112
|
+
</p>
|
|
113
|
+
)}
|
|
114
|
+
|
|
75
115
|
{renderedOutput && (
|
|
76
116
|
<ToolOutput output={renderedOutput} isError={isError} />
|
|
77
117
|
)}
|
|
@@ -88,6 +128,12 @@ function resolveState(state?: string): ToolState {
|
|
|
88
128
|
case "error":
|
|
89
129
|
case "output-error":
|
|
90
130
|
return "output-error";
|
|
131
|
+
case "approval-requested":
|
|
132
|
+
return "approval-requested";
|
|
133
|
+
case "approval-responded":
|
|
134
|
+
return "approval-responded";
|
|
135
|
+
case "output-denied":
|
|
136
|
+
return "output-denied";
|
|
91
137
|
case "call":
|
|
92
138
|
case "partial-call":
|
|
93
139
|
case "input-streaming":
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"{{aiProviderPackage}}": "{{aiProviderVersion}}",
|
|
18
18
|
"@prisma/client": "^6.2.0",
|
|
19
19
|
"@radix-ui/react-collapsible": "^1.1.0",
|
|
20
|
-
"@supyagent/sdk": "^0.1.
|
|
20
|
+
"@supyagent/sdk": "^0.1.16",
|
|
21
21
|
"class-variance-authority": "^0.7.0",
|
|
22
22
|
"ai": "^6.0.0",
|
|
23
23
|
"clsx": "^2.1.0",
|