@agent-platform/ui 0.0.6 → 0.0.7
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/README.md +5 -0
- package/dist/components/agent/agent-container-state.d.ts +2 -0
- package/dist/components/agent/agent-container-state.js +52 -0
- package/dist/components/agent/agent-container-view.d.ts +3 -0
- package/dist/components/agent/agent-container-view.js +22 -0
- package/dist/components/agent/agent-container-view.test.d.ts +1 -0
- package/dist/components/agent/agent-container-view.test.js +16 -0
- package/dist/components/agent/agent-container.d.ts +1 -1
- package/dist/components/agent/agent-container.js +6 -44
- package/dist/components/agent/agent-greeting.d.ts +1 -1
- package/dist/components/agent/agent-greeting.js +4 -4
- package/dist/components/agent/agent-header.d.ts +1 -1
- package/dist/components/agent/agent-header.js +6 -6
- package/dist/components/agent/agent-home-cards.d.ts +2 -2
- package/dist/components/agent/agent-home-cards.js +8 -6
- package/dist/components/agent/agent-screen.d.ts +1 -1
- package/dist/components/agent/agent-screen.js +5 -2
- package/dist/components/agent/approval-ui-model.d.ts +15 -0
- package/dist/components/agent/approval-ui-model.js +27 -0
- package/dist/components/agent/approval-ui-model.test.d.ts +1 -0
- package/dist/components/agent/approval-ui-model.test.js +39 -0
- package/dist/components/agent/defaults.d.ts +0 -6
- package/dist/components/agent/defaults.js +0 -11
- package/dist/components/agent/index.d.ts +0 -2
- package/dist/components/agent/index.js +0 -1
- package/dist/components/agent/input-mode.d.ts +5 -0
- package/dist/components/agent/input-mode.js +9 -0
- package/dist/components/agent/message/index.d.ts +1 -1
- package/dist/components/agent/message/index.js +1 -1
- package/dist/components/agent/message/message-item.js +3 -9
- package/dist/components/agent/message/message-list.js +6 -11
- package/dist/components/agent/message/message-loading.d.ts +0 -6
- package/dist/components/agent/message/message-loading.js +0 -6
- package/dist/components/agent/message/tool-call-card.js +2 -0
- package/dist/components/agent/message/utils.d.ts +8 -0
- package/dist/components/agent/message/utils.js +21 -0
- package/dist/components/agent/provider/agent-context.d.ts +1 -0
- package/dist/components/agent/provider/agent-context.js +3 -0
- package/dist/components/agent/provider/agent-provider.d.ts +1 -1
- package/dist/components/agent/provider/agent-provider.js +19 -5
- package/dist/components/agent/provider/index.d.ts +1 -1
- package/dist/components/agent/provider/runtime-config.js +14 -5
- package/dist/components/agent/provider/types.d.ts +17 -15
- package/dist/components/agent/types.d.ts +28 -3
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/modules/agent/agent.repository.d.ts +58 -0
- package/dist/modules/agent/agent.repository.js +235 -0
- package/dist/modules/agent/agent.repository.test.d.ts +1 -0
- package/dist/modules/agent/agent.repository.test.js +64 -0
- package/dist/modules/agent/domain/chat-state.d.ts +64 -0
- package/dist/modules/agent/domain/chat-state.js +148 -0
- package/dist/modules/agent/domain/chat-state.test.d.ts +1 -0
- package/dist/modules/agent/domain/chat-state.test.js +72 -0
- package/dist/modules/agent/use-agent-chat.d.ts +6 -0
- package/dist/modules/agent/use-agent-chat.js +106 -0
- package/dist/modules/agent/usecases/process-stream.d.ts +26 -0
- package/dist/modules/agent/usecases/process-stream.js +112 -0
- package/dist/modules/agent/usecases/process-stream.test.d.ts +1 -0
- package/dist/modules/agent/usecases/process-stream.test.js +91 -0
- package/dist/modules/agent/usecases/send-message.d.ts +21 -0
- package/dist/modules/agent/usecases/send-message.js +298 -0
- package/dist/modules/agent/usecases/send-message.test.d.ts +1 -0
- package/dist/modules/agent/usecases/send-message.test.js +257 -0
- package/dist/styles/globals.css +0 -56
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { DEFAULT_TOOL_NAME_LABELS } from './defaults';
|
|
4
|
+
import { useOptionalAgentContext } from './provider/agent-context';
|
|
5
|
+
export function useAgentContainerViewModel(props) {
|
|
6
|
+
const { showHeader = true, headerProps, showGreeting = true, greetingProps, showHomeCards = true, cards, onCardClick, toolNameLabels, autoShowMessages = true, showInput = true, inputProps, className, children, } = props;
|
|
7
|
+
const context = useOptionalAgentContext();
|
|
8
|
+
const messages = context?.messages ?? [];
|
|
9
|
+
const pendingToolCalls = context?.pendingToolCalls ?? [];
|
|
10
|
+
const activeApprovalRequest = context?.activeApprovalRequest ?? null;
|
|
11
|
+
const approveToolCall = context?.approveToolCall ?? (() => undefined);
|
|
12
|
+
const rejectToolCall = context?.rejectToolCall ?? (() => undefined);
|
|
13
|
+
const toolApprovalLabels = context?.config.toolApprovalLabels;
|
|
14
|
+
const error = context?.error ?? null;
|
|
15
|
+
const isLoading = context?.isLoading ?? false;
|
|
16
|
+
const sendMessage = context?.sendMessage;
|
|
17
|
+
const clearChat = context?.clearChat;
|
|
18
|
+
const mergedToolNameLabels = useMemo(() => ({ ...DEFAULT_TOOL_NAME_LABELS, ...toolNameLabels }), [toolNameLabels]);
|
|
19
|
+
const effectiveInputProps = useMemo(() => ({
|
|
20
|
+
...inputProps,
|
|
21
|
+
onSend: inputProps?.onSend ?? sendMessage,
|
|
22
|
+
isLoading: inputProps?.isLoading ?? isLoading,
|
|
23
|
+
}), [inputProps, isLoading, sendMessage]);
|
|
24
|
+
const effectiveHeaderProps = useMemo(() => ({
|
|
25
|
+
...headerProps,
|
|
26
|
+
onNewChatClick: headerProps?.onNewChatClick ?? clearChat,
|
|
27
|
+
}), [headerProps, clearChat]);
|
|
28
|
+
return {
|
|
29
|
+
showHeader,
|
|
30
|
+
effectiveHeaderProps,
|
|
31
|
+
showGreeting,
|
|
32
|
+
greetingProps,
|
|
33
|
+
showHomeCards,
|
|
34
|
+
cards,
|
|
35
|
+
onCardClick,
|
|
36
|
+
autoShowMessages,
|
|
37
|
+
showInput,
|
|
38
|
+
effectiveInputProps,
|
|
39
|
+
className,
|
|
40
|
+
messages,
|
|
41
|
+
pendingToolCalls,
|
|
42
|
+
activeApprovalRequest,
|
|
43
|
+
approveToolCall,
|
|
44
|
+
rejectToolCall,
|
|
45
|
+
toolApprovalLabels,
|
|
46
|
+
error,
|
|
47
|
+
isLoading,
|
|
48
|
+
mergedToolNameLabels,
|
|
49
|
+
hasMessages: messages.length > 0,
|
|
50
|
+
children,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { AgentChatStateShape } from './types';
|
|
2
|
+
declare function AgentContainerView({ showHeader, effectiveHeaderProps, showGreeting, greetingProps, showHomeCards, cards, onCardClick, autoShowMessages, showInput, effectiveInputProps, className, children, messages, pendingToolCalls, activeApprovalRequest, approveToolCall, rejectToolCall, toolApprovalLabels, mergedToolNameLabels, isLoading, error, hasMessages, }: AgentChatStateShape): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export { AgentContainerView };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { resolveToolApprovalLabels, resolveToolApprovalRiskLabel } from './approval-ui-model';
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
import { AgentGreeting } from './agent-greeting';
|
|
6
|
+
import { AgentHeader } from './agent-header';
|
|
7
|
+
import { AgentHomeCards } from './agent-home-cards';
|
|
8
|
+
import { AgentInput } from './agent-input';
|
|
9
|
+
import { resolveInputMode } from './input-mode';
|
|
10
|
+
import { MessageList } from './message';
|
|
11
|
+
import { Button } from '../ui/button';
|
|
12
|
+
function AgentContainerView({ showHeader, effectiveHeaderProps, showGreeting, greetingProps, showHomeCards, cards, onCardClick, autoShowMessages, showInput, effectiveInputProps, className, children, messages, pendingToolCalls, activeApprovalRequest, approveToolCall, rejectToolCall, toolApprovalLabels, mergedToolNameLabels, isLoading, error, hasMessages, }) {
|
|
13
|
+
const shouldShowMessages = autoShowMessages && (hasMessages || isLoading);
|
|
14
|
+
const inputMode = resolveInputMode({
|
|
15
|
+
showInput,
|
|
16
|
+
hasActiveApprovalRequest: Boolean(activeApprovalRequest),
|
|
17
|
+
});
|
|
18
|
+
const approvalLabels = resolveToolApprovalLabels(toolApprovalLabels);
|
|
19
|
+
const riskText = resolveToolApprovalRiskLabel(activeApprovalRequest?.riskLevel);
|
|
20
|
+
return (_jsxs("div", { "data-slot": "agent-container", className: cn('flex flex-col w-full h-full', className), children: [showHeader && _jsx(AgentHeader, { ...effectiveHeaderProps }), children ? (children) : shouldShowMessages ? (_jsxs("div", { className: "flex-1 overflow-y-auto bg-white", children: [_jsx(MessageList, { messages: messages, pendingToolCalls: pendingToolCalls, toolNameLabels: mergedToolNameLabels, isLoading: isLoading }), error && (_jsx("div", { className: "mx-4 mb-4 rounded-lg bg-red-50 p-3 text-sm text-red-600", children: error }))] })) : (_jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 bg-background px-4 py-4", children: [showGreeting && _jsx(AgentGreeting, { ...greetingProps }), showHomeCards && cards && _jsx(AgentHomeCards, { cards: cards, onCardClick: onCardClick })] })), inputMode === 'input' && _jsx(AgentInput, { ...effectiveInputProps }), inputMode === 'approval' && activeApprovalRequest && (_jsx("div", { "data-slot": "agent-tool-approval", className: "w-full border-t border-border bg-muted px-3 pb-3 pt-2", children: _jsxs("div", { className: "flex flex-col gap-2 rounded-lg border border-border bg-primary-foreground p-3 shadow-sm", children: [_jsx("div", { className: "text-sm font-bold text-foreground text-balance", children: approvalLabels.title }), _jsx("div", { className: "text-sm font-medium text-foreground text-balance", children: activeApprovalRequest.actionLabel }), _jsxs("div", { className: "text-xs text-muted-foreground text-pretty", children: [approvalLabels.description, " (", riskText, ")"] }), _jsxs("div", { className: "flex gap-2 pt-1", children: [_jsx(Button, { type: "button", variant: "outline", onClick: rejectToolCall, className: "flex-1", children: approvalLabels.rejectButton }), _jsx(Button, { type: "button", onClick: approveToolCall, className: "flex-1", children: approvalLabels.approveButton })] })] }) }))] }));
|
|
21
|
+
}
|
|
22
|
+
export { AgentContainerView };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { resolveInputMode } from './input-mode';
|
|
3
|
+
describe('resolveInputMode', () => {
|
|
4
|
+
it('承認待ちがある場合はapprovalを返す', () => {
|
|
5
|
+
expect(resolveInputMode({ showInput: true, hasActiveApprovalRequest: true })).toBe('approval');
|
|
6
|
+
});
|
|
7
|
+
it('showInput=falseでも承認待ちがあればapprovalを返す', () => {
|
|
8
|
+
expect(resolveInputMode({ showInput: false, hasActiveApprovalRequest: true })).toBe('approval');
|
|
9
|
+
});
|
|
10
|
+
it('承認待ちがなく入力表示有効ならinputを返す', () => {
|
|
11
|
+
expect(resolveInputMode({ showInput: true, hasActiveApprovalRequest: false })).toBe('input');
|
|
12
|
+
});
|
|
13
|
+
it('入力表示無効ならnoneを返す', () => {
|
|
14
|
+
expect(resolveInputMode({ showInput: false, hasActiveApprovalRequest: false })).toBe('none');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { AgentContainerProps } from './types';
|
|
2
|
-
declare function AgentContainer({
|
|
2
|
+
declare function AgentContainer({ ...containerProps }: AgentContainerProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export { AgentContainer };
|
|
@@ -1,47 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { DEFAULT_TOOL_NAME_LABELS } from './defaults';
|
|
9
|
-
import { MessageList } from './message';
|
|
10
|
-
import { useAgentContext } from './provider';
|
|
11
|
-
function AgentContainer({ showHeader = true, headerProps, showGreeting = true, greetingProps, showHomeCards = true, cards, onCardClick, toolNameLabels, autoShowMessages = true, showInput = true, inputProps, className, children, }) {
|
|
12
|
-
// AgentProvider配下で使用されている場合のみ有効
|
|
13
|
-
let messages = [];
|
|
14
|
-
let pendingToolCalls = [];
|
|
15
|
-
let error = null;
|
|
16
|
-
let sendMessage;
|
|
17
|
-
let clearChat;
|
|
18
|
-
let isLoading = false;
|
|
19
|
-
try {
|
|
20
|
-
const context = useAgentContext();
|
|
21
|
-
messages = context.messages;
|
|
22
|
-
pendingToolCalls = context.pendingToolCalls;
|
|
23
|
-
error = context.error;
|
|
24
|
-
sendMessage = context.sendMessage;
|
|
25
|
-
clearChat = context.clearChat;
|
|
26
|
-
isLoading = context.isLoading;
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
// AgentProvider外で使用されている場合は無視
|
|
30
|
-
}
|
|
31
|
-
// propsのデフォルト値をコンテキストから適用
|
|
32
|
-
const effectiveInputProps = {
|
|
33
|
-
...inputProps,
|
|
34
|
-
onSend: inputProps?.onSend ?? sendMessage,
|
|
35
|
-
isLoading: inputProps?.isLoading ?? isLoading,
|
|
36
|
-
};
|
|
37
|
-
const effectiveHeaderProps = {
|
|
38
|
-
...headerProps,
|
|
39
|
-
onNewChatClick: headerProps?.onNewChatClick ?? clearChat,
|
|
40
|
-
};
|
|
41
|
-
const hasMessages = messages.length > 0;
|
|
42
|
-
const mergedToolNameLabels = { ...DEFAULT_TOOL_NAME_LABELS, ...toolNameLabels };
|
|
43
|
-
// 自動メッセージ表示用コンテンツ
|
|
44
|
-
const autoMessagesContent = autoShowMessages && (hasMessages || isLoading) && (_jsxs("div", { className: "flex-1 overflow-y-auto bg-white", children: [_jsx(MessageList, { messages: messages, pendingToolCalls: pendingToolCalls, toolNameLabels: mergedToolNameLabels, isLoading: isLoading }), error && (_jsx("div", { className: "mx-4 mb-4 rounded-lg bg-red-50 p-3 text-sm text-red-600", children: error }))] }));
|
|
45
|
-
return (_jsxs("div", { "data-slot": "agent-container", className: cn('flex flex-col w-full h-full', className), children: [showHeader && _jsx(AgentHeader, { ...effectiveHeaderProps }), children ? (children) : autoMessagesContent ? (autoMessagesContent) : (_jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 bg-background px-4 py-4", children: [showGreeting && _jsx(AgentGreeting, { ...greetingProps }), showHomeCards && cards && _jsx(AgentHomeCards, { cards: cards, onCardClick: onCardClick })] })), showInput && _jsx(AgentInput, { ...effectiveInputProps })] }));
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useAgentContainerViewModel } from './agent-container-state';
|
|
4
|
+
import { AgentContainerView } from './agent-container-view';
|
|
5
|
+
function AgentContainer({ ...containerProps }) {
|
|
6
|
+
const viewModel = useAgentContainerViewModel(containerProps);
|
|
7
|
+
return _jsx(AgentContainerView, { ...viewModel });
|
|
46
8
|
}
|
|
47
9
|
export { AgentContainer };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { cn } from
|
|
4
|
-
function AgentGreeting({ greeting =
|
|
5
|
-
return (_jsxs("div", { "data-slot": "agent-greeting", className: cn(
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
function AgentGreeting({ greeting = 'こんにちは 👋', subtext = '何かお手伝いできることはありますか?', className, }) {
|
|
5
|
+
return (_jsxs("div", { "data-slot": "agent-greeting", className: cn('flex flex-col items-center gap-1.5 text-center', className), children: [_jsx("p", { className: "text-sm text-muted-foreground", children: greeting }), _jsx("p", { className: "text-lg font-semibold tracking-tight text-balance text-card-foreground", children: subtext })] }));
|
|
6
6
|
}
|
|
7
7
|
export { AgentGreeting };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { AgentHeaderProps } from
|
|
1
|
+
import type { AgentHeaderProps } from './types';
|
|
2
2
|
declare function AgentHeader({ historyLabel, onHistoryClick, onSettingsClick, onNewChatClick, newChatLabel, settingsAriaLabel, className, }: AgentHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export { AgentHeader };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { CaretDown, Gear } from
|
|
4
|
-
import { cn } from
|
|
5
|
-
import { Button } from
|
|
6
|
-
function AgentHeader({ historyLabel =
|
|
7
|
-
return (_jsxs("div", { "data-slot": "agent-header", className: cn(
|
|
3
|
+
import { CaretDown, Gear } from '@phosphor-icons/react/dist/ssr';
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
import { Button } from '../ui/button';
|
|
6
|
+
function AgentHeader({ historyLabel = '履歴', onHistoryClick, onSettingsClick, onNewChatClick, newChatLabel = '新規チャット', settingsAriaLabel = '設定', className, }) {
|
|
7
|
+
return (_jsxs("div", { "data-slot": "agent-header", className: cn('flex w-full items-center justify-between bg-background px-4 py-2', className), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("span", { className: "text-sm font-semibold text-foreground", children: historyLabel }), _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onHistoryClick, "aria-label": historyLabel, children: _jsx(CaretDown, { className: "size-5" }) })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onSettingsClick, "aria-label": settingsAriaLabel, className: "text-foreground", children: _jsx(Gear, { className: "size-5" }) }), _jsx(Button, { size: "sm", onClick: onNewChatClick, className: "bg-foreground text-background text-xs font-medium hover:bg-foreground/90", children: newChatLabel })] })] }));
|
|
8
8
|
}
|
|
9
9
|
export { AgentHeader };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { AgentHomeCardProps } from
|
|
1
|
+
import type { AgentHomeCardProps } from './types';
|
|
2
2
|
interface AgentHomeCardsProps {
|
|
3
3
|
cards: AgentHomeCardProps[];
|
|
4
4
|
onCardClick?: (cardId: string) => void;
|
|
5
5
|
className?: string;
|
|
6
6
|
}
|
|
7
|
-
declare function AgentHomeCards({ cards, onCardClick, className
|
|
7
|
+
declare function AgentHomeCards({ cards, onCardClick, className }: AgentHomeCardsProps): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export { AgentHomeCards };
|
|
9
9
|
export type { AgentHomeCardsProps };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { cn } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
function AgentHomeCard({ id, icon, title, description, onClick, className }) {
|
|
5
|
+
return (_jsxs("button", { type: "button", "data-slot": "agent-home-card", "data-card-id": id, onClick: onClick, className: cn('flex flex-1 flex-col items-start gap-2.5 rounded-lg border border-border bg-card p-3 text-left transition-all hover:border-primary/30 hover:bg-accent focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring', className), children: [_jsx("div", { className: "text-muted-foreground [&_svg]:size-5", children: icon }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("p", { className: "text-sm font-semibold leading-none text-card-foreground", children: title }), _jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: description })] })] }));
|
|
6
|
+
}
|
|
7
|
+
function AgentHomeCards({ cards, onCardClick, className }) {
|
|
8
|
+
return (_jsx("div", { "data-slot": "agent-home-cards", className: cn('flex w-full items-start justify-center gap-4 px-12', className), children: cards.map((card) => (_jsx(AgentHomeCard, { ...card, onClick: () => {
|
|
7
9
|
card.onClick?.();
|
|
8
10
|
onCardClick?.(card.id);
|
|
9
11
|
} }, card.id))) }));
|
|
@@ -2,4 +2,4 @@ import type { AgentProviderConfig } from './provider';
|
|
|
2
2
|
import type { AgentContainerProps } from './types';
|
|
3
3
|
export interface AgentScreenProps extends AgentProviderConfig, AgentContainerProps {
|
|
4
4
|
}
|
|
5
|
-
export declare function AgentScreen({ agentId,
|
|
5
|
+
export declare function AgentScreen({ agentId, onError, authToken, getAuthToken, getAgentHeaders, disableToolApiAuthHeader, toolApprovalLabels, ...containerProps }: AgentScreenProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { AgentContainer } from './agent-container';
|
|
4
4
|
import { AgentProvider } from './provider';
|
|
5
|
-
|
|
6
|
-
return
|
|
5
|
+
function AgentScreenController({ ...containerProps }) {
|
|
6
|
+
return _jsx(AgentContainer, { ...containerProps });
|
|
7
|
+
}
|
|
8
|
+
export function AgentScreen({ agentId, onError, authToken, getAuthToken, getAgentHeaders, disableToolApiAuthHeader, toolApprovalLabels, ...containerProps }) {
|
|
9
|
+
return (_jsx(AgentProvider, { agentId: agentId, onError: onError, authToken: authToken, getAuthToken: getAuthToken, getAgentHeaders: getAgentHeaders, disableToolApiAuthHeader: disableToolApiAuthHeader, toolApprovalLabels: toolApprovalLabels, children: _jsx(AgentScreenController, { ...containerProps }) }));
|
|
7
10
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ToolApprovalLabels } from './provider/types';
|
|
2
|
+
type ToolApprovalRiskLevel = 'read' | 'write' | 'destructive';
|
|
3
|
+
declare function resolveToolApprovalLabels(toolApprovalLabels?: ToolApprovalLabels): {
|
|
4
|
+
readonly title: "この操作を実行してもよろしいでしょうか?";
|
|
5
|
+
readonly description: "この操作はデータを変更する可能性があります。内容を確認して、承認または非承認を選択してください。";
|
|
6
|
+
readonly approveButton: "実行する";
|
|
7
|
+
readonly rejectButton: "実行しない";
|
|
8
|
+
} | {
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
approveButton: string;
|
|
12
|
+
rejectButton: string;
|
|
13
|
+
};
|
|
14
|
+
declare function resolveToolApprovalRiskLabel(riskLevel?: ToolApprovalRiskLevel): "高リスク操作" | "変更操作" | "確認操作";
|
|
15
|
+
export { resolveToolApprovalLabels, resolveToolApprovalRiskLabel };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const DEFAULT_TOOL_APPROVAL_LABELS = {
|
|
2
|
+
title: 'この操作を実行してもよろしいでしょうか?',
|
|
3
|
+
description: 'この操作はデータを変更する可能性があります。内容を確認して、承認または非承認を選択してください。',
|
|
4
|
+
approveButton: '実行する',
|
|
5
|
+
rejectButton: '実行しない',
|
|
6
|
+
};
|
|
7
|
+
function resolveToolApprovalLabels(toolApprovalLabels) {
|
|
8
|
+
if (!toolApprovalLabels) {
|
|
9
|
+
return DEFAULT_TOOL_APPROVAL_LABELS;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
title: toolApprovalLabels.title ?? DEFAULT_TOOL_APPROVAL_LABELS.title,
|
|
13
|
+
description: toolApprovalLabels.description ?? DEFAULT_TOOL_APPROVAL_LABELS.description,
|
|
14
|
+
approveButton: toolApprovalLabels.approveButton ?? DEFAULT_TOOL_APPROVAL_LABELS.approveButton,
|
|
15
|
+
rejectButton: toolApprovalLabels.rejectButton ?? DEFAULT_TOOL_APPROVAL_LABELS.rejectButton,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function resolveToolApprovalRiskLabel(riskLevel) {
|
|
19
|
+
if (riskLevel === 'destructive') {
|
|
20
|
+
return '高リスク操作';
|
|
21
|
+
}
|
|
22
|
+
if (riskLevel === 'write') {
|
|
23
|
+
return '変更操作';
|
|
24
|
+
}
|
|
25
|
+
return '確認操作';
|
|
26
|
+
}
|
|
27
|
+
export { resolveToolApprovalLabels, resolveToolApprovalRiskLabel };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { resolveToolApprovalLabels, resolveToolApprovalRiskLabel } from './approval-ui-model';
|
|
3
|
+
describe('resolveToolApprovalLabels', () => {
|
|
4
|
+
it('未指定時はデフォルト文言を返す', () => {
|
|
5
|
+
const labels = resolveToolApprovalLabels();
|
|
6
|
+
expect(labels).toEqual({
|
|
7
|
+
title: 'この操作を実行してもよろしいでしょうか?',
|
|
8
|
+
description: 'この操作はデータを変更する可能性があります。内容を確認して、承認または非承認を選択してください。',
|
|
9
|
+
approveButton: '実行する',
|
|
10
|
+
rejectButton: '実行しない',
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
it('指定された文言で部分上書きできる', () => {
|
|
14
|
+
const labels = resolveToolApprovalLabels({
|
|
15
|
+
description: '説明を上書き',
|
|
16
|
+
rejectButton: '非承認',
|
|
17
|
+
});
|
|
18
|
+
expect(labels).toEqual({
|
|
19
|
+
title: 'この操作を実行してもよろしいでしょうか?',
|
|
20
|
+
description: '説明を上書き',
|
|
21
|
+
approveButton: '実行する',
|
|
22
|
+
rejectButton: '非承認',
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('resolveToolApprovalRiskLabel', () => {
|
|
27
|
+
it('destructiveは高リスク操作を返す', () => {
|
|
28
|
+
expect(resolveToolApprovalRiskLabel('destructive')).toBe('高リスク操作');
|
|
29
|
+
});
|
|
30
|
+
it('writeは変更操作を返す', () => {
|
|
31
|
+
expect(resolveToolApprovalRiskLabel('write')).toBe('変更操作');
|
|
32
|
+
});
|
|
33
|
+
it('readは確認操作を返す', () => {
|
|
34
|
+
expect(resolveToolApprovalRiskLabel('read')).toBe('確認操作');
|
|
35
|
+
});
|
|
36
|
+
it('未指定時は確認操作を返す', () => {
|
|
37
|
+
expect(resolveToolApprovalRiskLabel()).toBe('確認操作');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export type { AgentPopupWidgetProps } from './agent-popup-widget';
|
|
2
|
-
export { AgentPopupWidget } from './agent-popup-widget';
|
|
3
1
|
export type { AgentScreenProps } from './agent-screen';
|
|
4
2
|
export { AgentScreen } from './agent-screen';
|
|
5
3
|
export type { AgentHomeCardProps } from './types';
|
|
@@ -4,5 +4,5 @@ export { MessageItem } from './message-item';
|
|
|
4
4
|
export type { MessageItemProps } from './message-item';
|
|
5
5
|
export { ToolCallCard } from './tool-call-card';
|
|
6
6
|
export type { ToolCallCardProps } from './tool-call-card';
|
|
7
|
-
export { MessageLoading
|
|
7
|
+
export { MessageLoading } from './message-loading';
|
|
8
8
|
export type { MessageLoadingProps } from './message-loading';
|
|
@@ -3,17 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { cn } from '../../../lib/utils';
|
|
4
4
|
import { Markdown } from './markdown';
|
|
5
5
|
import { ToolCallCard } from './tool-call-card';
|
|
6
|
+
import { extractTextContent, extractToolCalls } from './utils';
|
|
6
7
|
export function MessageItem({ message, pendingToolCalls, toolNameLabels }) {
|
|
7
8
|
const isUser = message.role === 'user';
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
.filter((c) => c.type === 'text')
|
|
11
|
-
.map((c) => c.text)
|
|
12
|
-
.join('');
|
|
13
|
-
// メッセージからツールコールを抽出
|
|
14
|
-
const toolCalls = message.content
|
|
15
|
-
.filter((c) => c.type === 'tool-call')
|
|
16
|
-
.map((c) => c.toolCall);
|
|
9
|
+
const textContent = extractTextContent(message.content);
|
|
10
|
+
const toolCalls = extractToolCalls(message.content);
|
|
17
11
|
// 空のストリーミング中の assistant メッセージは非表示(MessageLoading が表示されるため)
|
|
18
12
|
if (!isUser && message.isStreaming && !textContent && toolCalls.length === 0) {
|
|
19
13
|
return null;
|
|
@@ -3,23 +3,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useRef } from 'react';
|
|
4
4
|
import { MessageItem } from './message-item';
|
|
5
5
|
import { MessageLoading } from './message-loading';
|
|
6
|
+
import { shouldShowLoadingIndicator } from './utils';
|
|
6
7
|
export function MessageList({ messages, pendingToolCalls, toolNameLabels, isLoading = false, }) {
|
|
7
8
|
const bottomRef = useRef(null);
|
|
8
|
-
// 新しいメッセージが追加されたら自動スクロール
|
|
9
9
|
useEffect(() => {
|
|
10
10
|
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
11
|
-
}, []);
|
|
11
|
+
}, [messages, isLoading]);
|
|
12
12
|
if (messages.length === 0 && !isLoading) {
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Show loading indicator when:
|
|
20
|
-
// 1. isLoading is true AND
|
|
21
|
-
// 2. No messages yet OR last message is from user OR last assistant has no content
|
|
22
|
-
const showLoadingIndicator = isLoading &&
|
|
23
|
-
(messages.length === 0 || lastMessage?.role === 'user' || !lastAssistantHasContent);
|
|
15
|
+
const showLoadingIndicator = shouldShowLoadingIndicator({
|
|
16
|
+
messages,
|
|
17
|
+
isLoading,
|
|
18
|
+
});
|
|
24
19
|
return (_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [messages.map((message) => (_jsx(MessageItem, { message: message, pendingToolCalls: pendingToolCalls, toolNameLabels: toolNameLabels }, message.id))), showLoadingIndicator && _jsx(MessageLoading, {}), _jsx("div", { ref: bottomRef })] }));
|
|
25
20
|
}
|
|
@@ -2,9 +2,3 @@ export interface MessageLoadingProps {
|
|
|
2
2
|
className?: string;
|
|
3
3
|
}
|
|
4
4
|
export declare function MessageLoading({ className }: MessageLoadingProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
/**
|
|
6
|
-
* Compact loading indicator for inline use (e.g., inside MessageItem during streaming)
|
|
7
|
-
*/
|
|
8
|
-
export declare function ThinkingDots({ className }: {
|
|
9
|
-
className?: string;
|
|
10
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -8,9 +8,3 @@ function LoadingDots() {
|
|
|
8
8
|
export function MessageLoading({ className }) {
|
|
9
9
|
return (_jsx("output", { className: cn('flex justify-start', className), "aria-label": "AI\u5FDC\u7B54\u3092\u5F85\u6A5F\u4E2D", children: _jsx("div", { className: "max-w-xl", children: _jsx("div", { className: "rounded-lg border border-border bg-card px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: "\u8003\u3048\u4E2D" }), _jsx(LoadingDots, {})] }) }) }) }));
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Compact loading indicator for inline use (e.g., inside MessageItem during streaming)
|
|
13
|
-
*/
|
|
14
|
-
export function ThinkingDots({ className }) {
|
|
15
|
-
return (_jsx("output", { className: cn('flex items-center gap-2', className), "aria-label": "\u8003\u3048\u4E2D", children: _jsx(LoadingDots, {}) }));
|
|
16
|
-
}
|
|
@@ -4,12 +4,14 @@ import { CheckCircle, XCircle } from '@phosphor-icons/react/dist/ssr';
|
|
|
4
4
|
import { cn } from '../../../lib/utils';
|
|
5
5
|
const defaultStatusLabels = {
|
|
6
6
|
pending: '待機中',
|
|
7
|
+
'awaiting-approval': '承認待ち',
|
|
7
8
|
executing: '実行中',
|
|
8
9
|
completed: '完了',
|
|
9
10
|
error: 'エラー',
|
|
10
11
|
};
|
|
11
12
|
const statusColors = {
|
|
12
13
|
pending: 'bg-muted text-muted-foreground',
|
|
14
|
+
'awaiting-approval': 'bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-300',
|
|
13
15
|
executing: 'bg-primary/10 text-primary',
|
|
14
16
|
completed: 'bg-green-100 text-green-600 dark:bg-green-950 dark:text-green-400',
|
|
15
17
|
error: 'bg-destructive/10 text-destructive',
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentMessage, ToolCallData } from '../provider/types';
|
|
2
|
+
export interface LoadingIndicatorInput {
|
|
3
|
+
messages: AgentMessage[];
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function extractTextContent(content: AgentMessage['content']): string;
|
|
7
|
+
export declare function extractToolCalls(content: AgentMessage['content']): ToolCallData[];
|
|
8
|
+
export declare function shouldShowLoadingIndicator({ messages, isLoading, }: LoadingIndicatorInput): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function extractTextContent(content) {
|
|
2
|
+
return content
|
|
3
|
+
.filter((part) => part.type === 'text')
|
|
4
|
+
.map((part) => part.text)
|
|
5
|
+
.join('');
|
|
6
|
+
}
|
|
7
|
+
export function extractToolCalls(content) {
|
|
8
|
+
return content
|
|
9
|
+
.filter((part) => part.type === 'tool-call')
|
|
10
|
+
.map((part) => part.toolCall);
|
|
11
|
+
}
|
|
12
|
+
export function shouldShowLoadingIndicator({ messages, isLoading, }) {
|
|
13
|
+
if (!isLoading)
|
|
14
|
+
return false;
|
|
15
|
+
const lastMessage = messages[messages.length - 1];
|
|
16
|
+
const hasNoMessages = messages.length === 0;
|
|
17
|
+
if (hasNoMessages)
|
|
18
|
+
return true;
|
|
19
|
+
const lastAssistantHasContent = lastMessage?.role === 'assistant' && extractTextContent(lastMessage.content).trim().length > 0;
|
|
20
|
+
return lastMessage?.role === 'user' || !lastAssistantHasContent;
|
|
21
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { AgentContextValue } from './types';
|
|
2
2
|
export declare const AgentContext: import("react").Context<AgentContextValue | null>;
|
|
3
3
|
export declare function useAgentContext(): AgentContextValue;
|
|
4
|
+
export declare function useOptionalAgentContext(): AgentContextValue | null;
|
|
@@ -7,4 +7,4 @@ export interface AgentProviderProps extends AgentProviderConfig {
|
|
|
7
7
|
* エージェントチャット機能を提供する内部Provider
|
|
8
8
|
* 公開APIは AgentScreen / AgentPopupWidget を使用する。
|
|
9
9
|
*/
|
|
10
|
-
export declare function AgentProvider({ children, agentId,
|
|
10
|
+
export declare function AgentProvider({ children, agentId, onError, authToken, getAuthToken, getAgentHeaders, disableToolApiAuthHeader, toolApprovalLabels, }: AgentProviderProps): import("react/jsx-runtime").JSX.Element;
|