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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-supyagent-app",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Create a supyagent-powered chatbot app",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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.15",
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",