@cntyclub/agent-react 0.4.0 → 0.5.0

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/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { ChatMessage, ChatMessageBubble, Markdown, ChatToolChip, DataTablePaged, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Button, Spinner, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Tooltip, TooltipTrigger, TooltipContent, Chat, ChatMessages, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, ChatTypingIndicator, cn, ChatInput, useMediaQuery, TooltipProvider, Fab } from '@cntyclub/ui-react';
2
- import { WrenchIcon, CheckIcon, ShieldAlertIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, Maximize2Icon, XIcon, Trash2Icon, SparklesIcon } from 'lucide-react';
3
- import * as React2 from 'react';
1
+ import { ChatMessage, ChatMessageBubble, Markdown, Collapsible, CollapsibleTrigger, ChatToolChip, cn, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Button, Group, Spinner, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Tooltip, TooltipTrigger, TooltipContent, Chat, ChatMessages, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, ChatTypingIndicator, ChatInput, useMediaQuery, TooltipProvider, Fab, DataTablePaged } from '@cntyclub/ui-react';
2
+ import { ChevronDownIcon, CheckIcon, ShieldAlertIcon, CheckCheckIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, Maximize2Icon, XIcon, Trash2Icon, SparklesIcon, CodeIcon } from 'lucide-react';
3
+ import * as React3 from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
6
6
  // src/api-client.ts
@@ -77,6 +77,84 @@ var AgentApiClient = class {
77
77
  });
78
78
  }
79
79
  };
80
+
81
+ // src/message-group.ts
82
+ function groupMessages(messages) {
83
+ const items = [];
84
+ let group = null;
85
+ let groupKey = "";
86
+ const stepIndexByCallId = /* @__PURE__ */ new Map();
87
+ const flush = () => {
88
+ if (group && group.length > 0) {
89
+ items.push({ key: `tools-${groupKey}`, kind: "tool-activity", steps: group });
90
+ }
91
+ group = null;
92
+ groupKey = "";
93
+ stepIndexByCallId.clear();
94
+ };
95
+ for (const message of messages) {
96
+ if (message.role === "user") {
97
+ flush();
98
+ items.push({ key: message.id, kind: "user", message });
99
+ continue;
100
+ }
101
+ if (message.role === "assistant") {
102
+ const hasText = Boolean(message.content && message.content.trim());
103
+ if (hasText) {
104
+ flush();
105
+ items.push({ key: message.id, kind: "assistant-text", message });
106
+ }
107
+ for (const toolCall of message.tool_calls ?? []) {
108
+ const callId2 = toolCall.id ?? `${message.id}-${toolCall.name}`;
109
+ if (!group) {
110
+ group = [];
111
+ groupKey = message.id;
112
+ }
113
+ stepIndexByCallId.set(callId2, group.length);
114
+ group.push({
115
+ args: toolCall.arguments ?? {},
116
+ callId: callId2,
117
+ name: toolCall.name ?? "tool",
118
+ status: "running"
119
+ });
120
+ }
121
+ continue;
122
+ }
123
+ const callId = message.tool_call_id ?? "";
124
+ if (!group) {
125
+ group = [];
126
+ groupKey = message.id;
127
+ }
128
+ const existingIndex = callId ? stepIndexByCallId.get(callId) : void 0;
129
+ if (existingIndex !== void 0) {
130
+ const step = group[existingIndex];
131
+ step.status = "done";
132
+ step.result = message.result || step.result;
133
+ step.blocks = message.blocks ?? step.blocks;
134
+ } else {
135
+ group.push({
136
+ args: {},
137
+ blocks: message.blocks,
138
+ callId: callId || message.id,
139
+ name: message.tool_name ?? "tool",
140
+ result: message.result,
141
+ status: "done"
142
+ });
143
+ }
144
+ }
145
+ flush();
146
+ return items;
147
+ }
148
+ function MessageItem({ message }) {
149
+ if (message.role === "user") {
150
+ return /* @__PURE__ */ jsx(ChatMessage, { from: "user", children: /* @__PURE__ */ jsx(ChatMessageBubble, { from: "user", children: message.content }) });
151
+ }
152
+ if (message.role === "assistant") {
153
+ if (!message.content || !message.content.trim()) return null;
154
+ return /* @__PURE__ */ jsx(ChatMessage, { from: "assistant", children: /* @__PURE__ */ jsx(ChatMessageBubble, { from: "assistant", children: /* @__PURE__ */ jsx(Markdown, { children: message.content }) }) });
155
+ }
156
+ return null;
157
+ }
80
158
  var NOISE_KEYS = /* @__PURE__ */ new Set([
81
159
  "id",
82
160
  "pk",
@@ -215,27 +293,72 @@ function toDisplayTable(block) {
215
293
  });
216
294
  return { columns, rows: displayRows };
217
295
  }
296
+
297
+ // src/tool-presentation.ts
218
298
  function humanizeToolName(toolName) {
219
299
  return toolName.replace(/^[a-z]+_/, "").replaceAll("_", " ");
220
300
  }
221
- function MessageItem({ message }) {
222
- if (message.role === "user") {
223
- return /* @__PURE__ */ jsx(ChatMessage, { from: "user", children: /* @__PURE__ */ jsx(ChatMessageBubble, { from: "user", children: message.content }) });
301
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
302
+ function isTechnicalKey(key) {
303
+ const lower = key.toLowerCase();
304
+ return lower === "id" || lower === "client_id" || lower === "slug" || lower.endsWith("_id") || lower.endsWith("_ids") || lower.endsWith("_key") || lower.endsWith("_slug") || lower.endsWith("_uuid") || lower === "uuid" || lower.endsWith("_s3_key") || lower === "embedding";
305
+ }
306
+ function isTechnicalValue(value) {
307
+ return typeof value === "string" && UUID_RE2.test(value.trim());
308
+ }
309
+ function humanizeKey(key) {
310
+ const spaced = key.replaceAll("_", " ").trim();
311
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
312
+ }
313
+ function formatValue(value) {
314
+ if (value === null || value === void 0) return "\u2014";
315
+ if (typeof value === "boolean") return value ? "Yes" : "No";
316
+ if (typeof value === "string" || typeof value === "number") return String(value);
317
+ if (Array.isArray(value)) {
318
+ const items = value.filter((item) => !isTechnicalValue(item)).map((item) => formatValue(item));
319
+ return items.length ? items.join(", ") : `${value.length} item${value.length === 1 ? "" : "s"}`;
224
320
  }
225
- if (message.role === "assistant") {
226
- const requestedTools = message.tool_calls ?? [];
227
- if (!message.content && requestedTools.length === 0) return null;
228
- return /* @__PURE__ */ jsxs(ChatMessage, { from: "assistant", children: [
229
- message.content ? /* @__PURE__ */ jsx(ChatMessageBubble, { from: "assistant", children: /* @__PURE__ */ jsx(Markdown, { children: message.content }) }) : null,
230
- requestedTools.map((toolCall) => /* @__PURE__ */ jsxs(ChatToolChip, { icon: /* @__PURE__ */ jsx(WrenchIcon, {}), children: [
231
- "Using ",
232
- humanizeToolName(toolCall.name ?? "tool"),
233
- "\u2026"
234
- ] }, toolCall.id ?? toolCall.name))
235
- ] });
321
+ if (typeof value === "object") {
322
+ const record = value;
323
+ for (const key of ["name", "title", "label", "email"]) {
324
+ if (typeof record[key] === "string") return record[key];
325
+ }
326
+ return JSON.stringify(value);
327
+ }
328
+ return String(value);
329
+ }
330
+ function deriveDetails(args) {
331
+ const details = [];
332
+ for (const [key, value] of Object.entries(args ?? {})) {
333
+ if (isTechnicalKey(key)) continue;
334
+ if (isTechnicalValue(value)) continue;
335
+ if (value === "" || value === null || value === void 0) continue;
336
+ const formatted = formatValue(value);
337
+ if (!formatted || formatted === "\u2014") continue;
338
+ details.push({ label: humanizeKey(key), value: formatted.length > 140 ? `${formatted.slice(0, 140)}\u2026` : formatted });
236
339
  }
237
- const tableBlocks = (message.blocks ?? []).filter((block) => block.type === "table");
238
- return /* @__PURE__ */ jsx(ChatMessage, { from: "assistant", children: tableBlocks.length > 0 ? tableBlocks.map((block, index) => {
340
+ return details;
341
+ }
342
+ function resolveToolPresentation(describe, toolName, args) {
343
+ let description;
344
+ try {
345
+ description = describe?.(toolName, args ?? {});
346
+ } catch {
347
+ description = null;
348
+ }
349
+ const title = description?.title?.trim() || humanizeToolName(toolName);
350
+ const summary = description?.summary?.trim() || null;
351
+ let details = description?.details ?? [];
352
+ const wantsDerived = description?.showDerivedDetails ?? description?.details === void 0;
353
+ if (details.length === 0 && wantsDerived) {
354
+ details = deriveDetails(args ?? {});
355
+ }
356
+ return { details, summary, title };
357
+ }
358
+ function StepTables({ blocks, stepKey }) {
359
+ const tables = (blocks ?? []).filter((block) => block.type === "table");
360
+ if (tables.length === 0) return null;
361
+ return /* @__PURE__ */ jsx(Fragment, { children: tables.map((block, index) => {
239
362
  const { columns, rows } = toDisplayTable(block);
240
363
  return /* @__PURE__ */ jsx("div", { className: "w-full max-w-full", children: /* @__PURE__ */ jsx(
241
364
  DataTablePaged,
@@ -245,57 +368,127 @@ function MessageItem({ message }) {
245
368
  rows,
246
369
  totalCount: typeof block.pagination?.count === "number" ? block.pagination.count : block.row_count
247
370
  }
248
- ) }, `${message.id}-${index}`);
249
- }) : /* @__PURE__ */ jsxs(ChatToolChip, { icon: /* @__PURE__ */ jsx(CheckIcon, {}), children: [
250
- humanizeToolName(message.tool_name ?? "tool"),
251
- " finished"
252
- ] }) });
371
+ ) }, `${stepKey}-table-${index}`);
372
+ }) });
253
373
  }
254
- function humanizeToolName2(toolName) {
255
- return toolName.replace(/^[a-z]+_/, "").replaceAll("_", " ");
374
+ function RawJsonDisclosure({ result }) {
375
+ return /* @__PURE__ */ jsxs(Collapsible, { className: "mt-1.5", children: [
376
+ /* @__PURE__ */ jsxs(CollapsibleTrigger, { className: "group flex items-center gap-1 text-muted-foreground text-xs hover:text-foreground", children: [
377
+ /* @__PURE__ */ jsx(CodeIcon, { className: "size-3" }),
378
+ /* @__PURE__ */ jsx("span", { children: "Raw JSON" }),
379
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 transition-transform group-data-[panel-open]:rotate-180" })
380
+ ] }),
381
+ /* @__PURE__ */ jsx(CollapsiblePanel, { children: /* @__PURE__ */ jsx("pre", { className: "mt-1 max-h-64 overflow-auto rounded-lg bg-muted p-2 font-mono text-[11px] leading-snug text-muted-foreground", children: result }) })
382
+ ] });
383
+ }
384
+ function StepDetail({
385
+ describeToolCall,
386
+ step
387
+ }) {
388
+ const { details, summary, title } = resolveToolPresentation(describeToolCall, step.name, step.args);
389
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border/72 bg-card p-2.5", children: [
390
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 font-medium text-sm", children: [
391
+ step.status === "running" ? /* @__PURE__ */ jsx("span", { className: "size-1.5 animate-pulse rounded-full bg-warning" }) : /* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5 text-success" }),
392
+ title
393
+ ] }),
394
+ summary ? /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-muted-foreground text-xs", children: summary }) : null,
395
+ details.length > 0 ? /* @__PURE__ */ jsx("dl", { className: "mt-1.5 space-y-0.5 text-xs", children: details.map((detail) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
396
+ /* @__PURE__ */ jsx("dt", { className: "shrink-0 text-muted-foreground", children: detail.label }),
397
+ /* @__PURE__ */ jsx("dd", { className: "min-w-0 break-words", children: detail.value })
398
+ ] }, detail.label)) }) : null,
399
+ step.result ? /* @__PURE__ */ jsx(RawJsonDisclosure, { result: step.result }) : null
400
+ ] });
401
+ }
402
+ function ToolActivityGroup({ describeToolCall, steps }) {
403
+ const [open, setOpen] = React3.useState(false);
404
+ if (steps.length === 0) return null;
405
+ const last = steps[steps.length - 1];
406
+ const anyRunning = steps.some((step) => step.status === "running");
407
+ const lastTitle = resolveToolPresentation(describeToolCall, last.name, last.args).title || humanizeToolName(last.name);
408
+ const chipLabel = anyRunning ? `${lastTitle}\u2026` : steps.length > 1 ? lastTitle : `${lastTitle} finished`;
409
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-2", children: [
410
+ /* @__PURE__ */ jsxs(Collapsible, { onOpenChange: setOpen, open, children: [
411
+ /* @__PURE__ */ jsx(
412
+ CollapsibleTrigger,
413
+ {
414
+ render: /* @__PURE__ */ jsx(
415
+ ChatToolChip,
416
+ {
417
+ className: "cursor-pointer pr-2 hover:bg-muted",
418
+ icon: anyRunning ? /* @__PURE__ */ jsx("span", { className: "size-1.5 animate-pulse rounded-full bg-warning" }) : /* @__PURE__ */ jsx(CheckIcon, {}),
419
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
420
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: chipLabel }),
421
+ steps.length > 1 ? /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-background px-1.5 text-[10px] text-foreground/72", children: [
422
+ steps.length,
423
+ " steps"
424
+ ] }) : null,
425
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: cn("size-3 transition-transform", open && "rotate-180") })
426
+ ] })
427
+ }
428
+ )
429
+ }
430
+ ),
431
+ /* @__PURE__ */ jsx(CollapsiblePanel, { children: /* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-col gap-1.5", children: steps.map((step) => /* @__PURE__ */ jsx(StepDetail, { describeToolCall, step }, step.callId)) }) })
432
+ ] }),
433
+ steps.map((step) => /* @__PURE__ */ jsx(StepTables, { blocks: step.blocks, stepKey: step.callId }, `${step.callId}-tables`))
434
+ ] });
256
435
  }
257
- function ToolApprovalCard({ approval, onResolve, resolving }) {
258
- const argumentEntries = Object.entries(approval.arguments ?? {});
436
+ function ToolApprovalCard({
437
+ approval,
438
+ describeToolCall,
439
+ onAlwaysApprove,
440
+ onResolve,
441
+ resolving
442
+ }) {
443
+ const { details, summary, title } = resolveToolPresentation(
444
+ describeToolCall,
445
+ approval.tool_name,
446
+ approval.arguments ?? {}
447
+ );
259
448
  return /* @__PURE__ */ jsxs(ChatToolCard, { className: "border-warning/40", children: [
260
449
  /* @__PURE__ */ jsxs(ChatToolCardHeader, { children: [
261
450
  /* @__PURE__ */ jsx(ShieldAlertIcon, { className: "size-4 text-warning" }),
262
451
  "Approval needed"
263
452
  ] }),
264
- /* @__PURE__ */ jsxs("p", { className: "mt-1 text-muted-foreground", children: [
265
- "The assistant wants to run",
266
- " ",
267
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: humanizeToolName2(approval.tool_name) }),
268
- " ",
269
- "\u2014 this will change your data."
270
- ] }),
271
- argumentEntries.length > 0 && /* @__PURE__ */ jsxs("dl", { className: "mt-2 space-y-0.5 rounded-lg bg-muted p-2 font-mono text-xs", children: [
272
- argumentEntries.slice(0, 8).map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
273
- /* @__PURE__ */ jsxs("dt", { className: "shrink-0 text-muted-foreground", children: [
274
- key,
275
- ":"
276
- ] }),
277
- /* @__PURE__ */ jsx("dd", { className: "truncate", children: typeof value === "object" ? JSON.stringify(value) : String(value) })
278
- ] }, key)),
279
- argumentEntries.length > 8 && /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground", children: [
280
- "\u2026and ",
281
- argumentEntries.length - 8,
282
- " more"
283
- ] })
284
- ] }),
453
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: summary ?? /* @__PURE__ */ jsxs(Fragment, { children: [
454
+ "The assistant wants to ",
455
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: title.toLowerCase() }),
456
+ " \u2014 this will change your data."
457
+ ] }) }),
458
+ details.length > 0 && /* @__PURE__ */ jsx("dl", { className: "mt-2 space-y-1 rounded-lg bg-muted p-2 text-xs", children: details.map((detail) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
459
+ /* @__PURE__ */ jsx("dt", { className: "shrink-0 text-muted-foreground", children: detail.label }),
460
+ /* @__PURE__ */ jsx("dd", { className: "min-w-0 break-words font-medium text-foreground", children: detail.value })
461
+ ] }, detail.label)) }),
285
462
  /* @__PURE__ */ jsxs(ChatToolCardActions, { children: [
286
- /* @__PURE__ */ jsx(
287
- Button,
288
- {
289
- disabled: resolving,
290
- onClick: () => onResolve(approval.id, false),
291
- size: "sm",
292
- variant: "outline",
293
- children: "Decline"
294
- }
295
- ),
296
- /* @__PURE__ */ jsxs(Button, { disabled: resolving, onClick: () => onResolve(approval.id, true), size: "sm", children: [
297
- resolving ? /* @__PURE__ */ jsx(Spinner, {}) : null,
298
- "Approve"
463
+ /* @__PURE__ */ jsx(Button, { disabled: resolving, onClick: () => onResolve(approval.id, false), size: "sm", variant: "outline", children: "Decline" }),
464
+ /* @__PURE__ */ jsxs(Group, { children: [
465
+ /* @__PURE__ */ jsxs(Button, { disabled: resolving, onClick: () => onResolve(approval.id, true), size: "sm", children: [
466
+ resolving ? /* @__PURE__ */ jsx(Spinner, {}) : null,
467
+ "Approve"
468
+ ] }),
469
+ /* @__PURE__ */ jsxs(Menu, { children: [
470
+ /* @__PURE__ */ jsx(
471
+ MenuTrigger,
472
+ {
473
+ render: /* @__PURE__ */ jsx(Button, { "aria-label": "More approve options", disabled: resolving, size: "icon-sm", variant: "default", children: /* @__PURE__ */ jsx(ChevronDownIcon, {}) })
474
+ }
475
+ ),
476
+ /* @__PURE__ */ jsx(MenuPopup, { align: "end", children: /* @__PURE__ */ jsxs(
477
+ MenuItem,
478
+ {
479
+ onClick: () => {
480
+ onAlwaysApprove(approval.tool_name);
481
+ onResolve(approval.id, true);
482
+ },
483
+ children: [
484
+ /* @__PURE__ */ jsx(CheckCheckIcon, {}),
485
+ "Always approve \u201C",
486
+ title,
487
+ "\u201D"
488
+ ]
489
+ }
490
+ ) })
491
+ ] })
299
492
  ] })
300
493
  ] })
301
494
  ] });
@@ -306,24 +499,25 @@ function AgentPanel({
306
499
  chat,
307
500
  className,
308
501
  config,
502
+ onAlwaysApprove,
309
503
  onClose,
310
504
  onToggleAgentMode
311
505
  }) {
312
- const [draft, setDraft] = React2.useState("");
313
- const [view, setView] = React2.useState("chat");
506
+ const [draft, setDraft] = React3.useState("");
507
+ const [view, setView] = React3.useState("chat");
314
508
  const agentName = config.agentName ?? chat.agentInfo?.name ?? "Assistant";
315
509
  const suggestions = config.suggestions ?? [
316
510
  "What can you help me with?",
317
511
  "Give me a quick summary of my data."
318
512
  ];
319
- const handleSubmit = React2.useCallback(
513
+ const handleSubmit = React3.useCallback(
320
514
  (text) => {
321
515
  setDraft("");
322
516
  void chat.send(text);
323
517
  },
324
518
  [chat]
325
519
  );
326
- const openHistory = React2.useCallback(() => {
520
+ const openHistory = React3.useCallback(() => {
327
521
  setView("history");
328
522
  void chat.refreshConversations();
329
523
  }, [chat]);
@@ -461,11 +655,15 @@ function AgentPanel({
461
655
  suggestion
462
656
  )) })
463
657
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
464
- chat.messages.map((message) => /* @__PURE__ */ jsx(MessageItem, { message }, message.id)),
658
+ groupMessages(chat.messages).map(
659
+ (item) => item.kind === "tool-activity" ? /* @__PURE__ */ jsx(ChatMessage, { from: "assistant", children: /* @__PURE__ */ jsx(ToolActivityGroup, { describeToolCall: config.describeToolCall, steps: item.steps }) }, item.key) : /* @__PURE__ */ jsx(MessageItem, { message: item.message }, item.key)
660
+ ),
465
661
  chat.pendingApprovals.map((approval) => /* @__PURE__ */ jsx(
466
662
  ToolApprovalCard,
467
663
  {
468
664
  approval,
665
+ describeToolCall: config.describeToolCall,
666
+ onAlwaysApprove,
469
667
  onResolve: (callId, approve) => void chat.resolveApproval(callId, approve),
470
668
  resolving: chat.resolvingApprovalId === approval.id
471
669
  },
@@ -494,16 +692,16 @@ function AgentPanel({
494
692
  );
495
693
  }
496
694
  function useAgentChat(config) {
497
- const client = React2.useMemo(() => new AgentApiClient(config), [config]);
498
- const [agentInfo, setAgentInfo] = React2.useState(null);
499
- const [conversationId, setConversationId] = React2.useState(null);
500
- const [conversations, setConversations] = React2.useState([]);
501
- const [messages, setMessages] = React2.useState([]);
502
- const [pendingApprovals, setPendingApprovals] = React2.useState([]);
503
- const [sending, setSending] = React2.useState(false);
504
- const [resolvingApprovalId, setResolvingApprovalId] = React2.useState(null);
505
- const [error, setError] = React2.useState(null);
506
- React2.useEffect(() => {
695
+ const client = React3.useMemo(() => new AgentApiClient(config), [config]);
696
+ const [agentInfo, setAgentInfo] = React3.useState(null);
697
+ const [conversationId, setConversationId] = React3.useState(null);
698
+ const [conversations, setConversations] = React3.useState([]);
699
+ const [messages, setMessages] = React3.useState([]);
700
+ const [pendingApprovals, setPendingApprovals] = React3.useState([]);
701
+ const [sending, setSending] = React3.useState(false);
702
+ const [resolvingApprovalId, setResolvingApprovalId] = React3.useState(null);
703
+ const [error, setError] = React3.useState(null);
704
+ React3.useEffect(() => {
507
705
  let cancelled = false;
508
706
  client.getConfig().then((info) => {
509
707
  if (!cancelled) setAgentInfo(info);
@@ -513,11 +711,11 @@ function useAgentChat(config) {
513
711
  cancelled = true;
514
712
  };
515
713
  }, [client]);
516
- const handleFailure = React2.useCallback((err) => {
714
+ const handleFailure = React3.useCallback((err) => {
517
715
  const message = err instanceof AgentApiError ? err.message : "Something went wrong. Please try again.";
518
716
  setError(message);
519
717
  }, []);
520
- const applyResponse = React2.useCallback(
718
+ const applyResponse = React3.useCallback(
521
719
  (response) => {
522
720
  if (response.status === "error" && !response.messages?.length) {
523
721
  setError(response.message ?? "The assistant could not answer.");
@@ -538,7 +736,7 @@ function useAgentChat(config) {
538
736
  },
539
737
  []
540
738
  );
541
- const send = React2.useCallback(
739
+ const send = React3.useCallback(
542
740
  async (text) => {
543
741
  if (sending) return;
544
742
  setSending(true);
@@ -554,7 +752,7 @@ function useAgentChat(config) {
554
752
  },
555
753
  [applyResponse, client, conversationId, handleFailure, sending]
556
754
  );
557
- const resolveApproval = React2.useCallback(
755
+ const resolveApproval = React3.useCallback(
558
756
  async (callId, approve) => {
559
757
  if (resolvingApprovalId) return;
560
758
  setResolvingApprovalId(callId);
@@ -570,13 +768,13 @@ function useAgentChat(config) {
570
768
  },
571
769
  [applyResponse, client, handleFailure, resolvingApprovalId]
572
770
  );
573
- const newChat = React2.useCallback(() => {
771
+ const newChat = React3.useCallback(() => {
574
772
  setConversationId(null);
575
773
  setMessages([]);
576
774
  setPendingApprovals([]);
577
775
  setError(null);
578
776
  }, []);
579
- const refreshConversations = React2.useCallback(async () => {
777
+ const refreshConversations = React3.useCallback(async () => {
580
778
  try {
581
779
  const { conversations: list } = await client.listConversations();
582
780
  setConversations(list);
@@ -584,7 +782,7 @@ function useAgentChat(config) {
584
782
  handleFailure(err);
585
783
  }
586
784
  }, [client, handleFailure]);
587
- const openConversation = React2.useCallback(
785
+ const openConversation = React3.useCallback(
588
786
  async (id) => {
589
787
  setError(null);
590
788
  try {
@@ -598,7 +796,7 @@ function useAgentChat(config) {
598
796
  },
599
797
  [client, handleFailure]
600
798
  );
601
- const deleteConversation = React2.useCallback(
799
+ const deleteConversation = React3.useCallback(
602
800
  async (id) => {
603
801
  try {
604
802
  await client.deleteConversation(id);
@@ -610,7 +808,7 @@ function useAgentChat(config) {
610
808
  },
611
809
  [client, conversationId, handleFailure, newChat]
612
810
  );
613
- const clearError = React2.useCallback(() => setError(null), []);
811
+ const clearError = React3.useCallback(() => setError(null), []);
614
812
  return {
615
813
  agentInfo,
616
814
  clearError,
@@ -629,26 +827,86 @@ function useAgentChat(config) {
629
827
  sending
630
828
  };
631
829
  }
830
+ var STORAGE_PREFIX = "cc-agent-always-approve:";
831
+ function storageKey(clientId) {
832
+ return `${STORAGE_PREFIX}${clientId || "default"}`;
833
+ }
834
+ function readStored(clientId) {
835
+ if (typeof window === "undefined") return /* @__PURE__ */ new Set();
836
+ try {
837
+ const raw = window.localStorage.getItem(storageKey(clientId));
838
+ if (!raw) return /* @__PURE__ */ new Set();
839
+ const parsed = JSON.parse(raw);
840
+ return Array.isArray(parsed) ? new Set(parsed.filter((v) => typeof v === "string")) : /* @__PURE__ */ new Set();
841
+ } catch {
842
+ return /* @__PURE__ */ new Set();
843
+ }
844
+ }
845
+ function useAlwaysApprove(clientId) {
846
+ const [approved, setApproved] = React3.useState(() => /* @__PURE__ */ new Set());
847
+ React3.useEffect(() => {
848
+ setApproved(readStored(clientId));
849
+ }, [clientId]);
850
+ const persist = React3.useCallback(
851
+ (next) => {
852
+ setApproved(next);
853
+ if (typeof window === "undefined") return;
854
+ try {
855
+ window.localStorage.setItem(storageKey(clientId), JSON.stringify([...next]));
856
+ } catch {
857
+ }
858
+ },
859
+ [clientId]
860
+ );
861
+ const isAlwaysApproved = React3.useCallback((toolName) => approved.has(toolName), [approved]);
862
+ const setAlwaysApprove = React3.useCallback(
863
+ (toolName) => {
864
+ if (approved.has(toolName)) return;
865
+ const next = new Set(approved);
866
+ next.add(toolName);
867
+ persist(next);
868
+ },
869
+ [approved, persist]
870
+ );
871
+ const clearAlwaysApprove = React3.useCallback(
872
+ (toolName) => {
873
+ if (!approved.has(toolName)) return;
874
+ const next = new Set(approved);
875
+ next.delete(toolName);
876
+ persist(next);
877
+ },
878
+ [approved, persist]
879
+ );
880
+ return { clearAlwaysApprove, isAlwaysApproved, setAlwaysApprove };
881
+ }
632
882
  function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
633
- const [uncontrolledOpen, setUncontrolledOpen] = React2.useState(false);
883
+ const [uncontrolledOpen, setUncontrolledOpen] = React3.useState(false);
634
884
  const open = controlledOpen ?? uncontrolledOpen;
635
- const setOpen = React2.useCallback(
885
+ const setOpen = React3.useCallback(
636
886
  (next) => {
637
887
  setUncontrolledOpen(next);
638
888
  onOpenChange?.(next);
639
889
  },
640
890
  [onOpenChange]
641
891
  );
642
- const [agentMode, setAgentMode] = React2.useState(false);
892
+ const [agentMode, setAgentMode] = React3.useState(false);
643
893
  const isDesktop = useMediaQuery("(min-width: 640px)", {
644
894
  defaultSSRValue: true,
645
895
  ssr: true
646
896
  });
647
897
  const fullscreen = !isDesktop || agentMode;
648
898
  const chat = useAgentChat(config);
649
- const lastNavigatedMessageId = React2.useRef(null);
899
+ const alwaysApprove = useAlwaysApprove(config.clientId);
900
+ const { pendingApprovals, resolveApproval, resolvingApprovalId } = chat;
901
+ const { isAlwaysApproved } = alwaysApprove;
902
+ React3.useEffect(() => {
903
+ if (!open || resolvingApprovalId) return;
904
+ const target = pendingApprovals.find((approval) => isAlwaysApproved(approval.tool_name));
905
+ if (target) void resolveApproval(target.id, true);
906
+ }, [open, pendingApprovals, resolvingApprovalId, isAlwaysApproved, resolveApproval]);
907
+ const lastNavigatedMessageId = React3.useRef(null);
650
908
  const { messages } = chat;
651
- React2.useEffect(() => {
909
+ React3.useEffect(() => {
652
910
  if (!open || fullscreen || !config.navigate || !config.pages?.length) return;
653
911
  const toolMessages = messages.filter((message) => message.role === "tool");
654
912
  const latest = toolMessages[toolMessages.length - 1];
@@ -660,7 +918,7 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
660
918
  const path = mapping.buildRoute ? mapping.buildRoute(args) : mapping.route;
661
919
  if (path) config.navigate(path);
662
920
  }, [config, fullscreen, messages, open]);
663
- const closePanel = React2.useCallback(() => {
921
+ const closePanel = React3.useCallback(() => {
664
922
  setOpen(false);
665
923
  setAgentMode(false);
666
924
  }, [setOpen]);
@@ -688,6 +946,7 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
688
946
  chat,
689
947
  className: "flex-1",
690
948
  config,
949
+ onAlwaysApprove: alwaysApprove.setAlwaysApprove,
691
950
  onClose: closePanel,
692
951
  onToggleAgentMode: () => setAgentMode((value) => !value)
693
952
  }
@@ -713,6 +972,7 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
713
972
  chat,
714
973
  className: "flex-1",
715
974
  config,
975
+ onAlwaysApprove: alwaysApprove.setAlwaysApprove,
716
976
  onClose: closePanel,
717
977
  onToggleAgentMode: () => setAgentMode(true)
718
978
  }
@@ -737,6 +997,6 @@ function defineAgentConfig(config) {
737
997
  return config;
738
998
  }
739
999
 
740
- export { AgentApiClient, AgentApiError, AgentPanel, AgentWidget, MessageItem, ToolApprovalCard, defineAgentConfig, useAgentChat };
1000
+ export { AgentApiClient, AgentApiError, AgentPanel, AgentWidget, MessageItem, ToolActivityGroup, ToolApprovalCard, defineAgentConfig, deriveDetails, groupMessages, humanizeToolName, resolveToolPresentation, useAgentChat, useAlwaysApprove };
741
1001
  //# sourceMappingURL=index.js.map
742
1002
  //# sourceMappingURL=index.js.map