@springbrand/message-panel 0.1.3-alpha.0 → 0.1.3-alpha.2

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.
@@ -0,0 +1,926 @@
1
+ import type { UIMessage } from "ai";
2
+
3
+ /**
4
+ * 演示场景的唯一事实源。camel 与 cloud-os 两套展示壳共用这一份 —— 需求是
5
+ * 「cloud-os demo 的状态和 camel-chat-showcase 一致」,所以状态表只能有一份,
6
+ * 不能各拷一份等着漂移。
7
+ */
8
+
9
+ /** 和 MessagePanelStatus 同构。刻意不 import ../src —— cloud-os 展示壳要能
10
+ * 在完全不碰 src 的前提下用这份场景表。 */
11
+ export type DemoStatus = "ready" | "submitted" | "streaming" | "error";
12
+
13
+ export interface ComposerCommandLike {
14
+ name: string;
15
+ description: string;
16
+ prompt: string;
17
+ icon: string;
18
+ }
19
+ const demoCompletedAt = Date.now() - 1_000;
20
+ const demoTurnStartedAt = demoCompletedAt - 18_000;
21
+ export const defaultPrompt =
22
+ "请调研 2026 年 AI Agent 产品的交互趋势,并给出可落地的消息 UI 建议。";
23
+ export const demoModels = [
24
+ { value: "claude-sonnet-4.6", label: "Claude Sonnet 4.6", hint: "默认 · 均衡" },
25
+ { value: "gpt-5.2", label: "GPT-5.2", hint: "OpenAI" },
26
+ ] as const;
27
+ export const finalToolNames = ["create_agent", "publish_extension"] as const;
28
+
29
+ export type DemoScenario =
30
+ | "empty"
31
+ | "hydrating"
32
+ | "submitted"
33
+ | "ask"
34
+ | "planning"
35
+ | "working"
36
+ | "orchestration"
37
+ | "temporary_agent"
38
+ | "agent_tools"
39
+ | "background_agent"
40
+ | "schedule"
41
+ | "schedule_cancel"
42
+ | "approval"
43
+ | "recovering"
44
+ | "interrupted"
45
+ | "collected"
46
+ | "complete"
47
+ | "followup"
48
+ | "final_artifacts"
49
+ | "commands"
50
+ | "compact"
51
+ | "error";
52
+
53
+ export interface DemoScenarioMeta {
54
+ label: string;
55
+ description: string;
56
+ status: DemoStatus;
57
+ flowStep: number;
58
+ }
59
+
60
+ export const scenarios: Record<DemoScenario, DemoScenarioMeta> = {
61
+ empty: {
62
+ label: "空会话",
63
+ description: "没有历史消息时,只显示欢迎语和可用的输入框。",
64
+ status: "ready",
65
+ flowStep: -1,
66
+ },
67
+ hydrating: {
68
+ label: "加载历史会话",
69
+ description: "会话恢复期间覆盖消息区,避免旧消息或空会话短暂闪现。",
70
+ status: "ready",
71
+ flowStep: -1,
72
+ },
73
+ submitted: {
74
+ label: "已提交",
75
+ description: "用户消息已经进入会话,助手尚未产出内容,显示等待响应。",
76
+ status: "submitted",
77
+ flowStep: 0,
78
+ },
79
+ ask: {
80
+ label: "等待用户回答",
81
+ description: "Agent 在缺少关键决策时原位提问;选择并提交后继续同一任务。",
82
+ status: "ready",
83
+ flowStep: 1,
84
+ },
85
+ planning: {
86
+ label: "计划更新中",
87
+ description: "计划使用完整快照覆盖更新,只展示最新版本和当前执行步骤。",
88
+ status: "streaming",
89
+ flowStep: 2,
90
+ },
91
+ working: {
92
+ label: "工具执行中",
93
+ description: "思考与工具按产生顺序展示;完成项沉淀结果,当前项保留运行态。",
94
+ status: "streaming",
95
+ flowStep: 2,
96
+ },
97
+ orchestration: {
98
+ label: "并行与子 Agent",
99
+ description: "并行工具和子 Agent 在同一工作流里展示部分完成与当前运行状态。",
100
+ status: "streaming",
101
+ flowStep: 2,
102
+ },
103
+ temporary_agent: {
104
+ label: "临时 Agent 调用",
105
+ description: "真实 run_temporary_agent 调用展示已完成与正在运行的临时 Agent。",
106
+ status: "streaming",
107
+ flowStep: 2,
108
+ },
109
+ agent_tools: {
110
+ label: "Fanout / Extract",
111
+ description: "机制型子 Agent 以真实 fanout、extract 工具调用展示结构化执行进度。",
112
+ status: "streaming",
113
+ flowStep: 2,
114
+ },
115
+ background_agent: {
116
+ label: "后台子 Agent 回注",
117
+ description: "后台子 Agent 先返回 runId,完成后再把结果作为通知注入当前会话。",
118
+ status: "ready",
119
+ flowStep: 3,
120
+ },
121
+ schedule: {
122
+ label: "定时任务已创建",
123
+ description: "schedule 执行成功后展示任务名称、触发规则、时区和已安排状态。",
124
+ status: "ready",
125
+ flowStep: 3,
126
+ },
127
+ schedule_cancel: {
128
+ label: "取消定时授权",
129
+ description: "不可逆的 cancel_schedule 原位等待授权,并展示允许或拒绝后的结果。",
130
+ status: "ready",
131
+ flowStep: 2,
132
+ },
133
+ approval: {
134
+ label: "等待工具授权",
135
+ description: "高风险工具停在调用位置等待允许或拒绝,不折叠也不丢上下文。",
136
+ status: "ready",
137
+ flowStep: 2,
138
+ },
139
+ recovering: {
140
+ label: "恢复执行",
141
+ description: "连接恢复时保留已有过程,并明确标记正在重新接续当前步骤。",
142
+ status: "streaming",
143
+ flowStep: 2,
144
+ },
145
+ interrupted: {
146
+ label: "已停止",
147
+ description: "用户停止后保留已经完成的工作,同时在原回合后显示停止状态。",
148
+ status: "ready",
149
+ flowStep: 2,
150
+ },
151
+ collected: {
152
+ label: "工具结果已收齐",
153
+ description: "所有工具已经返回,过程保持展开,助手开始流式组织最终回答。",
154
+ status: "streaming",
155
+ flowStep: 3,
156
+ },
157
+ complete: {
158
+ label: "完成并收起",
159
+ description:
160
+ "工具结果收齐并完成回答后,思考和工具统一收进 show work,最终答案独立保留。",
161
+ status: "ready",
162
+ flowStep: 5,
163
+ },
164
+ followup: {
165
+ label: "完成与 Follow-up",
166
+ description: "最终答案之后提供可忽略的后续方向;点击后直接进入下一轮用户请求。",
167
+ status: "ready",
168
+ flowStep: 6,
169
+ },
170
+ final_artifacts: {
171
+ label: "Agent 与扩展产物",
172
+ description: "create_agent 与 publish_extension 的完成结果保留在最终答案区域。",
173
+ status: "ready",
174
+ flowStep: 5,
175
+ },
176
+ commands: {
177
+ label: "命令消息",
178
+ description: "Slash command 与本地命令输出使用独立的轻量消息形态。",
179
+ status: "ready",
180
+ flowStep: 0,
181
+ },
182
+ compact: {
183
+ label: "上下文已压缩",
184
+ description: "较早的聊天内容收进 Compacted context,当前对话继续正常展示。",
185
+ status: "ready",
186
+ flowStep: 5,
187
+ },
188
+ error: {
189
+ label: "失败与重试",
190
+ description: "失败工具保留在工作过程里,错误说明与 Retry 留在消息末尾。",
191
+ status: "error",
192
+ flowStep: 2,
193
+ },
194
+ };
195
+
196
+ export const flowSteps = [
197
+ { label: "收到问题", scenario: "submitted" },
198
+ { label: "向人提问", scenario: "ask" },
199
+ { label: "计划与工具", scenario: "planning" },
200
+ { label: "结果收齐", scenario: "collected" },
201
+ { label: "收进 show work", scenario: "complete" },
202
+ { label: "最终答案", scenario: "complete" },
203
+ { label: "Follow-up", scenario: "followup" },
204
+ ] as const;
205
+
206
+ function noticeMessage(scenario: DemoScenario): UIMessage {
207
+ return {
208
+ id: `camel-demo-notice-${scenario}`,
209
+ role: "assistant",
210
+ parts: [
211
+ {
212
+ type: "data-notice",
213
+ data: {
214
+ text: `验收说明:当前展示「${scenarios[scenario].label}」。${scenarios[scenario].description}`,
215
+ },
216
+ },
217
+ ],
218
+ } as unknown as UIMessage;
219
+ }
220
+
221
+ function userMessage(prompt: string): UIMessage {
222
+ return {
223
+ id: "camel-demo-user",
224
+ role: "user",
225
+ metadata: {
226
+ authorDisplayName: "You",
227
+ createdAt: demoTurnStartedAt,
228
+ messageSource: "Web",
229
+ },
230
+ parts: [
231
+ {
232
+ type: "file",
233
+ filename: "research-brief.pdf",
234
+ mediaType: "application/pdf",
235
+ url: "data:application/pdf;base64,",
236
+ },
237
+ { type: "text", text: prompt },
238
+ ],
239
+ } as UIMessage;
240
+ }
241
+
242
+ const askUserRequest = {
243
+ id: "camel-demo-ask-user",
244
+ role: "assistant",
245
+ parts: [
246
+ {
247
+ type: "tool-ask_user",
248
+ toolCallId: "camel-demo-ask-user-1",
249
+ state: "output-available",
250
+ input: {
251
+ question: "这份建议优先面向哪个读者?",
252
+ options: ["产品团队", "管理层", "设计团队"],
253
+ multiSelect: false,
254
+ },
255
+ output: { asked: true },
256
+ },
257
+ ],
258
+ } as unknown as UIMessage;
259
+
260
+ function humanReplyMessage(answer: string): UIMessage {
261
+ return {
262
+ id: "camel-demo-human-reply",
263
+ role: "user",
264
+ metadata: {
265
+ authorDisplayName: "You",
266
+ createdAt: demoTurnStartedAt + 2_000,
267
+ messageSource: "Web",
268
+ },
269
+ parts: [{ type: "text", text: answer }],
270
+ } as UIMessage;
271
+ }
272
+
273
+ const planningWork = {
274
+ id: "camel-demo-planning-work",
275
+ role: "assistant",
276
+ metadata: { turnStartedAt: demoTurnStartedAt + 3_000 },
277
+ parts: [
278
+ {
279
+ type: "tool-update_plan",
280
+ state: "output-available",
281
+ input: {
282
+ steps: [
283
+ { text: "确认读者与交付范围", status: "in_progress" },
284
+ { text: "检索并验证产品案例", status: "pending" },
285
+ { text: "整理可落地 UI 建议", status: "pending" },
286
+ ],
287
+ },
288
+ output: { ok: true, done: 0, total: 3 },
289
+ },
290
+ {
291
+ type: "tool-web_search",
292
+ state: "output-available",
293
+ input: { query: "AI Agent product interface patterns 2026", limit: 8 },
294
+ output: { results: 8, summary: "已完成第一轮产品案例检索" },
295
+ },
296
+ {
297
+ type: "tool-update_plan",
298
+ state: "input-available",
299
+ input: {
300
+ steps: [
301
+ { text: "确认读者与交付范围", status: "done" },
302
+ { text: "检索并验证产品案例", status: "in_progress" },
303
+ { text: "整理可落地 UI 建议", status: "pending" },
304
+ ],
305
+ },
306
+ },
307
+ ],
308
+ } as unknown as UIMessage;
309
+
310
+ const orchestrationWork = {
311
+ id: "camel-demo-orchestration-work",
312
+ role: "assistant",
313
+ metadata: { turnStartedAt: demoTurnStartedAt + 4_000 },
314
+ parts: [
315
+ {
316
+ type: "data-parallel",
317
+ data: {
318
+ label: "并行验证产品案例",
319
+ tools: [
320
+ {
321
+ icon: "web",
322
+ label: "检索 Agent 产品",
323
+ status: "done",
324
+ meta: "8 sources",
325
+ },
326
+ {
327
+ icon: "code",
328
+ label: "归纳交互模式",
329
+ status: "running",
330
+ },
331
+ {
332
+ icon: "file",
333
+ label: "核对内部研究",
334
+ status: "done",
335
+ meta: "4 notes",
336
+ },
337
+ ],
338
+ },
339
+ },
340
+ {
341
+ type: "data-subagents",
342
+ data: {
343
+ agents: [
344
+ {
345
+ name: "Product Researcher",
346
+ tint: "#5577cc",
347
+ summary: "已交叉验证 8 个产品案例。",
348
+ status: "done",
349
+ },
350
+ {
351
+ name: "UX Analyst",
352
+ tint: "#8f61c7",
353
+ summary: "",
354
+ status: "running",
355
+ },
356
+ ],
357
+ },
358
+ },
359
+ ],
360
+ } as unknown as UIMessage;
361
+
362
+ const temporaryAgentWork = {
363
+ id: "camel-demo-temporary-agents",
364
+ role: "assistant",
365
+ metadata: { turnStartedAt: demoTurnStartedAt + 4_000 },
366
+ parts: [
367
+ {
368
+ type: "tool-run_temporary_agent",
369
+ toolCallId: "camel-demo-temporary-agent-1",
370
+ state: "output-available",
371
+ input: {
372
+ subagentName: "Research Agent",
373
+ instructions: "交叉验证来源,只返回可引用的结论。",
374
+ task: "核对 8 个 Agent 产品案例。",
375
+ },
376
+ output: "完成 8 个来源的交叉验证。",
377
+ },
378
+ {
379
+ type: "tool-run_temporary_agent",
380
+ toolCallId: "camel-demo-temporary-agent-2",
381
+ state: "input-available",
382
+ input: {
383
+ subagentName: "UX Analyst",
384
+ instructions: "归纳可复用的交互模式。",
385
+ task: "分析已验证案例中的消息 UI 模式。",
386
+ },
387
+ },
388
+ ],
389
+ } as unknown as UIMessage;
390
+
391
+ const mechanismAgentWork = {
392
+ id: "camel-demo-mechanism-agents",
393
+ role: "assistant",
394
+ metadata: { turnStartedAt: demoTurnStartedAt + 4_000 },
395
+ parts: [
396
+ {
397
+ type: "tool-fanout",
398
+ toolCallId: "camel-demo-fanout-1",
399
+ state: "output-available",
400
+ input: {
401
+ items: ["claude.ai", "chatgpt.com", "perplexity.ai"],
402
+ instruction: "核对是否展示工具执行过程。",
403
+ },
404
+ output: {
405
+ results: [
406
+ { item: "claude.ai", finding: "展示折叠后的工具过程" },
407
+ { item: "chatgpt.com", finding: "展示工具状态与引用" },
408
+ { item: "perplexity.ai", finding: "展示来源检索过程" },
409
+ ],
410
+ },
411
+ },
412
+ {
413
+ type: "tool-extract",
414
+ toolCallId: "camel-demo-extract-1",
415
+ state: "input-available",
416
+ input: {
417
+ target: "/workspace/research/agent-ui-notes.md",
418
+ fields: ["pattern", "benefit", "risk"],
419
+ instruction: "抽取消息 UI 模式。",
420
+ },
421
+ },
422
+ ],
423
+ } as unknown as UIMessage;
424
+
425
+ const backgroundDispatch = {
426
+ id: "camel-demo-background-dispatch",
427
+ role: "assistant",
428
+ parts: [
429
+ {
430
+ type: "tool-dispatch_background",
431
+ toolCallId: "camel-demo-background-1",
432
+ state: "output-available",
433
+ input: {
434
+ agentType: "extract",
435
+ input: {
436
+ target: "/workspace/research/agent-ui-notes.md",
437
+ fields: ["pattern", "evidence"],
438
+ },
439
+ },
440
+ output: {
441
+ runId: "subagent-run-42",
442
+ status: "running",
443
+ agentType: "extract",
444
+ },
445
+ },
446
+ ],
447
+ } as unknown as UIMessage;
448
+
449
+ const backgroundResult = {
450
+ id: "camel-demo-background-result",
451
+ role: "assistant",
452
+ parts: [
453
+ {
454
+ type: "data-notice",
455
+ data: {
456
+ text: "后台 Extract Agent 已完成 · subagent-run-42 · 返回 6 条结构化记录。",
457
+ },
458
+ },
459
+ ],
460
+ } as unknown as UIMessage;
461
+
462
+ const scheduledTask = {
463
+ id: "camel-demo-schedule",
464
+ role: "assistant",
465
+ parts: [
466
+ {
467
+ type: "tool-schedule",
468
+ toolCallId: "camel-demo-schedule-1",
469
+ state: "output-available",
470
+ input: {
471
+ label: "每周发送竞品报告",
472
+ prompt: "生成并发送本周竞品报告。",
473
+ trigger: { kind: "cron", cron: "0 9 * * 1" },
474
+ timezone: "Asia/Shanghai",
475
+ },
476
+ output: { scheduled: true, id: "schedule-1" },
477
+ },
478
+ ],
479
+ } as unknown as UIMessage;
480
+
481
+ function scheduleCancellation(approved: boolean | undefined): UIMessage {
482
+ const settled = approved !== undefined;
483
+ return {
484
+ id: "camel-demo-schedule-cancel",
485
+ role: "assistant",
486
+ parts: [
487
+ {
488
+ type: "tool-cancel_schedule",
489
+ toolCallId: "camel-demo-schedule-cancel-1",
490
+ state: settled
491
+ ? approved
492
+ ? "output-available"
493
+ : "output-denied"
494
+ : "approval-requested",
495
+ input: { id: "schedule-1" },
496
+ ...(settled
497
+ ? {
498
+ output: approved
499
+ ? { cancelled: true, id: "schedule-1" }
500
+ : { cancelled: false, id: "schedule-1" },
501
+ }
502
+ : {}),
503
+ },
504
+ ...(settled
505
+ ? [
506
+ {
507
+ type: "text",
508
+ text: approved
509
+ ? "已取消「每周发送竞品报告」。"
510
+ : "已保留「每周发送竞品报告」。",
511
+ },
512
+ ]
513
+ : []),
514
+ ],
515
+ } as unknown as UIMessage;
516
+ }
517
+
518
+ const finalArtifacts = {
519
+ id: "camel-demo-final-artifacts",
520
+ role: "assistant",
521
+ metadata: { completedAt: demoCompletedAt },
522
+ parts: [
523
+ {
524
+ type: "tool-create_agent",
525
+ toolCallId: "camel-demo-create-agent-1",
526
+ state: "output-available",
527
+ input: {
528
+ name: "Competitive Researcher",
529
+ description: "每周生成竞品变化报告。",
530
+ },
531
+ output: { id: "competitive-researcher", ready: true },
532
+ },
533
+ {
534
+ type: "tool-publish_extension",
535
+ toolCallId: "camel-demo-publish-extension-1",
536
+ state: "output-available",
537
+ input: {
538
+ name: "competitor-diff",
539
+ version: "1.0.0",
540
+ description: "比较两期竞品数据。",
541
+ },
542
+ output: {
543
+ resourceId: "extension-competitor-diff",
544
+ name: "competitor-diff",
545
+ version: "1.0.0",
546
+ changed: true,
547
+ tools: ["competitor_diff"],
548
+ note: "下一条消息起可用。",
549
+ },
550
+ },
551
+ ],
552
+ } as unknown as UIMessage;
553
+
554
+ const commandMessages = [
555
+ {
556
+ id: "camel-demo-slash-command",
557
+ role: "user",
558
+ parts: [
559
+ {
560
+ type: "data-slash-command",
561
+ data: { command: "/compact" },
562
+ },
563
+ ],
564
+ },
565
+ {
566
+ id: "camel-demo-local-output",
567
+ role: "user",
568
+ parts: [
569
+ {
570
+ type: "data-local-command-output",
571
+ data: { output: "Compacted 24,318 tokens." },
572
+ },
573
+ ],
574
+ },
575
+ ] as unknown as UIMessage[];
576
+
577
+ const runningWork = {
578
+ id: "camel-demo-running-work",
579
+ role: "assistant",
580
+ metadata: { turnStartedAt: demoTurnStartedAt },
581
+ parts: [
582
+ {
583
+ type: "reasoning",
584
+ state: "streaming",
585
+ text: "先确认调研范围,再检索产品案例并读取已有研究记录。",
586
+ },
587
+ {
588
+ type: "tool-web_search",
589
+ state: "output-available",
590
+ input: { query: "AI Agent product interface patterns 2026", limit: 8 },
591
+ output: { results: 8, summary: "已完成第一轮产品案例检索" },
592
+ },
593
+ {
594
+ type: "tool-read_file",
595
+ state: "input-available",
596
+ input: {
597
+ path: "/workspace/research/agent-ui-notes.md",
598
+ project: "agent-landscape",
599
+ },
600
+ },
601
+ ],
602
+ } as unknown as UIMessage;
603
+
604
+ const completedResearch = {
605
+ id: "camel-demo-completed-research",
606
+ role: "assistant",
607
+ metadata: { turnStartedAt: demoTurnStartedAt },
608
+ parts: [
609
+ {
610
+ type: "reasoning",
611
+ state: "done",
612
+ text: "先确认调研范围,再检索产品案例并读取已有研究记录。",
613
+ },
614
+ {
615
+ type: "tool-web_search",
616
+ state: "output-available",
617
+ input: { query: "AI Agent product interface patterns 2026", limit: 8 },
618
+ output: { results: 8, summary: "已完成产品案例检索与交叉验证" },
619
+ },
620
+ {
621
+ type: "tool-read_file",
622
+ state: "output-available",
623
+ input: {
624
+ path: "/workspace/research/agent-ui-notes.md",
625
+ project: "agent-landscape",
626
+ },
627
+ output: [
628
+ "# Agent UI notes",
629
+ "",
630
+ "1. Long-running work needs visible progress.",
631
+ "2. Tool details should stay close to their call site.",
632
+ "3. Completed work should collapse without hiding the answer.",
633
+ "4. Approvals need an explicit pause and decision.",
634
+ ].join("\n"),
635
+ },
636
+ ],
637
+ } as unknown as UIMessage;
638
+
639
+ const completedSynthesis = {
640
+ id: "camel-demo-completed-synthesis",
641
+ role: "assistant",
642
+ parts: [
643
+ {
644
+ type: "tool-update_plan",
645
+ state: "output-available",
646
+ input: {
647
+ steps: [
648
+ { text: "确认调研范围", status: "done" },
649
+ { text: "检索与验证来源", status: "done" },
650
+ { text: "整理 UI 建议", status: "done" },
651
+ ],
652
+ },
653
+ output: { ok: true, done: 3, total: 3 },
654
+ },
655
+ {
656
+ type: "tool-write_file",
657
+ state: "output-available",
658
+ input: {
659
+ path: "reports/agent-ui-recommendations.md",
660
+ overwrite: false,
661
+ },
662
+ output: {
663
+ path: "reports/agent-ui-recommendations.md",
664
+ bytes: 4_218,
665
+ },
666
+ },
667
+ ],
668
+ } as unknown as UIMessage;
669
+
670
+ const approvalRequest = {
671
+ id: "camel-demo-approval-request",
672
+ role: "assistant",
673
+ parts: [
674
+ {
675
+ type: "tool-write_file",
676
+ state: "approval-requested",
677
+ input: {
678
+ path: "reports/agent-ui-recommendations.md",
679
+ overwrite: false,
680
+ },
681
+ },
682
+ ],
683
+ } as unknown as UIMessage;
684
+
685
+ const collectedAnswer = {
686
+ id: "camel-demo-collected-answer",
687
+ role: "assistant",
688
+ parts: [
689
+ {
690
+ type: "text",
691
+ text: [
692
+ "## 正在整理结论",
693
+ "",
694
+ "3 组工具结果已经收齐,正在把产品案例、研究记录和执行计划合并为最终建议…",
695
+ ].join("\n"),
696
+ },
697
+ ],
698
+ } as unknown as UIMessage;
699
+
700
+ const finalAnswer = {
701
+ id: "camel-demo-final-answer",
702
+ role: "assistant",
703
+ metadata: {
704
+ createdAt: demoCompletedAt,
705
+ completedAt: demoCompletedAt,
706
+ turnDurationMs: 18_000,
707
+ },
708
+ parts: [
709
+ {
710
+ type: "text",
711
+ text: [
712
+ "## AI Agent 消息 UI 建议",
713
+ "",
714
+ "调研结论是:真实聊天需要同时照顾**当前进展**和**完成后的可读性**。",
715
+ "",
716
+ "- 执行中原位展示思考、工具状态和授权节点,让用户知道系统正在做什么。",
717
+ "- 工具结果收齐后继续保持展开,直到最终回答稳定。",
718
+ "- 回合完成后把过程统一收进 `show work`,不让工具日志打断答案。",
719
+ "- 错误与 Retry 留在失败位置,避免丢失已完成步骤。",
720
+ "",
721
+ "| 验收项 | 完成态展示 |",
722
+ "| --- | --- |",
723
+ "| 工具过程 | 收进 show work |",
724
+ "| 最终回答 | 始终可读 |",
725
+ "| 高风险操作 | 原位等待授权 |",
726
+ ].join("\n"),
727
+ },
728
+ ],
729
+ } as unknown as UIMessage;
730
+
731
+ const completeTurn = {
732
+ ...finalAnswer,
733
+ id: "camel-demo-complete-turn",
734
+ parts: [
735
+ { type: "step-start" },
736
+ completedResearch.parts[0],
737
+ completedResearch.parts[1],
738
+ { type: "step-start" },
739
+ completedResearch.parts[2],
740
+ { type: "step-start" },
741
+ ...completedSynthesis.parts,
742
+ { type: "step-start" },
743
+ ...finalAnswer.parts,
744
+ ],
745
+ } as unknown as UIMessage;
746
+
747
+ const followupAnswer = {
748
+ ...finalAnswer,
749
+ id: "camel-demo-followup-answer",
750
+ parts: [
751
+ ...finalAnswer.parts,
752
+ {
753
+ type: "tool-suggest_followups",
754
+ toolCallId: "camel-demo-followups-1",
755
+ state: "output-available",
756
+ input: {
757
+ items: [
758
+ "把建议整理成实施优先级",
759
+ "补充移动端消息交互",
760
+ "生成一份设计验收清单",
761
+ ],
762
+ },
763
+ output: { noted: true, count: 3 },
764
+ },
765
+ ],
766
+ } as unknown as UIMessage;
767
+
768
+ const interruptedByUser = {
769
+ id: "camel-demo-interrupted",
770
+ role: "assistant",
771
+ metadata: {
772
+ createdAt: demoCompletedAt,
773
+ completedAt: demoCompletedAt + 2_000,
774
+ turnDurationMs: 2_000,
775
+ turnStatus: "aborted",
776
+ interruptedByUser: true,
777
+ },
778
+ parts: [],
779
+ } as unknown as UIMessage;
780
+
781
+ const compactSummary = {
782
+ id: "camel-demo-compact-summary",
783
+ role: "user",
784
+ metadata: { createdAt: demoTurnStartedAt - 60_000 },
785
+ parts: [
786
+ {
787
+ type: "data-compact-summary",
788
+ data: {
789
+ text: [
790
+ "此前已确认:目标是梳理 AI Agent 消息交互,并以管理层可快速理解的方式交付。",
791
+ "已经收集了竞品案例、工具执行记录和风险授权要求。",
792
+ ].join("\n\n"),
793
+ },
794
+ },
795
+ ],
796
+ } as unknown as UIMessage;
797
+
798
+ const failedWork = {
799
+ id: "camel-demo-failed-work",
800
+ role: "assistant",
801
+ metadata: { turnStartedAt: demoTurnStartedAt },
802
+ parts: [
803
+ ...completedResearch.parts,
804
+ {
805
+ type: "tool-exec_command",
806
+ state: "output-error",
807
+ input: {
808
+ command: "pnpm analyze agent-products.csv",
809
+ description: "product comparison",
810
+ },
811
+ output: {
812
+ exit_code: 1,
813
+ stdout: "Loaded 24 product records",
814
+ stderr: "数据源连接中断",
815
+ },
816
+ },
817
+ {
818
+ type: "data-error",
819
+ data: {
820
+ title: "调研暂时中断",
821
+ body: "已完成的检索结果仍然保留;重试后将从产品对比步骤继续。",
822
+ },
823
+ },
824
+ ],
825
+ } as unknown as UIMessage;
826
+
827
+ export function messagesForScenario(
828
+ scenario: DemoScenario,
829
+ prompt: string,
830
+ humanAnswer: string,
831
+ approval: boolean | undefined,
832
+ ): UIMessage[] {
833
+ if (scenario === "empty") return [];
834
+ if (scenario === "commands") {
835
+ return [noticeMessage(scenario), ...commandMessages];
836
+ }
837
+ const conversation = [noticeMessage(scenario), userMessage(prompt)];
838
+ const answer = humanAnswer || "管理层";
839
+ const decidedConversation = [
840
+ ...conversation,
841
+ askUserRequest,
842
+ humanReplyMessage(answer),
843
+ ];
844
+ if (scenario === "hydrating") return conversation;
845
+ if (scenario === "submitted") return conversation;
846
+ if (scenario === "ask") return [...conversation, askUserRequest];
847
+ if (scenario === "planning") {
848
+ return [...decidedConversation, planningWork];
849
+ }
850
+ if (scenario === "working") return [...conversation, runningWork];
851
+ if (scenario === "orchestration") {
852
+ return [...decidedConversation, planningWork, orchestrationWork];
853
+ }
854
+ if (scenario === "temporary_agent") {
855
+ return [...conversation, temporaryAgentWork];
856
+ }
857
+ if (scenario === "agent_tools") {
858
+ return [...conversation, mechanismAgentWork];
859
+ }
860
+ if (scenario === "background_agent") {
861
+ return [...conversation, backgroundDispatch, backgroundResult];
862
+ }
863
+ if (scenario === "schedule") {
864
+ return [...conversation, scheduledTask];
865
+ }
866
+ if (scenario === "schedule_cancel") {
867
+ return [...conversation, scheduleCancellation(approval)];
868
+ }
869
+ if (scenario === "approval") {
870
+ return [...conversation, completedResearch, approvalRequest];
871
+ }
872
+ if (scenario === "recovering") {
873
+ return [...conversation, completedResearch, runningWork];
874
+ }
875
+ if (scenario === "interrupted") {
876
+ return [...conversation, completedResearch, interruptedByUser];
877
+ }
878
+ if (scenario === "collected") {
879
+ return [
880
+ ...conversation,
881
+ completedResearch,
882
+ completedSynthesis,
883
+ collectedAnswer,
884
+ ];
885
+ }
886
+ if (scenario === "compact") {
887
+ return [noticeMessage(scenario), compactSummary, userMessage(prompt), finalAnswer];
888
+ }
889
+ if (scenario === "error") return [...conversation, failedWork];
890
+ if (scenario === "followup") {
891
+ return [
892
+ ...conversation,
893
+ completedResearch,
894
+ completedSynthesis,
895
+ followupAnswer,
896
+ ];
897
+ }
898
+ if (scenario === "final_artifacts") {
899
+ return [...conversation, finalArtifacts];
900
+ }
901
+ return [
902
+ ...conversation,
903
+ completeTurn,
904
+ ];
905
+ }
906
+
907
+ export const commands = [
908
+ {
909
+ name: "plan",
910
+ description: "先制定执行计划",
911
+ prompt: "请先制定计划:\n",
912
+ icon: "plan",
913
+ },
914
+ {
915
+ name: "research",
916
+ description: "开始深入调研",
917
+ prompt: "请深入调研:\n",
918
+ icon: "research",
919
+ },
920
+ {
921
+ name: "summarize",
922
+ description: "总结当前结果",
923
+ prompt: "请总结当前结果:\n",
924
+ icon: "file",
925
+ },
926
+ ] satisfies ComposerCommandLike[];