@springbrand/message-panel 0.1.3-alpha.3 → 0.1.3-alpha.30

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.
Files changed (39) hide show
  1. package/cloud-os/README.md +6 -6
  2. package/cloud-os/assets/followup-arrow.svg +3 -0
  3. package/cloud-os/assets/loading-corner.svg +3 -0
  4. package/cloud-os/assets/loading-mark.svg +16 -0
  5. package/cloud-os/assets/loading-spark.svg +3 -0
  6. package/cloud-os/assets/model-selected.svg +5 -0
  7. package/cloud-os/assets/thinking-spark.svg +9 -0
  8. package/cloud-os/capability-chip.tsx +35 -0
  9. package/cloud-os/chat/activity-indicator.tsx +2 -2
  10. package/cloud-os/chat/cloud-os-chat-messages.tsx +374 -63
  11. package/cloud-os/chat/image-generation.tsx +76 -0
  12. package/cloud-os/chat/loading-state.tsx +25 -0
  13. package/cloud-os/chat/markdown-message.tsx +144 -41
  14. package/cloud-os/chat/rich-blocks.tsx +548 -246
  15. package/cloud-os/chat/tool-presentation.ts +35 -16
  16. package/cloud-os/chat/tool-rows.tsx +330 -85
  17. package/cloud-os/chat/transcript-model.ts +428 -56
  18. package/cloud-os/composer/cloud-os-chat-input.tsx +228 -142
  19. package/cloud-os/composer/cloud-os-model-select.tsx +105 -0
  20. package/cloud-os/file-view.tsx +441 -0
  21. package/cloud-os/index.ts +35 -3
  22. package/cloud-os/layout/cloud-os-workspace-split.tsx +58 -15
  23. package/cloud-os/primitives/workshop-controls.tsx +2 -2
  24. package/cloud-os/styles/cloud-os.css +100 -20
  25. package/demo/camel-chat-showcase.tsx +21 -13
  26. package/demo/chat-scenarios.ts +223 -43
  27. package/demo/cloud-os-chat-showcase.tsx +47 -14
  28. package/demo/fixtures.ts +4 -5
  29. package/demo/message-panel-gallery.tsx +16 -2
  30. package/package.json +3 -4
  31. package/src/camel/camel-chat-messages.tsx +78 -78
  32. package/src/camel/camel-tool-presentation.tsx +19 -15
  33. package/src/camel/camel-turn.ts +1 -17
  34. package/src/composer/composer-attachments.tsx +1 -1
  35. package/src/composer/composer-trigger-popover.tsx +1 -1
  36. package/src/composer/composer.tsx +2 -2
  37. package/src/contracts.ts +0 -2
  38. package/src/message-panel.tsx +1 -24
  39. package/src/parts/index.tsx +103 -64
@@ -51,6 +51,8 @@
51
51
  --color-kumo-success: oklch(72.3% 0.219 149.579);
52
52
  --color-kumo-success-tint: oklch(92% 0.08 150);
53
53
 
54
+ --color-operation-base: #ffffff;
55
+
54
56
  --color-kumo-tip-shadow: #14111014;
55
57
  --color-kumo-tip-stroke: transparent;
56
58
 
@@ -73,6 +75,10 @@
73
75
  --color-cos-status-live: oklch(72.3% 0.219 149.579);
74
76
  --color-cos-status-draft: oklch(68.1% 0.162 75.834);
75
77
  --color-cos-status-building: oklch(62.3% 0.214 259.815);
78
+ --text-color-cos-work-description: rgba(0, 0, 0, 0.5);
79
+ --text-color-cos-work-description-subtle: rgba(0, 0, 0, 0.7);
80
+ --text-color-cos-tool-group: rgba(0, 0, 0, 0.5);
81
+ --text-color-cos-output: #000000b3;
76
82
  }
77
83
 
78
84
  /* ── 作用域根:通用 token 只在 cloud-os 子树内生效 ─────────────────────────── */
@@ -152,6 +158,8 @@
152
158
  --color-kumo-success: oklch(52.7% 0.154 150.069);
153
159
  --color-kumo-success-tint: oklch(0.25 0.06 150.069);
154
160
 
161
+ --color-operation-base: var(--color-kumo-base);
162
+
155
163
  --color-kumo-tip-shadow: #00000066;
156
164
  --color-kumo-tip-stroke: oklch(0.34 0.022 285);
157
165
 
@@ -172,6 +180,10 @@
172
180
  --color-cos-status-live: oklch(79.2% 0.209 151.711);
173
181
  --color-cos-status-draft: oklch(82.8% 0.189 84.429);
174
182
  --color-cos-status-building: oklch(74% 0.16 250);
183
+ --text-color-cos-work-description: var(--text-color-kumo-subtle);
184
+ --text-color-cos-work-description-subtle: var(--text-color-kumo-subtle);
185
+ --text-color-cos-tool-group: var(--text-color-kumo-inactive);
186
+ --text-color-cos-output: var(--text-color-kumo-subtle);
175
187
 
176
188
  --color-selection-bg: color-mix(in srgb, var(--color-kumo-brand) 28%, transparent);
177
189
  --color-selection-text: oklch(0.97 0.006 285);
@@ -341,6 +353,54 @@
341
353
  display: none;
342
354
  }
343
355
 
356
+ .cos-model-select {
357
+ --cos-model-select-bg: #fff;
358
+ --cos-model-select-border: rgb(0 0 0 / 7%);
359
+ --cos-model-select-text: #000;
360
+ --cos-model-select-muted: rgb(0 0 0 / 50%);
361
+ --cos-model-select-disabled: rgb(0 0 0 / 30%);
362
+ --cos-model-select-highlight: rgb(0 0 0 / 3%);
363
+ border-color: var(--cos-model-select-border);
364
+ background: var(--cos-model-select-bg);
365
+ box-shadow: 0 8px 14px rgb(0 0 0 / 6%);
366
+ color: var(--cos-model-select-text);
367
+ font-family: "Inter Variable", Inter, sans-serif;
368
+ }
369
+
370
+ .cos-model-select__item[data-highlighted] {
371
+ background: var(--cos-model-select-highlight);
372
+ }
373
+
374
+ .cos-model-select__item[data-disabled] {
375
+ cursor: not-allowed;
376
+ opacity: 1;
377
+ }
378
+
379
+ .cos-model-select__name {
380
+ color: var(--cos-model-select-text);
381
+ }
382
+
383
+ .cos-model-select__name[data-muted] {
384
+ color: var(--cos-model-select-muted);
385
+ }
386
+
387
+ .cos-model-select__plan {
388
+ color: var(--cos-model-select-muted);
389
+ }
390
+
391
+ .cos-model-select__plan[data-disabled] {
392
+ color: var(--cos-model-select-disabled);
393
+ }
394
+
395
+ [data-cloud-os-portal][data-mode="dark"].cos-model-select {
396
+ --cos-model-select-bg: #343434;
397
+ --cos-model-select-border: rgb(255 255 255 / 10%);
398
+ --cos-model-select-text: #fff;
399
+ --cos-model-select-muted: rgb(255 255 255 / 50%);
400
+ --cos-model-select-disabled: rgb(255 255 255 / 30%);
401
+ --cos-model-select-highlight: rgb(255 255 255 / 7%);
402
+ }
403
+
344
404
  .cos-chat-panel {
345
405
  scrollbar-width: thin;
346
406
  scrollbar-color: var(--color-kumo-line) transparent;
@@ -356,23 +416,6 @@
356
416
  background: transparent;
357
417
  }
358
418
 
359
- /* ── 顶部推进条(isAgentActive)──────────────────────────────────────────── */
360
- @keyframes cos-thinking {
361
- 0% {
362
- left: -33%;
363
- }
364
- 50% {
365
- left: 100%;
366
- }
367
- 100% {
368
- left: -33%;
369
- }
370
- }
371
-
372
- .cos-progress-sweep {
373
- animation: cos-thinking 1.5s ease-in-out infinite;
374
- }
375
-
376
419
  /* ============================================================================
377
420
  * 以下整段来自 ChatInterface.module.css —— CSS Module 类名改成 cos- 全局前缀
378
421
  * ========================================================================== */
@@ -382,6 +425,9 @@
382
425
  line-height: 1.6;
383
426
  overflow-wrap: anywhere;
384
427
  }
428
+ .cos-user-markdown :where(p, li) {
429
+ white-space: pre-wrap;
430
+ }
385
431
  .cos-markdown p {
386
432
  margin: 0.4em 0;
387
433
  }
@@ -486,8 +532,12 @@
486
532
  font-weight: 600;
487
533
  }
488
534
  .cos-markdown img {
489
- max-width: 100%;
535
+ max-width: min(100%, 16rem);
536
+ max-height: 13rem;
537
+ width: auto;
490
538
  height: auto;
539
+ border-radius: 0.75rem;
540
+ object-fit: contain;
491
541
  }
492
542
 
493
543
  /* ── 输入框容器(container query 决定「添加资源」标签是否显示)─────────────── */
@@ -545,13 +595,43 @@
545
595
  }
546
596
 
547
597
  @media (prefers-reduced-motion: reduce) {
598
+ [data-sd-animate] {
599
+ animation: none !important;
600
+ }
601
+
548
602
  .cos-thinking-shimmer {
549
603
  animation: none;
550
604
  background: none;
551
605
  -webkit-text-fill-color: currentColor;
552
606
  }
553
- .cos-progress-sweep {
554
- animation: none;
607
+ }
608
+
609
+ /* ── 原位提问:逐题横向切换 ─────────────────────────────────────────────── */
610
+ .cos-ask-user-question[data-direction="forward"] {
611
+ animation: cos-ask-user-forward 180ms ease-out;
612
+ }
613
+
614
+ .cos-ask-user-question[data-direction="back"] {
615
+ animation: cos-ask-user-back 180ms ease-out;
616
+ }
617
+
618
+ @keyframes cos-ask-user-forward {
619
+ from {
620
+ opacity: 0;
621
+ transform: translateX(24px);
622
+ }
623
+ }
624
+
625
+ @keyframes cos-ask-user-back {
626
+ from {
627
+ opacity: 0;
628
+ transform: translateX(-24px);
629
+ }
630
+ }
631
+
632
+ @media (prefers-reduced-motion: reduce) {
633
+ .cos-ask-user-question {
634
+ animation: none !important;
555
635
  }
556
636
  }
557
637
 
@@ -15,11 +15,13 @@ import {
15
15
  import {
16
16
  commands as sharedCommands,
17
17
  defaultPrompt,
18
+ demoAskUserResponse,
18
19
  demoModels,
19
20
  finalToolNames,
20
21
  flowSteps,
21
22
  messagesForScenario,
22
23
  scenarios,
24
+ type DemoAskUserResponse,
23
25
  type DemoScenario,
24
26
  type DemoScenarioMeta,
25
27
  } from "./chat-scenarios";
@@ -42,7 +44,7 @@ export function CamelChatShowcase() {
42
44
  const [theme, setTheme] = useState<"light" | "dark">("dark");
43
45
  const [scenario, setScenario] = useState<DemoScenario>(initialScenario);
44
46
  const [prompt, setPrompt] = useState(defaultPrompt);
45
- const [humanAnswer, setHumanAnswer] = useState("管理层");
47
+ const [humanAnswer, setHumanAnswer] = useState(demoAskUserResponse);
46
48
  const [model, setModel] = useState<string>(demoModels[0].value);
47
49
  const [value, setValue] = useState("");
48
50
  const [attachments, setAttachments] =
@@ -70,7 +72,8 @@ export function CamelChatShowcase() {
70
72
  : {};
71
73
  return (
72
74
  record.type === "tool-write_file" ||
73
- record.type === "tool-cancel_schedule"
75
+ record.type === "tool-cancel_schedule" ||
76
+ record.type === "tool-schedule"
74
77
  ) &&
75
78
  record.state === "approval-requested"
76
79
  ? { id: "camel-demo-approval", approved: approval }
@@ -254,6 +257,14 @@ export function CamelChatShowcase() {
254
257
  }}
255
258
  onApprove={(_approvalId, decision) => {
256
259
  const approved = decision !== "deny";
260
+ if (scenario === "schedule_create") {
261
+ setApproval(approved);
262
+ selectScenario(approved ? "schedule" : "submitted");
263
+ setLastEvent(approved
264
+ ? "Scheduled task created"
265
+ : "Schedule creation cancelled");
266
+ return;
267
+ }
257
268
  if (scenario === "schedule_cancel") {
258
269
  setApproval(approved);
259
270
  setLastEvent(
@@ -274,20 +285,17 @@ export function CamelChatShowcase() {
274
285
  onAnswer={(payload) => {
275
286
  const data =
276
287
  payload && typeof payload === "object"
277
- ? (payload as {
278
- selections?: string[][];
279
- text?: string;
280
- })
281
- : {};
282
- const answer = [
283
- ...(data.selections?.flat() ?? []),
284
- data.text?.trim() ?? "",
285
- ]
288
+ ? (payload as DemoAskUserResponse)
289
+ : { answers: [] };
290
+ const answer = data.answers.flatMap((item) => [
291
+ ...item.selections,
292
+ item.text?.trim() ?? "",
293
+ ])
286
294
  .filter(Boolean)
287
295
  .join("、");
288
- if (answer) setHumanAnswer(answer);
296
+ if (data.answers.length > 0) setHumanAnswer(data);
289
297
  selectScenario("planning");
290
- setLastEvent(`已回答并继续:${answer || humanAnswer}`);
298
+ setLastEvent(`已回答并继续:${answer}`);
291
299
  }}
292
300
  getToolApproval={approvalView}
293
301
  />
@@ -18,6 +18,7 @@ export interface ComposerCommandLike {
18
18
  }
19
19
  const demoCompletedAt = Date.now() - 1_000;
20
20
  const demoTurnStartedAt = demoCompletedAt - 18_000;
21
+ const demoTurnId = "camel-demo-research-turn";
21
22
  export const defaultPrompt =
22
23
  "请调研 2026 年 AI Agent 产品的交互趋势,并给出可落地的消息 UI 建议。";
23
24
  export const demoModels = [
@@ -37,6 +38,9 @@ export type DemoScenario =
37
38
  | "temporary_agent"
38
39
  | "agent_tools"
39
40
  | "background_agent"
41
+ | "action_loading"
42
+ | "action_files"
43
+ | "schedule_create"
40
44
  | "schedule"
41
45
  | "schedule_cancel"
42
46
  | "approval"
@@ -118,9 +122,27 @@ export const scenarios: Record<DemoScenario, DemoScenarioMeta> = {
118
122
  status: "ready",
119
123
  flowStep: 3,
120
124
  },
125
+ action_loading: {
126
+ label: "Action product loading",
127
+ description: "Preview loading UI for every Action product type.",
128
+ status: "streaming",
129
+ flowStep: 2,
130
+ },
131
+ action_files: {
132
+ label: "Action file results",
133
+ description: "Preview image, audio, video, document, spreadsheet, archive, and unknown files.",
134
+ status: "ready",
135
+ flowStep: 3,
136
+ },
137
+ schedule_create: {
138
+ label: "Create scheduled task",
139
+ description: "Review the task, cadence, and timezone before creating the schedule.",
140
+ status: "ready",
141
+ flowStep: 2,
142
+ },
121
143
  schedule: {
122
- label: "定时任务已创建",
123
- description: "schedule 执行成功后展示任务名称、触发规则、时区和已安排状态。",
144
+ label: "Scheduled task created",
145
+ description: "A completed schedule shows its task, cadence, timezone, and final status.",
124
146
  status: "ready",
125
147
  flowStep: 3,
126
148
  },
@@ -239,42 +261,84 @@ function userMessage(prompt: string): UIMessage {
239
261
  } as UIMessage;
240
262
  }
241
263
 
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
- },
264
+ function actionUserMessage(prompt: string): UIMessage {
265
+ const message = userMessage(prompt);
266
+ return { ...message, parts: message.parts.filter((part) => part.type === "text") };
267
+ }
268
+
269
+ export interface DemoAskUserResponse {
270
+ answers: Array<{ selections: string[]; text?: string }>;
271
+ }
272
+
273
+ export const demoAskUserResponse: DemoAskUserResponse = {
274
+ answers: [
275
+ { selections: ["管理层"], text: "" },
276
+ { selections: ["报告"], text: "" },
257
277
  ],
258
- } as unknown as UIMessage;
278
+ };
259
279
 
260
- function humanReplyMessage(answer: string): UIMessage {
280
+ const askUserQuestions = [
281
+ {
282
+ question: "这份建议优先面向哪个读者?",
283
+ options: ["产品团队", "管理层", "设计团队"],
284
+ },
285
+ {
286
+ question: "需要哪些交付物?",
287
+ options: ["报告", "演示文稿", "执行清单"],
288
+ multiSelect: true,
289
+ allowCustom: true,
290
+ },
291
+ ];
292
+
293
+ function askUserRequest(response?: DemoAskUserResponse): UIMessage {
261
294
  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;
295
+ id: "camel-demo-ask-user",
296
+ role: "assistant",
297
+ parts: [
298
+ {
299
+ type: "tool-ask_user",
300
+ toolCallId: "camel-demo-ask-user-1",
301
+ state: response ? "output-available" : "input-available",
302
+ input: { questions: askUserQuestions },
303
+ ...(response
304
+ ? {
305
+ output: {
306
+ answers: response.answers.map((answer, index) => ({
307
+ question: askUserQuestions[index]!.question,
308
+ selections: answer.selections,
309
+ text: answer.text?.trim() ?? "",
310
+ })),
311
+ },
312
+ }
313
+ : {}),
314
+ },
315
+ ],
316
+ } as unknown as UIMessage;
271
317
  }
272
318
 
273
319
  const planningWork = {
274
320
  id: "camel-demo-planning-work",
275
321
  role: "assistant",
276
- metadata: { turnStartedAt: demoTurnStartedAt + 3_000 },
322
+ metadata: {
323
+ turnStartedAt: demoTurnStartedAt + 3_000,
324
+ turnDurationMs: 18_000,
325
+ },
277
326
  parts: [
327
+ {
328
+ type: "reasoning",
329
+ text: `先确认受众和交付范围,再验证产品案例。
330
+
331
+ 用户选择的读者是管理层,交付物是报告。因此内容不能只罗列界面趋势,还需要把每项趋势连接到业务价值、落地成本和潜在风险,帮助管理层判断优先级。
332
+
333
+ 接下来会分三步推进:
334
+
335
+ 1. 检索 2025–2026 年主流 AI Agent 产品,优先采集官方发布说明、产品文档和可验证的真实界面。
336
+ 2. 对比任务规划、工具调用、人工确认、后台执行和结果交付等关键交互,区分普遍模式与单个产品的特色设计。
337
+ 3. 把证据整理成可执行建议,并为每条建议标注适用场景、实现复杂度和预期收益。
338
+
339
+ 第一轮检索先覆盖通用 Agent、开发者 Agent 与企业自动化产品。如果不同来源对同一趋势的描述冲突,会保留分歧并继续寻找一手证据,而不是直接下结论。`,
340
+ state: "done",
341
+ },
278
342
  {
279
343
  type: "tool-update_plan",
280
344
  state: "output-available",
@@ -459,6 +523,115 @@ const backgroundResult = {
459
523
  ],
460
524
  } as unknown as UIMessage;
461
525
 
526
+ const actionProductHints = [
527
+ { type: "image", mediaType: "image/png" },
528
+ { type: "audio", mediaType: "audio/mpeg" },
529
+ { type: "video", mediaType: "video/mp4" },
530
+ { type: "document", mediaType: "application/pdf" },
531
+ { type: "archive", mediaType: "application/zip" },
532
+ { type: "data", mediaType: "application/json" },
533
+ { type: "other" },
534
+ ] as const;
535
+
536
+ function demoExecutionId(index: number) {
537
+ return `00000000-0000-4000-8000-${String(index + 1).padStart(12, "0")}`;
538
+ }
539
+
540
+ const actionProductLoading = {
541
+ id: "camel-demo-action-product-loading",
542
+ role: "assistant",
543
+ parts: actionProductHints.map((produces, index) => ({
544
+ type: "dynamic-tool",
545
+ toolCallId: `camel-demo-action-loading-${index}`,
546
+ toolName: `action_generate_${produces.type}`,
547
+ state: "output-available",
548
+ input: {},
549
+ output: {
550
+ status: "running",
551
+ executionId: demoExecutionId(index),
552
+ presentation: {
553
+ type: "action-execution",
554
+ executionId: demoExecutionId(index),
555
+ state: "running",
556
+ produces,
557
+ content: [],
558
+ },
559
+ },
560
+ })),
561
+ } as unknown as UIMessage;
562
+
563
+ const actionFileFixtures = [
564
+ {
565
+ name: "image",
566
+ mediaType: "image/png",
567
+ url: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1024&q=80",
568
+ },
569
+ { name: "audio", mediaType: "audio/mpeg", url: "https://files.example/action-demo/audio.mp3" },
570
+ { name: "video", mediaType: "video/mp4", url: "https://files.example/action-demo/video.mp4" },
571
+ { name: "pdf", mediaType: "application/pdf", url: "https://files.example/action-demo/report.pdf" },
572
+ { name: "markdown", mediaType: "text/markdown", url: "https://files.example/action-demo/report.md" },
573
+ {
574
+ name: "spreadsheet",
575
+ mediaType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
576
+ url: "https://files.example/action-demo/data.xlsx",
577
+ },
578
+ { name: "archive", mediaType: "application/zip", url: "https://files.example/action-demo/files.zip" },
579
+ {
580
+ name: "unknown",
581
+ mediaType: "application/octet-stream",
582
+ url: "https://files.example/action-demo/result.bin",
583
+ },
584
+ ] as const;
585
+
586
+ const actionFileResults = {
587
+ id: "camel-demo-action-file-results",
588
+ role: "assistant",
589
+ parts: [
590
+ ...actionFileFixtures.map((file, index) => ({
591
+ type: "dynamic-tool",
592
+ toolCallId: `camel-demo-action-file-${index}`,
593
+ toolName: `action_generate_${file.name}`,
594
+ state: "output-available",
595
+ input: {},
596
+ output: {
597
+ status: "succeeded",
598
+ executionId: demoExecutionId(index + actionProductHints.length),
599
+ result: { kind: "file", content_type: file.mediaType, url: file.url },
600
+ presentation: {
601
+ type: "action-execution",
602
+ executionId: demoExecutionId(index + actionProductHints.length),
603
+ state: "ready",
604
+ content: [{ type: "file", url: file.url, mediaType: file.mediaType }],
605
+ },
606
+ },
607
+ })),
608
+ {
609
+ type: "text",
610
+ text: "All Action file result types are rendered above from structured output.",
611
+ },
612
+ ],
613
+ } as unknown as UIMessage;
614
+
615
+ export const demoScheduleInput = {
616
+ label: "Weekly competitor report",
617
+ prompt: "Generate and send this week's competitor report.",
618
+ trigger: { kind: "cron", cron: "0 9 * * 1" },
619
+ timezone: "Asia/Shanghai",
620
+ };
621
+
622
+ const scheduleRequest = {
623
+ id: "camel-demo-schedule-request",
624
+ role: "assistant",
625
+ parts: [
626
+ {
627
+ type: "tool-schedule",
628
+ toolCallId: "camel-demo-schedule-request-1",
629
+ state: "approval-requested",
630
+ input: demoScheduleInput,
631
+ },
632
+ ],
633
+ } as unknown as UIMessage;
634
+
462
635
  const scheduledTask = {
463
636
  id: "camel-demo-schedule",
464
637
  role: "assistant",
@@ -467,12 +640,7 @@ const scheduledTask = {
467
640
  type: "tool-schedule",
468
641
  toolCallId: "camel-demo-schedule-1",
469
642
  state: "output-available",
470
- input: {
471
- label: "每周发送竞品报告",
472
- prompt: "生成并发送本周竞品报告。",
473
- trigger: { kind: "cron", cron: "0 9 * * 1" },
474
- timezone: "Asia/Shanghai",
475
- },
643
+ input: demoScheduleInput,
476
644
  output: { scheduled: true, id: "schedule-1" },
477
645
  },
478
646
  ],
@@ -577,7 +745,7 @@ const commandMessages = [
577
745
  const runningWork = {
578
746
  id: "camel-demo-running-work",
579
747
  role: "assistant",
580
- metadata: { turnStartedAt: demoTurnStartedAt },
748
+ metadata: { turnId: demoTurnId, turnStartedAt: demoTurnStartedAt },
581
749
  parts: [
582
750
  {
583
751
  type: "reasoning",
@@ -604,7 +772,7 @@ const runningWork = {
604
772
  const completedResearch = {
605
773
  id: "camel-demo-completed-research",
606
774
  role: "assistant",
607
- metadata: { turnStartedAt: demoTurnStartedAt },
775
+ metadata: { turnId: demoTurnId, turnStartedAt: demoTurnStartedAt },
608
776
  parts: [
609
777
  {
610
778
  type: "reasoning",
@@ -639,6 +807,7 @@ const completedResearch = {
639
807
  const completedSynthesis = {
640
808
  id: "camel-demo-completed-synthesis",
641
809
  role: "assistant",
810
+ metadata: { turnId: demoTurnId },
642
811
  parts: [
643
812
  {
644
813
  type: "tool-update_plan",
@@ -670,6 +839,7 @@ const completedSynthesis = {
670
839
  const approvalRequest = {
671
840
  id: "camel-demo-approval-request",
672
841
  role: "assistant",
842
+ metadata: { turnId: demoTurnId },
673
843
  parts: [
674
844
  {
675
845
  type: "tool-write_file",
@@ -685,6 +855,7 @@ const approvalRequest = {
685
855
  const collectedAnswer = {
686
856
  id: "camel-demo-collected-answer",
687
857
  role: "assistant",
858
+ metadata: { turnId: demoTurnId },
688
859
  parts: [
689
860
  {
690
861
  type: "text",
@@ -701,6 +872,7 @@ const finalAnswer = {
701
872
  id: "camel-demo-final-answer",
702
873
  role: "assistant",
703
874
  metadata: {
875
+ turnId: demoTurnId,
704
876
  createdAt: demoCompletedAt,
705
877
  completedAt: demoCompletedAt,
706
878
  turnDurationMs: 18_000,
@@ -769,6 +941,7 @@ const interruptedByUser = {
769
941
  id: "camel-demo-interrupted",
770
942
  role: "assistant",
771
943
  metadata: {
944
+ turnId: demoTurnId,
772
945
  createdAt: demoCompletedAt,
773
946
  completedAt: demoCompletedAt + 2_000,
774
947
  turnDurationMs: 2_000,
@@ -798,7 +971,7 @@ const compactSummary = {
798
971
  const failedWork = {
799
972
  id: "camel-demo-failed-work",
800
973
  role: "assistant",
801
- metadata: { turnStartedAt: demoTurnStartedAt },
974
+ metadata: { turnId: demoTurnId, turnStartedAt: demoTurnStartedAt },
802
975
  parts: [
803
976
  ...completedResearch.parts,
804
977
  {
@@ -827,7 +1000,7 @@ const failedWork = {
827
1000
  export function messagesForScenario(
828
1001
  scenario: DemoScenario,
829
1002
  prompt: string,
830
- humanAnswer: string,
1003
+ humanAnswer: DemoAskUserResponse,
831
1004
  approval: boolean | undefined,
832
1005
  ): UIMessage[] {
833
1006
  if (scenario === "empty") return [];
@@ -835,15 +1008,13 @@ export function messagesForScenario(
835
1008
  return [noticeMessage(scenario), ...commandMessages];
836
1009
  }
837
1010
  const conversation = [noticeMessage(scenario), userMessage(prompt)];
838
- const answer = humanAnswer || "管理层";
839
1011
  const decidedConversation = [
840
1012
  ...conversation,
841
- askUserRequest,
842
- humanReplyMessage(answer),
1013
+ askUserRequest(humanAnswer),
843
1014
  ];
844
1015
  if (scenario === "hydrating") return conversation;
845
1016
  if (scenario === "submitted") return conversation;
846
- if (scenario === "ask") return [...conversation, askUserRequest];
1017
+ if (scenario === "ask") return [...conversation, askUserRequest()];
847
1018
  if (scenario === "planning") {
848
1019
  return [...decidedConversation, planningWork];
849
1020
  }
@@ -860,6 +1031,15 @@ export function messagesForScenario(
860
1031
  if (scenario === "background_agent") {
861
1032
  return [...conversation, backgroundDispatch, backgroundResult];
862
1033
  }
1034
+ if (scenario === "action_loading") {
1035
+ return [noticeMessage(scenario), actionUserMessage(prompt), actionProductLoading];
1036
+ }
1037
+ if (scenario === "action_files") {
1038
+ return [noticeMessage(scenario), actionUserMessage(prompt), actionFileResults];
1039
+ }
1040
+ if (scenario === "schedule_create") {
1041
+ return [...conversation, scheduleRequest];
1042
+ }
863
1043
  if (scenario === "schedule") {
864
1044
  return [...conversation, scheduledTask];
865
1045
  }