@alpaca-editor/core 1.0.4123 → 1.0.4128
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/dist/editor/ai/AgentCostDisplay.js +3 -1
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +349 -21
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.js +31 -10
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +6 -0
- package/dist/editor/ai/ToolCallDisplay.js +118 -14
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/types.d.ts +5 -0
- package/dist/editor/services/agentService.d.ts +16 -0
- package/dist/editor/services/agentService.js +39 -11
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/ui/Splitter.js +72 -2
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +25 -0
- package/package.json +1 -1
- package/src/editor/ai/AgentCostDisplay.tsx +3 -1
- package/src/editor/ai/AgentTerminal.tsx +494 -19
- package/src/editor/ai/ContextInfoBar.tsx +83 -58
- package/src/editor/ai/ToolCallDisplay.tsx +193 -11
- package/src/editor/ai/types.ts +6 -0
- package/src/editor/services/agentService.ts +69 -11
- package/src/editor/ui/Splitter.tsx +88 -2
- package/src/revision.ts +2 -2
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
|
2
2
|
import { useEditContext } from "../client/editContext";
|
|
3
3
|
import { Button } from "../../components/ui/button";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
FileText,
|
|
6
|
+
Puzzle,
|
|
7
|
+
Type,
|
|
8
|
+
Plus,
|
|
9
|
+
ChevronDown,
|
|
10
|
+
ChevronUp,
|
|
11
|
+
X,
|
|
12
|
+
} from "lucide-react";
|
|
5
13
|
import { getComponentById } from "../componentTreeHelper";
|
|
6
14
|
import {
|
|
7
15
|
AgentDetails,
|
|
@@ -39,8 +47,10 @@ export function ContextInfoBar({
|
|
|
39
47
|
) => {
|
|
40
48
|
if (!agent?.id) return;
|
|
41
49
|
const current = agentMetadata || {};
|
|
50
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
51
|
+
const { context: _, ...currentWithoutContext } = current;
|
|
42
52
|
const next: AgentMetadata = {
|
|
43
|
-
...
|
|
53
|
+
...currentWithoutContext,
|
|
44
54
|
additionalData: {
|
|
45
55
|
...(current.additionalData || {}),
|
|
46
56
|
context: {
|
|
@@ -115,8 +125,10 @@ export function ContextInfoBar({
|
|
|
115
125
|
return;
|
|
116
126
|
}
|
|
117
127
|
|
|
128
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
129
|
+
const { context: _, ...currentWithoutContext } = current;
|
|
118
130
|
const next: AgentMetadata = {
|
|
119
|
-
...
|
|
131
|
+
...currentWithoutContext,
|
|
120
132
|
additionalData: {
|
|
121
133
|
...(current.additionalData || {}),
|
|
122
134
|
context: {
|
|
@@ -158,8 +170,10 @@ export function ContextInfoBar({
|
|
|
158
170
|
|
|
159
171
|
if (newComponentIds.length === 0) return;
|
|
160
172
|
|
|
173
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
174
|
+
const { context: _, ...currentWithoutContext } = current;
|
|
161
175
|
const next: AgentMetadata = {
|
|
162
|
-
...
|
|
176
|
+
...currentWithoutContext,
|
|
163
177
|
additionalData: {
|
|
164
178
|
...(current.additionalData || {}),
|
|
165
179
|
context: {
|
|
@@ -198,8 +212,10 @@ export function ContextInfoBar({
|
|
|
198
212
|
const newComponentIds = ids.filter((id) => !!id && !existingIds.has(id));
|
|
199
213
|
if (newComponentIds.length === 0) return;
|
|
200
214
|
|
|
215
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
216
|
+
const { context: _, ...currentWithoutContext } = current;
|
|
201
217
|
const next: AgentMetadata = {
|
|
202
|
-
...
|
|
218
|
+
...currentWithoutContext,
|
|
203
219
|
additionalData: {
|
|
204
220
|
...(current.additionalData || {}),
|
|
205
221
|
context: {
|
|
@@ -239,15 +255,34 @@ export function ContextInfoBar({
|
|
|
239
255
|
|
|
240
256
|
const pagesToAdd = items
|
|
241
257
|
.filter((it) => !!it?.id)
|
|
242
|
-
.map((it) =>
|
|
258
|
+
.map((it) => {
|
|
259
|
+
const anyIt = it as any;
|
|
260
|
+
const page = {
|
|
261
|
+
id: it.id,
|
|
262
|
+
language: it.language,
|
|
263
|
+
version: it.version,
|
|
264
|
+
} as {
|
|
265
|
+
id: string;
|
|
266
|
+
language: string;
|
|
267
|
+
version: number;
|
|
268
|
+
name?: string;
|
|
269
|
+
path?: string;
|
|
270
|
+
};
|
|
271
|
+
const candidateName = anyIt?.displayName || anyIt?.name;
|
|
272
|
+
if (candidateName) page.name = candidateName as string;
|
|
273
|
+
if (anyIt?.path) page.path = anyIt.path as string;
|
|
274
|
+
return page;
|
|
275
|
+
})
|
|
243
276
|
.filter(
|
|
244
277
|
(p) => !existingPageIds.has(`${p.id}-${p.language}-${p.version}`),
|
|
245
278
|
);
|
|
246
279
|
|
|
247
280
|
if (pagesToAdd.length === 0) return;
|
|
248
281
|
|
|
282
|
+
// Exclude top-level context to avoid duplicate keys when spreading
|
|
283
|
+
const { context: _, ...currentWithoutContext } = current;
|
|
249
284
|
const next: AgentMetadata = {
|
|
250
|
-
...
|
|
285
|
+
...currentWithoutContext,
|
|
251
286
|
additionalData: {
|
|
252
287
|
...(current.additionalData || {}),
|
|
253
288
|
context: {
|
|
@@ -502,70 +537,60 @@ export function ContextInfoBar({
|
|
|
502
537
|
|
|
503
538
|
return (
|
|
504
539
|
<div
|
|
505
|
-
className=
|
|
506
|
-
"flex flex-col gap-2 border-t border-gray-100 px-4 py-2 " +
|
|
507
|
-
(isDragOver ? "bg-gray-50" : "")
|
|
508
|
-
}
|
|
540
|
+
className="border-t border-gray-200 bg-gray-50"
|
|
509
541
|
onDragOver={handleDragOver}
|
|
510
542
|
onDragEnter={handleDragOver}
|
|
511
543
|
onDragLeave={handleDragLeave}
|
|
512
544
|
onDrop={handleDrop}
|
|
513
545
|
>
|
|
514
|
-
<
|
|
515
|
-
className="flex cursor-pointer items-center justify-between"
|
|
546
|
+
<button
|
|
516
547
|
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
517
|
-
|
|
548
|
+
className="flex w-full cursor-pointer items-center justify-between px-4 py-2 text-left transition-colors hover:bg-gray-100"
|
|
518
549
|
aria-label={isCollapsed ? "Expand context" : "Collapse context"}
|
|
519
550
|
data-testid="context-toggle"
|
|
520
551
|
>
|
|
521
|
-
<div className="
|
|
522
|
-
<
|
|
552
|
+
<div className="flex items-center gap-2">
|
|
553
|
+
<FileText className="h-4 w-4 text-gray-500" strokeWidth={1} />
|
|
554
|
+
<span className="text-xs font-medium text-gray-700">Context</span>
|
|
523
555
|
{isCollapsed && summaryText && (
|
|
524
|
-
<
|
|
525
|
-
className="text-2xs truncate text-gray-400"
|
|
526
|
-
title={summaryText}
|
|
527
|
-
>
|
|
528
|
-
{summaryText}
|
|
529
|
-
</div>
|
|
556
|
+
<span className="text-xs text-gray-500">· {summaryText}</span>
|
|
530
557
|
)}
|
|
531
558
|
</div>
|
|
532
|
-
|
|
533
|
-
className={
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
/>
|
|
539
|
-
</div>
|
|
559
|
+
{isCollapsed ? (
|
|
560
|
+
<ChevronDown className="h-4 w-4 text-gray-500" strokeWidth={1} />
|
|
561
|
+
) : (
|
|
562
|
+
<ChevronUp className="h-4 w-4 text-gray-500" strokeWidth={1} />
|
|
563
|
+
)}
|
|
564
|
+
</button>
|
|
540
565
|
{!isCollapsed && (
|
|
541
|
-
<div className="
|
|
542
|
-
<div className="flex items-center
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
<div className="text-
|
|
566
|
+
<div className="px-4 pb-3">
|
|
567
|
+
<div className="flex flex-wrap items-center gap-2 pb-2">{chips}</div>
|
|
568
|
+
{(canAddPage || canAddSelection) && (
|
|
569
|
+
<div className="flex flex-col items-start gap-1 pb-2">
|
|
570
|
+
{canAddPage && (
|
|
571
|
+
<Button
|
|
572
|
+
size="xs"
|
|
573
|
+
variant="outline"
|
|
574
|
+
onClick={addPagesToContext}
|
|
575
|
+
data-testid="add-current-item"
|
|
576
|
+
>
|
|
577
|
+
<Plus className="mr-1 h-3 w-3" strokeWidth={1} /> Add current
|
|
578
|
+
item
|
|
579
|
+
</Button>
|
|
580
|
+
)}
|
|
581
|
+
{canAddSelection && (
|
|
582
|
+
<Button
|
|
583
|
+
size="xs"
|
|
584
|
+
variant="outline"
|
|
585
|
+
onClick={addSelectedComponentsToContext}
|
|
586
|
+
>
|
|
587
|
+
<Plus className="mr-1 h-3 w-3" strokeWidth={1} /> Add selected
|
|
588
|
+
components
|
|
589
|
+
</Button>
|
|
590
|
+
)}
|
|
591
|
+
</div>
|
|
592
|
+
)}
|
|
593
|
+
<div className="text-xs text-gray-500">
|
|
569
594
|
Tip: Drag items or components here to add them
|
|
570
595
|
</div>
|
|
571
596
|
</div>
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
import { SimpleTabs, Tab } from "../ui/SimpleTabs";
|
|
11
11
|
import { Spinner } from "../ui/Spinner";
|
|
12
12
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
13
|
+
import { Button } from "../../components/ui/button";
|
|
14
|
+
import { approveToolCall, rejectToolCall } from "../services/agentService";
|
|
13
15
|
import {
|
|
14
16
|
X,
|
|
15
17
|
FileText,
|
|
@@ -73,6 +75,11 @@ const getToolIcon = (toolName: string) => {
|
|
|
73
75
|
};
|
|
74
76
|
|
|
75
77
|
// Types for tool calls - supporting both AI and Agent formats
|
|
78
|
+
export type ApprovalInfo = {
|
|
79
|
+
summary: string;
|
|
80
|
+
riskLevel?: 'low' | 'medium' | 'high';
|
|
81
|
+
};
|
|
82
|
+
|
|
76
83
|
export interface BaseToolCall {
|
|
77
84
|
id: string;
|
|
78
85
|
displayName?: string;
|
|
@@ -82,6 +89,7 @@ export interface BaseToolCall {
|
|
|
82
89
|
result?: string;
|
|
83
90
|
error?: string;
|
|
84
91
|
};
|
|
92
|
+
requiresApproval?: ApprovalInfo;
|
|
85
93
|
}
|
|
86
94
|
|
|
87
95
|
export interface AgentToolCall {
|
|
@@ -95,6 +103,7 @@ export interface AgentToolCall {
|
|
|
95
103
|
isCompleted: boolean;
|
|
96
104
|
responseTimeMs?: number;
|
|
97
105
|
createdDate: string;
|
|
106
|
+
requiresApproval?: ApprovalInfo;
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
interface ToolCallDisplayProps {
|
|
@@ -107,6 +116,7 @@ interface ToolCallDisplayProps {
|
|
|
107
116
|
messageId: string;
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
|
|
110
120
|
// Helper function to normalize tool calls to a common format
|
|
111
121
|
const normalizeToolCall = (
|
|
112
122
|
toolCall: BaseToolCall | AgentToolCall,
|
|
@@ -122,6 +132,7 @@ const normalizeToolCall = (
|
|
|
122
132
|
result: toolCall.functionResult,
|
|
123
133
|
error: toolCall.functionError,
|
|
124
134
|
},
|
|
135
|
+
requiresApproval: toolCall.requiresApproval,
|
|
125
136
|
};
|
|
126
137
|
}
|
|
127
138
|
// Already in base format
|
|
@@ -297,13 +308,67 @@ export function ToolCallDisplay({
|
|
|
297
308
|
const toolResult = toolCall.function?.result;
|
|
298
309
|
const popoverKey = `${messageId}-${toolIndex}`;
|
|
299
310
|
const isAgentToolCall = "isCompleted" in originalToolCall;
|
|
311
|
+
|
|
312
|
+
// Get approval information to check if tool call is pending approval
|
|
313
|
+
const approvalInfo = originalToolCall.requiresApproval || toolCall.requiresApproval;
|
|
314
|
+
|
|
300
315
|
const isCompleted = isAgentToolCall
|
|
301
316
|
? originalToolCall.isCompleted
|
|
302
|
-
: !!(toolResult || toolCall.function?.error);
|
|
317
|
+
: !!(toolResult || toolCall.function?.error) && !approvalInfo;
|
|
318
|
+
|
|
319
|
+
// Use the approval info from the backend
|
|
320
|
+
const finalApprovalInfo = approvalInfo;
|
|
321
|
+
|
|
322
|
+
// Check if this tool call has been approved/rejected (look for status indicators in function name)
|
|
323
|
+
// The AgentTerminal adds " (approved)" or " (rejected)" or " (pending approval)" to the functionName field
|
|
324
|
+
const originalFunctionName = ("functionName" in originalToolCall ? originalToolCall.functionName : toolCall?.function?.name) || '';
|
|
325
|
+
const isApproved = originalFunctionName.includes('(approved)');
|
|
326
|
+
const isRejected = originalFunctionName.includes('(rejected)');
|
|
327
|
+
const isPending = originalFunctionName.includes('(pending approval)');
|
|
328
|
+
// Treat only approved/rejected as final; pending should still show buttons
|
|
329
|
+
const hasApprovalStatus = isApproved || isRejected;
|
|
330
|
+
|
|
331
|
+
console.log("🔍 Approval status check:", {
|
|
332
|
+
toolCallId: toolCall.id,
|
|
333
|
+
originalFunctionName,
|
|
334
|
+
displayName: toolCall?.displayName,
|
|
335
|
+
functionName: toolCall?.function?.name,
|
|
336
|
+
isApproved,
|
|
337
|
+
isRejected,
|
|
338
|
+
hasApprovalStatus
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Debug: Check what we're receiving
|
|
342
|
+
console.log("🔍 Approval debug:", {
|
|
343
|
+
toolIndex,
|
|
344
|
+
functionName: toolCall?.function?.name,
|
|
345
|
+
originalRequiresApproval: originalToolCall.requiresApproval,
|
|
346
|
+
normalizedRequiresApproval: toolCall.requiresApproval,
|
|
347
|
+
approvalInfo,
|
|
348
|
+
approvalInfoType: typeof approvalInfo,
|
|
349
|
+
isCompleted,
|
|
350
|
+
hasApprovalStatus,
|
|
351
|
+
finalApprovalInfo,
|
|
352
|
+
shouldShowApprovalUI: !isCompleted && finalApprovalInfo && !hasApprovalStatus,
|
|
353
|
+
toolResult: toolCall.function?.result,
|
|
354
|
+
toolError: toolCall.function?.error,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Debug: Check if agent ID is available
|
|
358
|
+
if (finalApprovalInfo) {
|
|
359
|
+
console.log("🔍 Agent ID check:", {
|
|
360
|
+
currentAgentId: (window as any)?.currentAgentId,
|
|
361
|
+
messageId,
|
|
362
|
+
toolCallId: toolCall.id,
|
|
363
|
+
hasAgentId: !!(window as any)?.currentAgentId,
|
|
364
|
+
windowKeys: Object.keys(window).filter(k => k.includes('agent') || k.includes('Agent'))
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
303
368
|
|
|
304
369
|
return (
|
|
370
|
+
<div key={toolIndex}>
|
|
305
371
|
<Popover
|
|
306
|
-
key={toolIndex}
|
|
307
372
|
enableIframeClickDetection={false}
|
|
308
373
|
open={openPopovers[popoverKey] || false}
|
|
309
374
|
onOpenChange={(open) => {
|
|
@@ -314,13 +379,17 @@ export function ToolCallDisplay({
|
|
|
314
379
|
}}
|
|
315
380
|
>
|
|
316
381
|
<PopoverTrigger asChild>
|
|
317
|
-
<div className="
|
|
382
|
+
<div className="flex items-center gap-2 text-xs text-gray-500">
|
|
318
383
|
{isCompleted ? (
|
|
319
384
|
<div
|
|
320
385
|
className={`flex items-center ${toolCall.function?.error ? "text-red-500" : ""}`}
|
|
321
386
|
>
|
|
322
387
|
{getToolIcon(toolCall?.function?.name || "")}
|
|
323
388
|
</div>
|
|
389
|
+
) : finalApprovalInfo && !hasApprovalStatus ? (
|
|
390
|
+
<div className="flex items-center text-amber-600">
|
|
391
|
+
{getToolIcon(toolCall?.function?.name || "")}
|
|
392
|
+
</div>
|
|
324
393
|
) : finished ? (
|
|
325
394
|
<div className="flex items-center opacity-50">
|
|
326
395
|
{getToolIcon(toolCall?.function?.name || "")}
|
|
@@ -328,14 +397,33 @@ export function ToolCallDisplay({
|
|
|
328
397
|
) : (
|
|
329
398
|
<Spinner size="xs" />
|
|
330
399
|
)}
|
|
331
|
-
<
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
"tool call"
|
|
337
|
-
|
|
338
|
-
|
|
400
|
+
<div className="inline-flex items-center gap-2">
|
|
401
|
+
<span
|
|
402
|
+
className={`${toolCall.function?.error ? "text-red-500" : "hover:text-gray-700"} cursor-pointer`}
|
|
403
|
+
>
|
|
404
|
+
{/* Show clean function name without approval suffix */}
|
|
405
|
+
{(originalFunctionName || toolCall?.function?.name || "tool call")
|
|
406
|
+
.replace(' (approved)', '')
|
|
407
|
+
.replace(' (rejected)', '')
|
|
408
|
+
.replace(' (pending approval)', '')}
|
|
409
|
+
{toolCall.function?.error && " (error)"}
|
|
410
|
+
{finalApprovalInfo && isApproved && (
|
|
411
|
+
<span className="ml-2 text-xs text-green-600 font-medium">
|
|
412
|
+
✓ Approved
|
|
413
|
+
</span>
|
|
414
|
+
)}
|
|
415
|
+
{finalApprovalInfo && isRejected && (
|
|
416
|
+
<span className="ml-2 text-xs text-red-600 font-medium">
|
|
417
|
+
✗ Rejected
|
|
418
|
+
</span>
|
|
419
|
+
)}
|
|
420
|
+
{finalApprovalInfo && isPending && (
|
|
421
|
+
<span className="ml-2 text-xs text-amber-600 font-medium">
|
|
422
|
+
⏸ Pending approval
|
|
423
|
+
</span>
|
|
424
|
+
)}
|
|
425
|
+
</span>
|
|
426
|
+
</div>
|
|
339
427
|
</div>
|
|
340
428
|
</PopoverTrigger>
|
|
341
429
|
<PopoverContent
|
|
@@ -358,6 +446,100 @@ export function ToolCallDisplay({
|
|
|
358
446
|
/>
|
|
359
447
|
</PopoverContent>
|
|
360
448
|
</Popover>
|
|
449
|
+
{!isCompleted && finalApprovalInfo && !hasApprovalStatus && (
|
|
450
|
+
<div className="border rounded mt-2">
|
|
451
|
+
<div className="p-3 border-b bg-amber-50">
|
|
452
|
+
<div className="flex items-start gap-2">
|
|
453
|
+
<div className={`w-2 h-2 rounded-full mt-1.5 ${
|
|
454
|
+
finalApprovalInfo.riskLevel === 'high' ? 'bg-red-500' :
|
|
455
|
+
finalApprovalInfo.riskLevel === 'medium' ? 'bg-amber-500' :
|
|
456
|
+
'bg-green-500'
|
|
457
|
+
}`} />
|
|
458
|
+
<div>
|
|
459
|
+
<div className="text-xs font-medium text-gray-900 mb-1">
|
|
460
|
+
Action requires approval
|
|
461
|
+
</div>
|
|
462
|
+
<div className="text-xs text-gray-700">
|
|
463
|
+
{finalApprovalInfo.summary}
|
|
464
|
+
</div>
|
|
465
|
+
{finalApprovalInfo.riskLevel && (
|
|
466
|
+
<div className={`text-xs mt-1 font-medium ${
|
|
467
|
+
finalApprovalInfo.riskLevel === 'high' ? 'text-red-600' :
|
|
468
|
+
finalApprovalInfo.riskLevel === 'medium' ? 'text-amber-600' :
|
|
469
|
+
'text-green-600'
|
|
470
|
+
}`}>
|
|
471
|
+
Risk level: {finalApprovalInfo.riskLevel}
|
|
472
|
+
</div>
|
|
473
|
+
)}
|
|
474
|
+
</div>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
<div className="flex items-center justify-end gap-2 p-2">
|
|
478
|
+
<Button
|
|
479
|
+
size="sm"
|
|
480
|
+
variant="secondary"
|
|
481
|
+
onClick={async () => {
|
|
482
|
+
console.log("🚫 Rejecting tool call:", {
|
|
483
|
+
agentId: (window as any)?.currentAgentId,
|
|
484
|
+
messageId: messageId,
|
|
485
|
+
toolCallId: toolCall.id,
|
|
486
|
+
});
|
|
487
|
+
try {
|
|
488
|
+
const result = await rejectToolCall({
|
|
489
|
+
agentId: (window as any)?.currentAgentId,
|
|
490
|
+
messageId: messageId,
|
|
491
|
+
toolCallId: toolCall.id,
|
|
492
|
+
});
|
|
493
|
+
console.log("✅ Reject successful:", result);
|
|
494
|
+
const ev = new CustomEvent('agent:toolApprovalResolved', {
|
|
495
|
+
detail: {
|
|
496
|
+
messageId: messageId,
|
|
497
|
+
toolCallId: toolCall.id,
|
|
498
|
+
approved: false,
|
|
499
|
+
},
|
|
500
|
+
} as any);
|
|
501
|
+
window.dispatchEvent(ev);
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.error("❌ Reject failed:", error);
|
|
504
|
+
}
|
|
505
|
+
}}
|
|
506
|
+
>
|
|
507
|
+
Reject
|
|
508
|
+
</Button>
|
|
509
|
+
<Button
|
|
510
|
+
size="sm"
|
|
511
|
+
onClick={async () => {
|
|
512
|
+
console.log("✅ Approving tool call:", {
|
|
513
|
+
agentId: (window as any)?.currentAgentId,
|
|
514
|
+
messageId: messageId,
|
|
515
|
+
toolCallId: toolCall.id,
|
|
516
|
+
});
|
|
517
|
+
try {
|
|
518
|
+
const result = await approveToolCall({
|
|
519
|
+
agentId: (window as any)?.currentAgentId,
|
|
520
|
+
messageId: messageId,
|
|
521
|
+
toolCallId: toolCall.id,
|
|
522
|
+
});
|
|
523
|
+
console.log("✅ Approve successful:", result);
|
|
524
|
+
const ev = new CustomEvent('agent:toolApprovalResolved', {
|
|
525
|
+
detail: {
|
|
526
|
+
messageId: messageId,
|
|
527
|
+
toolCallId: toolCall.id,
|
|
528
|
+
approved: true,
|
|
529
|
+
},
|
|
530
|
+
} as any);
|
|
531
|
+
window.dispatchEvent(ev);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error("❌ Approve failed:", error);
|
|
534
|
+
}
|
|
535
|
+
}}
|
|
536
|
+
>
|
|
537
|
+
Approve
|
|
538
|
+
</Button>
|
|
539
|
+
</div>
|
|
540
|
+
</div>
|
|
541
|
+
)}
|
|
542
|
+
</div>
|
|
361
543
|
);
|
|
362
544
|
})}
|
|
363
545
|
</div>
|
package/src/editor/ai/types.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export type ApprovalInfo = {
|
|
2
|
+
summary: string;
|
|
3
|
+
riskLevel?: 'low' | 'medium' | 'high';
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
export type ToolCall = {
|
|
2
7
|
id: string;
|
|
3
8
|
displayName?: string;
|
|
@@ -7,6 +12,7 @@ export type ToolCall = {
|
|
|
7
12
|
result?: string;
|
|
8
13
|
error?: string;
|
|
9
14
|
};
|
|
15
|
+
requiresApproval?: ApprovalInfo;
|
|
10
16
|
};
|
|
11
17
|
|
|
12
18
|
export type Message = {
|
|
@@ -86,6 +86,46 @@ export type AgentDetails = Agent & {
|
|
|
86
86
|
messages?: AgentChatMessage[];
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
+
export async function approveToolCall(params: {
|
|
90
|
+
agentId: string;
|
|
91
|
+
messageId: string;
|
|
92
|
+
toolCallId: string;
|
|
93
|
+
note?: string;
|
|
94
|
+
}): Promise<{ success: boolean }> {
|
|
95
|
+
const result = await post<{ success: boolean }>(
|
|
96
|
+
AGENT_BASE_URL + "/approveToolCall",
|
|
97
|
+
params,
|
|
98
|
+
);
|
|
99
|
+
if (result.type !== "success") {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Failed to approve tool call: ${result.summary || "Unknown error"} ${
|
|
102
|
+
result.details || ""
|
|
103
|
+
}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return result.data ?? { success: true };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function rejectToolCall(params: {
|
|
110
|
+
agentId: string;
|
|
111
|
+
messageId: string;
|
|
112
|
+
toolCallId: string;
|
|
113
|
+
note?: string;
|
|
114
|
+
}): Promise<{ success: boolean }> {
|
|
115
|
+
const result = await post<{ success: boolean }>(
|
|
116
|
+
AGENT_BASE_URL + "/rejectToolCall",
|
|
117
|
+
params,
|
|
118
|
+
);
|
|
119
|
+
if (result.type !== "success") {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Failed to reject tool call: ${result.summary || "Unknown error"} ${
|
|
122
|
+
result.details || ""
|
|
123
|
+
}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return result.data ?? { success: true };
|
|
127
|
+
}
|
|
128
|
+
|
|
89
129
|
// Metadata shape stored on the agent. Server accepts a flexible JSON object; keep it permissive.
|
|
90
130
|
export type AgentMetadata = {
|
|
91
131
|
selection?: string[];
|
|
@@ -529,9 +569,12 @@ export async function updateAgentMetadata(
|
|
|
529
569
|
agentId: string,
|
|
530
570
|
metadata: AgentMetadata,
|
|
531
571
|
): Promise<{ success: boolean }> {
|
|
572
|
+
// Remove top-level context to avoid duplicate keys (context should only be in additionalData)
|
|
573
|
+
const { context: _, ...cleanMetadata } = metadata;
|
|
574
|
+
|
|
532
575
|
const result = await post<{ success: boolean }>(
|
|
533
576
|
AGENT_BASE_URL + "/updateMetadata",
|
|
534
|
-
{ agentId, metadata },
|
|
577
|
+
{ agentId, metadata: cleanMetadata },
|
|
535
578
|
);
|
|
536
579
|
|
|
537
580
|
if (result.type !== "success") {
|
|
@@ -611,16 +654,31 @@ export function convertAgentMessagesToTerminalFormat(
|
|
|
611
654
|
|
|
612
655
|
// Add tool calls if they exist
|
|
613
656
|
if (agentMessage.toolCalls && agentMessage.toolCalls.length > 0) {
|
|
614
|
-
terminalMessage.tool_calls = agentMessage.toolCalls.map((tc) =>
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
657
|
+
terminalMessage.tool_calls = agentMessage.toolCalls.map((tc) => {
|
|
658
|
+
const requiresApproval =
|
|
659
|
+
(tc as any)?.requiresApproval === true ||
|
|
660
|
+
(tc as any)?.requiresApproval === 1 ||
|
|
661
|
+
(tc as any)?.RequiresApproval === true;
|
|
662
|
+
const toolCall: any = {
|
|
663
|
+
id: tc.toolCallId,
|
|
664
|
+
displayName: tc.functionName,
|
|
665
|
+
function: {
|
|
666
|
+
name: tc.functionName,
|
|
667
|
+
arguments: tc.functionArguments,
|
|
668
|
+
result: tc.functionResult,
|
|
669
|
+
error: tc.functionError,
|
|
670
|
+
},
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
if (requiresApproval) {
|
|
674
|
+
toolCall.requiresApproval = {
|
|
675
|
+
summary: `Function call: ${tc.functionName}`,
|
|
676
|
+
riskLevel: "medium" as const,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return toolCall;
|
|
681
|
+
});
|
|
624
682
|
}
|
|
625
683
|
|
|
626
684
|
terminalMessages.push(terminalMessage);
|