Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions apps/aevatar-console-web/src/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,38 @@ const enUSMessages = {
'pages.chat.index.reviewPlan': 'Review the plan before creating resources.',
'pages.chat.index.scopeValue': 'Scope {scopeId}',
'pages.chat.index.status.completed': 'Completed',
'pages.chat.index.statusCenter.actionRequired': 'Action required',
'pages.chat.index.statusCenter.approvalTool': 'Tool: {toolName}',
'pages.chat.index.statusCenter.completed': 'Completed',
'pages.chat.index.statusCenter.completedDescription':
'The run completed. You can continue in chat.',
'pages.chat.index.statusCenter.completedTextDescription':
'The run completed. You can continue in chat.',
'pages.chat.index.statusCenter.completedTitle': 'Workflow created',
'pages.chat.index.statusCenter.completedWithTargetDescription':
'Open Studio to review the created workflow.',
'pages.chat.index.statusCenter.confirmDescription':
'No resources are created until you confirm.',
'pages.chat.index.statusCenter.creatingDescription':
'The current chat run is still working.',
'pages.chat.index.statusCenter.creatingTitle': 'Creating workflow',
'pages.chat.index.statusCenter.currentStep': 'Current step: {stepName}',
'pages.chat.index.statusCenter.error': 'Run stopped',
'pages.chat.index.statusCenter.errorDescription':
'This run stopped or failed. You can send a follow-up message to continue.',
'pages.chat.index.statusCenter.reviewAction': 'Review action',
'pages.chat.index.statusCenter.reviewBeforeCreate':
'Review before creating resources',
'pages.chat.index.statusCenter.runInterventionFallback':
'This run is waiting for an operator response.',
'pages.chat.index.statusCenter.runInterventionWaiting':
'Run input or approval is waiting below.',
'pages.chat.index.statusCenter.runningTool': 'Running tool: {toolName}',
'pages.chat.index.statusCenter.stopHelp':
'Stop only cancels the current chat run.',
'pages.chat.index.statusCenter.streamingTitle': 'Working on your request',
'pages.chat.index.statusCenter.toolApprovalWaiting':
'Tool approval is waiting below.',
'pages.chat.index.status.creating': 'Creating',
'pages.chat.index.status.draft': 'Draft',
'pages.chat.index.status.error': 'Error',
Expand Down
32 changes: 32 additions & 0 deletions apps/aevatar-console-web/src/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,38 @@ const zhCNMessages = {
'pages.chat.index.reviewPlan': '创建资源前请先确认计划。',
'pages.chat.index.scopeValue': 'Scope {scopeId}',
'pages.chat.index.status.completed': '已完成',
'pages.chat.index.statusCenter.actionRequired': '需要处理操作',
'pages.chat.index.statusCenter.approvalTool': '工具:{toolName}',
'pages.chat.index.statusCenter.completed': '已完成',
'pages.chat.index.statusCenter.completedDescription':
'本次运行已完成,你可以继续在 Chat 中追问。',
'pages.chat.index.statusCenter.completedTextDescription':
'本次运行已完成,你可以继续在 Chat 中追问。',
'pages.chat.index.statusCenter.completedTitle': 'Workflow 已创建',
'pages.chat.index.statusCenter.completedWithTargetDescription':
'打开 Studio 查看刚创建的 workflow。',
'pages.chat.index.statusCenter.confirmDescription':
'在你确认之前,不会真正创建任何资源。',
'pages.chat.index.statusCenter.creatingDescription':
'当前 Chat run 仍在处理中。',
'pages.chat.index.statusCenter.creatingTitle': '正在创建 workflow',
'pages.chat.index.statusCenter.currentStep': '当前步骤:{stepName}',
'pages.chat.index.statusCenter.error': '运行已停止',
'pages.chat.index.statusCenter.errorDescription':
'这次运行已停止或失败。你可以发送后续消息继续。',
'pages.chat.index.statusCenter.reviewAction': '查看操作',
'pages.chat.index.statusCenter.reviewBeforeCreate':
'创建资源前先确认',
'pages.chat.index.statusCenter.runInterventionFallback':
'当前 run 正在等待操作员响应。',
'pages.chat.index.statusCenter.runInterventionWaiting':
'当前有输入或审批等待你处理。',
'pages.chat.index.statusCenter.runningTool': '正在运行工具:{toolName}',
'pages.chat.index.statusCenter.stopHelp':
'停止只会取消当前这次 chat run。',
'pages.chat.index.statusCenter.streamingTitle': '正在处理你的请求',
'pages.chat.index.statusCenter.toolApprovalWaiting':
'当前有工具审批在下方等待处理。',
'pages.chat.index.status.creating': '创建中',
'pages.chat.index.status.draft': '草稿',
'pages.chat.index.status.error': '错误',
Expand Down
151 changes: 151 additions & 0 deletions apps/aevatar-console-web/src/pages/chat/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ async function sendPrompt(prompt: string): Promise<void> {
fireEvent.click(screen.getByRole("button", { name: "Send" }));
}

function seedConversationHistory(
conversations: readonly Record<string, unknown>[]
): void {
window.localStorage.setItem(
"aevatar.chat.localHistory.v1:scope-a",
JSON.stringify(conversations)
);
}

describe("ChatPage MVP", () => {
beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -259,6 +268,148 @@ describe("ChatPage MVP", () => {
expect(await screen.findByRole("button", { name: "Open Workflow Studio" })).toBeTruthy();
});

it("shows a top-level creating summary from runtime step and tool events", async () => {
const stream = createControlledSseResponse();
(authFetch as jest.Mock)
.mockResolvedValueOnce(
createSseResponse([
{
runFinished: {
result: {
output: "Plan ready. Please confirm before I create resources.",
},
},
},
])
)
.mockResolvedValueOnce(stream.response);

renderWithQueryClient(<ChatPage />);
await sendPrompt("Create a workflow from chat");
fireEvent.click(await screen.findByRole("button", { name: "Confirm and create" }));

stream.enqueue({
stepStarted: {
stepName: "Create workflow draft",
},
timestamp: 1,
});
stream.enqueue({
toolCallStart: {
toolCallId: "tool-1",
toolName: "create_team_member",
},
timestamp: 2,
});

expect(await screen.findByText("Creating workflow")).toBeTruthy();
expect(await screen.findByText("Current step: Create workflow draft")).toBeTruthy();
expect(await screen.findByText("Running tool: create_team_member")).toBeTruthy();
expect(
await screen.findByText("Stop only cancels the current chat run.")
).toBeTruthy();
});

it("shows confirmation in the top-level status center for restored conversations", async () => {
seedConversationHistory([
{
createdAt: "2026-07-10T07:00:00.000Z",
id: "conv-confirm",
messages: [
{
content: "Plan ready. Please confirm before I create resources.",
id: "assistant-confirm",
role: "assistant",
status: "complete",
timestamp: 1,
},
],
scopeId: "scope-a",
status: "needs_confirmation",
title: "Workflow planning",
updatedAt: "2026-07-10T07:00:00.000Z",
},
]);

renderWithQueryClient(<ChatPage />);
fireEvent.click(await screen.findByRole("button", { name: "Workflow planning" }));

expect(await screen.findByText("Review before creating resources")).toBeTruthy();
expect(await screen.findByRole("button", { name: "Confirm and create" })).toBeTruthy();
});

it("surfaces pending operator action in the top-level status center", async () => {
seedConversationHistory([
{
createdAt: "2026-07-10T07:00:00.000Z",
id: "conv-action",
messages: [
{
content: "Waiting for approval.",
id: "assistant-action",
pendingApproval: {
argumentsJson: "{\"scopeId\":\"scope-a\"}",
isDestructive: false,
requestId: "approval-1",
timeoutSeconds: 30,
toolCallId: "tool-approval-1",
toolName: "create_workflow",
},
role: "assistant",
status: "complete",
timestamp: 1,
},
],
scopeId: "scope-a",
status: "creating",
title: "Workflow approval",
updatedAt: "2026-07-10T07:00:00.000Z",
},
]);

renderWithQueryClient(<ChatPage />);
fireEvent.click(await screen.findByRole("button", { name: "Workflow approval" }));

expect(await screen.findByText("Action required")).toBeTruthy();
expect(await screen.findByText("Tool approval is waiting below.")).toBeTruthy();
expect(await screen.findByRole("button", { name: "Review action" })).toBeTruthy();
});

it("shows completed next-step guidance in the top-level status center", async () => {
seedConversationHistory([
{
createdAt: "2026-07-10T07:00:00.000Z",
id: "conv-complete",
messages: [
{
content: "Created.",
id: "assistant-complete",
role: "assistant",
status: "complete",
timestamp: 1,
},
],
scopeId: "scope-a",
status: "completed_with_studio_target",
target: {
memberId: "member-a",
scopeId: "scope-a",
teamId: "team-a",
workflowId: "workflow-a",
},
title: "Workflow created",
updatedAt: "2026-07-10T07:00:00.000Z",
},
]);

renderWithQueryClient(<ChatPage />);
fireEvent.click(await screen.findByRole("button", { name: "Workflow created" }));

expect(await screen.findByText("Workflow created")).toBeTruthy();
expect(await screen.findByText("Open Studio to review the created workflow.")).toBeTruthy();
expect(await screen.findByRole("button", { name: "Open Workflow Studio" })).toBeTruthy();
});

it("opens Workflow Studio only when structured identifiers are returned", async () => {
(authFetch as jest.Mock).mockResolvedValueOnce(
createSseResponse([
Expand Down
Loading
Loading