@seed-ship/mcp-ui-solid 4.0.6 → 4.1.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.
Files changed (56) hide show
  1. package/dist/components/AgentCard.cjs +122 -0
  2. package/dist/components/AgentCard.cjs.map +1 -0
  3. package/dist/components/AgentCard.d.ts +20 -0
  4. package/dist/components/AgentCard.d.ts.map +1 -0
  5. package/dist/components/AgentCard.js +122 -0
  6. package/dist/components/AgentCard.js.map +1 -0
  7. package/dist/components/AgentHandoff.cjs +49 -0
  8. package/dist/components/AgentHandoff.cjs.map +1 -0
  9. package/dist/components/AgentHandoff.d.ts +12 -0
  10. package/dist/components/AgentHandoff.d.ts.map +1 -0
  11. package/dist/components/AgentHandoff.js +49 -0
  12. package/dist/components/AgentHandoff.js.map +1 -0
  13. package/dist/components/BriefingDiff.cjs +165 -0
  14. package/dist/components/BriefingDiff.cjs.map +1 -0
  15. package/dist/components/BriefingDiff.d.ts +12 -0
  16. package/dist/components/BriefingDiff.d.ts.map +1 -0
  17. package/dist/components/BriefingDiff.js +165 -0
  18. package/dist/components/BriefingDiff.js.map +1 -0
  19. package/dist/components/ScratchpadPanel.cjs +618 -411
  20. package/dist/components/ScratchpadPanel.cjs.map +1 -1
  21. package/dist/components/ScratchpadPanel.d.ts.map +1 -1
  22. package/dist/components/ScratchpadPanel.js +619 -412
  23. package/dist/components/ScratchpadPanel.js.map +1 -1
  24. package/dist/components/SplitStepper.cjs +121 -0
  25. package/dist/components/SplitStepper.cjs.map +1 -0
  26. package/dist/components/SplitStepper.d.ts +12 -0
  27. package/dist/components/SplitStepper.d.ts.map +1 -0
  28. package/dist/components/SplitStepper.js +121 -0
  29. package/dist/components/SplitStepper.js.map +1 -0
  30. package/dist/components/index.d.ts +8 -0
  31. package/dist/components/index.d.ts.map +1 -1
  32. package/dist/components.cjs +9 -0
  33. package/dist/components.cjs.map +1 -1
  34. package/dist/components.d.cts +8 -0
  35. package/dist/components.d.ts +8 -0
  36. package/dist/components.js +9 -0
  37. package/dist/components.js.map +1 -1
  38. package/dist/index.cjs +9 -0
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +9 -1
  41. package/dist/index.d.ts +9 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +9 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/types/chat-bus.d.ts +81 -1
  46. package/dist/types/chat-bus.d.ts.map +1 -1
  47. package/package.json +1 -1
  48. package/src/components/AgentCard.tsx +109 -0
  49. package/src/components/AgentHandoff.tsx +64 -0
  50. package/src/components/BriefingDiff.tsx +93 -0
  51. package/src/components/ScratchpadPanel.tsx +131 -49
  52. package/src/components/SplitStepper.tsx +111 -0
  53. package/src/components/index.ts +13 -0
  54. package/src/index.ts +15 -0
  55. package/src/types/chat-bus.ts +70 -1
  56. package/tsconfig.tsbuildinfo +1 -1
@@ -6,13 +6,17 @@
6
6
  */
7
7
 
8
8
  import { Component, Show, For, Switch, Match, createSignal, createEffect, onCleanup } from 'solid-js'
9
- import type { ScratchpadState, ScratchpadSection, VerifiedTextContent, DataPreviewContent, MapSectionContent } from '../types/chat-bus'
9
+ import type { ScratchpadState, ScratchpadSection, VerifiedTextContent, DataPreviewContent, MapSectionContent, AgentCardContent, SplitStepperContent, AgentHandoffContent, BriefingDiffContent } from '../types/chat-bus'
10
10
  import type { FormFieldParams, ChartComponentParams } from '../types'
11
11
  import { FormFieldRenderer } from './FormFieldRenderer'
12
12
  import { VerifiedText } from './VerifiedText'
13
13
  import { DataPreviewSection } from './DataPreviewSection'
14
14
  import { MapRenderer } from './MapRenderer'
15
15
  import { ChartJSRenderer } from './ChartJSRenderer'
16
+ import { AgentCard, AgentStatusBadge } from './AgentCard'
17
+ import { SplitStepper } from './SplitStepper'
18
+ import { AgentHandoff } from './AgentHandoff'
19
+ import { BriefingDiff } from './BriefingDiff'
16
20
 
17
21
  export interface ScratchpadPanelProps {
18
22
  state: ScratchpadState
@@ -177,6 +181,14 @@ export const ScratchpadPanel: Component<ScratchpadPanelProps> = (props) => {
177
181
  <div class="flex items-center gap-2">
178
182
  <span class="text-base">&#128221;</span>
179
183
  <h3 class="text-sm font-semibold text-gray-900 dark:text-white">{props.state.title}</h3>
184
+ {/* AgentStatusBadge — auto-detected from agent_card sections (v4.1.0) */}
185
+ {(() => {
186
+ const agentSection = props.state.sections.find(s => s.type === 'agent_card')
187
+ if (!agentSection) return null
188
+ const ac = parseContent(agentSection.content) as AgentCardContent | null
189
+ if (!ac?.name) return null
190
+ return <AgentStatusBadge agentName={ac.name} status={ac.status || 'idle'} />
191
+ })()}
180
192
  <Show when={isCollapsible()}>
181
193
  <svg class={`w-3.5 h-3.5 text-gray-400 transition-transform ${collapsed() ? '-rotate-90' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
182
194
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
@@ -363,7 +375,7 @@ const SectionRenderer: Component<{
363
375
  onSubmit?: (sectionId: string, values: Record<string, unknown>) => void
364
376
  }> = (props) => {
365
377
  return (
366
- <div class="px-4 py-3">
378
+ <div class="px-4 py-3 animate-[slideDown_0.2s_ease-out]">
367
379
  <h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">{props.section.title}</h4>
368
380
  <Switch>
369
381
  <Match when={props.section.type === 'data'}><DataSection content={parseContent(props.section.content)} /></Match>
@@ -383,6 +395,10 @@ const SectionRenderer: Component<{
383
395
  <Match when={props.section.type === 'data_preview'}><DataPreviewSection content={parseContent(props.section.content) as DataPreviewContent} /></Match>
384
396
  <Match when={props.section.type === 'map'}>{(() => { const c = parseContent(props.section.content) as MapSectionContent; return <MapRenderer params={{ geojson: c.geojson, center: c.center, zoom: c.zoom, geojsonStyle: c.style, popup: c.popup, layers: c.layers, height: c.height || '300px', fitBounds: true }} /> })()}</Match>
385
397
  <Match when={props.section.type === 'chart'}>{(() => { const c = parseContent(props.section.content) as ChartComponentParams; return <ChartJSRenderer component={{ id: props.section.id, type: 'chart', position: { colStart: 1, colSpan: 12 }, params: { ...c, renderer: 'native', height: (c as any)?.height || '250px' } }} /> })()}</Match>
398
+ <Match when={props.section.type === 'agent_card'}><AgentCard content={parseContent(props.section.content) as AgentCardContent} /></Match>
399
+ <Match when={props.section.type === 'split_stepper'}><SplitStepper content={parseContent(props.section.content) as SplitStepperContent} /></Match>
400
+ <Match when={props.section.type === 'agent_handoff'}><AgentHandoff content={parseContent(props.section.content) as AgentHandoffContent} /></Match>
401
+ <Match when={props.section.type === 'briefing_diff'}><BriefingDiff content={parseContent(props.section.content) as BriefingDiffContent} /></Match>
386
402
  <Match when={true}><pre class="text-xs text-gray-500 overflow-auto">{JSON.stringify(props.section.content, null, 2)}</pre></Match>
387
403
  </Switch>
388
404
  </div>
@@ -654,30 +670,63 @@ const ActionSection: Component<{
654
670
  content: unknown
655
671
  onAction?: (action: string, data?: unknown) => void
656
672
  }> = (props) => {
657
- const actions = () => {
658
- if (Array.isArray(props.content)) return props.content as Array<{ label: string; value?: string; action?: string; variant?: string; icon?: string }>
659
- const obj = props.content as Record<string, unknown> | null
660
- if (obj && Array.isArray(obj.actions)) {
661
- console.warn('[MCP-UI] ActionSection: content should be an array, got { actions: [...] }. Unwrapping automatically.')
662
- return obj.actions as Array<{ label: string; value?: string; action?: string; variant?: string; icon?: string }>
663
- }
664
- return []
673
+ const data = () => {
674
+ const c = props.content as any
675
+ if (Array.isArray(c)) return { actions: c, title: undefined, preview: undefined, validation: undefined }
676
+ if (c && Array.isArray(c.actions)) return { actions: c.actions, title: c.title, preview: c.preview, validation: c.validation }
677
+ return { actions: [], title: undefined, preview: undefined, validation: undefined }
665
678
  }
679
+
666
680
  return (
667
- <div class="flex flex-wrap gap-2">
668
- <For each={actions()}>
669
- {(item) => (
670
- <button type="button" onClick={() => props.onAction?.(item.value || item.action || item.label, item)}
671
- class={`px-3 py-1.5 text-sm font-medium rounded-lg transition-colors ${
672
- item.variant === 'primary' ? 'bg-blue-600 text-white hover:bg-blue-700'
673
- : item.variant === 'danger' ? 'bg-red-600 text-white hover:bg-red-700'
674
- : 'border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
675
- }`}>
676
- <Show when={item.icon}><span class="mr-1">{item.icon}</span></Show>
677
- {item.label}
678
- </button>
681
+ <div>
682
+ {/* Confirm checkpoint: title + preview (v4.1.0) */}
683
+ <Show when={data().title}>
684
+ <p class="text-sm font-medium text-gray-800 dark:text-gray-200 mb-2">{data().title}</p>
685
+ </Show>
686
+ <Show when={data().preview}>
687
+ {(preview) => (
688
+ <div class="mb-2 p-2 rounded bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-xs text-gray-600 dark:text-gray-400">
689
+ <Show when={preview().count != null}>
690
+ <span class="font-medium text-gray-800 dark:text-gray-200">{preview().count}</span> items
691
+ </Show>
692
+ <Show when={preview().summary}>
693
+ <span class="ml-1">&mdash; {preview().summary}</span>
694
+ </Show>
695
+ </div>
679
696
  )}
680
- </For>
697
+ </Show>
698
+ <Show when={data().validation && data().validation.confidence != null}>
699
+ <div class="mb-2 flex items-center gap-2 text-xs">
700
+ <span classList={{
701
+ 'text-green-600 dark:text-green-400': data().validation.confidence >= 0.8,
702
+ 'text-amber-600 dark:text-amber-400': data().validation.confidence >= 0.5 && data().validation.confidence < 0.8,
703
+ 'text-red-600 dark:text-red-400': data().validation.confidence < 0.5,
704
+ }}>
705
+ {Math.round(data().validation.confidence * 100)}% verified
706
+ </span>
707
+ <Show when={data().validation.hallucinated?.length > 0}>
708
+ <span class="text-amber-600">({data().validation.hallucinated.length} unverified)</span>
709
+ </Show>
710
+ </div>
711
+ </Show>
712
+
713
+ {/* Action buttons */}
714
+ <div class="flex flex-wrap gap-2">
715
+ <For each={data().actions as Array<{ label: string; value?: string; action?: string; variant?: string; icon?: string }>}>
716
+ {(item) => (
717
+ <button type="button" on:click={() => props.onAction?.(item.value || item.action || item.label, item)}
718
+ class={`px-3 py-1.5 text-sm font-medium rounded-lg transition-colors ${
719
+ item.variant === 'primary' ? 'bg-blue-600 text-white hover:bg-blue-700'
720
+ : item.variant === 'danger' ? 'bg-red-600 text-white hover:bg-red-700'
721
+ : item.variant === 'secondary' ? 'border border-blue-300 dark:border-blue-600 text-blue-700 dark:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/20'
722
+ : 'border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
723
+ }`}>
724
+ <Show when={item.icon}><span class="mr-1">{item.icon}</span></Show>
725
+ {item.label}
726
+ </button>
727
+ )}
728
+ </For>
729
+ </div>
681
730
  </div>
682
731
  )
683
732
  }
@@ -737,18 +786,21 @@ const FeedbackSection: Component<{
737
786
  }> = (props) => {
738
787
  const [comment, setComment] = createSignal('')
739
788
  const [showComment, setShowComment] = createSignal(false)
789
+ const [submitted, setSubmitted] = createSignal<string | null>(null)
740
790
  const data = () => {
741
791
  const c = props.content as any
742
- // Support both formats: options array (universal) and approve/reject (simple)
743
792
  const options = c?.options || [
744
- { value: c?.approve?.value || 'approve', label: c?.approve?.label || 'Yes', icon: '👍', variant: 'primary' },
745
- { value: c?.reject?.value || 'reject', label: c?.reject?.label || 'No', icon: '👎' },
793
+ { value: c?.approve?.value || 'approve', label: c?.approve?.label || 'Yes', icon: '\uD83D\uDC4D', variant: 'primary' },
794
+ { value: c?.reject?.value || 'reject', label: c?.reject?.label || 'No', icon: '\uD83D\uDC4E' },
746
795
  ]
747
796
  return {
748
797
  question: c?.question || '',
749
798
  options: options as Array<{ value: string; label: string; icon?: string; variant?: string; needsComment?: boolean }>,
750
799
  allowFreeText: c?.allowFreeText ?? c?.allowComment ?? false,
751
800
  placeholder: c?.placeholder || c?.commentPlaceholder || 'Add a comment...',
801
+ // v4.1.0: per-step feedback
802
+ agentId: c?.agentId as string | undefined,
803
+ stepId: c?.stepId as string | undefined,
752
804
  }
753
805
  }
754
806
 
@@ -757,35 +809,65 @@ const FeedbackSection: Component<{
757
809
  setShowComment(true)
758
810
  return
759
811
  }
760
- props.onAction?.('feedback', { option: option.value, comment: comment() })
812
+ setSubmitted(option.value)
813
+ const payload = {
814
+ option: option.value,
815
+ comment: comment(),
816
+ ...(data().agentId ? { agentId: data().agentId } : {}),
817
+ ...(data().stepId ? { stepId: data().stepId } : {}),
818
+ }
819
+ console.info('[MCP-UI:HITL] user responded', {
820
+ agentId: data().agentId, stepId: data().stepId, action: option.value,
821
+ })
822
+ props.onAction?.('feedback', payload)
761
823
  }
762
824
 
763
825
  return (
764
826
  <div class="space-y-3">
765
827
  <p class="text-sm text-gray-700 dark:text-gray-300">{data().question}</p>
766
- <div class="flex flex-wrap gap-2">
767
- <For each={data().options}>
768
- {(option) => (
769
- <button type="button" onClick={() => handleOption(option)}
770
- class={`px-3 py-1.5 text-sm font-medium rounded-lg transition-colors flex items-center gap-1 ${
771
- option.variant === 'primary' ? 'bg-blue-600 text-white hover:bg-blue-700'
772
- : option.variant === 'danger' ? 'bg-red-600 text-white hover:bg-red-700'
773
- : 'border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
774
- }`}>
775
- <Show when={option.icon}><span>{option.icon}</span></Show>
776
- {option.label}
777
- </button>
778
- )}
779
- </For>
780
- </div>
781
- <Show when={data().allowFreeText || showComment()}>
782
- <div class="flex gap-1">
783
- <input type="text" value={comment()} onInput={(e) => setComment(e.currentTarget.value)}
784
- placeholder={data().placeholder} autofocus={showComment()}
785
- class="flex-1 px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 outline-none" />
786
- <button type="button" onClick={() => props.onAction?.('feedback', { option: 'comment', comment: comment() })}
787
- class="px-3 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700">Send</button>
828
+
829
+ {/* Already submitted — show micro-badge */}
830
+ <Show when={submitted()}>
831
+ <div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
832
+ <span classList={{
833
+ 'text-green-600': submitted() === 'approve',
834
+ 'text-red-600': submitted() === 'reject',
835
+ 'text-blue-600': submitted() !== 'approve' && submitted() !== 'reject',
836
+ }}>
837
+ {submitted() === 'approve' ? '\u2705' : submitted() === 'reject' ? '\u274C' : '\uD83D\uDCAC'} {submitted()}
838
+ </span>
839
+ <Show when={comment()}>
840
+ <span class="italic">&mdash; {comment()}</span>
841
+ </Show>
842
+ </div>
843
+ </Show>
844
+
845
+ {/* Buttons hidden after submit */}
846
+ <Show when={!submitted()}>
847
+ <div class="flex flex-wrap gap-2">
848
+ <For each={data().options}>
849
+ {(option) => (
850
+ <button type="button" on:click={() => handleOption(option)}
851
+ class={`px-3 py-1.5 text-sm font-medium rounded-lg transition-colors flex items-center gap-1 ${
852
+ option.variant === 'primary' ? 'bg-blue-600 text-white hover:bg-blue-700'
853
+ : option.variant === 'danger' ? 'bg-red-600 text-white hover:bg-red-700'
854
+ : 'border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
855
+ }`}>
856
+ <Show when={option.icon}><span>{option.icon}</span></Show>
857
+ {option.label}
858
+ </button>
859
+ )}
860
+ </For>
788
861
  </div>
862
+ <Show when={data().allowFreeText || showComment()}>
863
+ <div class="flex gap-1">
864
+ <input type="text" value={comment()} onInput={(e) => setComment(e.currentTarget.value)}
865
+ placeholder={data().placeholder} autofocus={showComment()}
866
+ class="flex-1 px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 outline-none" />
867
+ <button type="button" on:click={() => { setSubmitted('comment'); props.onAction?.('feedback', { option: 'comment', comment: comment(), agentId: data().agentId, stepId: data().stepId }) }}
868
+ class="px-3 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700">Send</button>
869
+ </div>
870
+ </Show>
789
871
  </Show>
790
872
  </div>
791
873
  )
@@ -0,0 +1,111 @@
1
+ /**
2
+ * SplitStepper — parallel agent steppers side by side
3
+ * v4.1.0: AITL sprint — 2-3 columns, synthesis row at bottom
4
+ *
5
+ * @experimental
6
+ */
7
+
8
+ import { For, Show } from 'solid-js'
9
+ import type { SplitStepperContent } from '../types/chat-bus'
10
+
11
+ export interface SplitStepperProps {
12
+ content: SplitStepperContent
13
+ }
14
+
15
+ const STEP_ICONS: Record<string, string> = {
16
+ done: '\u2705',
17
+ active: '\uD83D\uDD04',
18
+ pending: '\u23F3',
19
+ skipped: '\u23ED\uFE0F',
20
+ error: '\u274C',
21
+ }
22
+
23
+ const AGENT_STATUS_COLORS: Record<string, string> = {
24
+ done: 'border-green-400 dark:border-green-600',
25
+ active: 'border-blue-400 dark:border-blue-500',
26
+ pending: 'border-gray-300 dark:border-gray-600',
27
+ error: 'border-red-400 dark:border-red-600',
28
+ }
29
+
30
+ export function SplitStepper(props: SplitStepperProps) {
31
+ const c = () => props.content
32
+
33
+ if (typeof console !== 'undefined') {
34
+ console.info('[MCP-UI:SplitStepper] mounted', {
35
+ agents: c().agents.map(a => `${a.id}:${a.status}`),
36
+ synthesis: c().synthesis?.status,
37
+ })
38
+ }
39
+
40
+ return (
41
+ <div class="split-stepper">
42
+ {/* Agent columns */}
43
+ <div class="grid gap-3" style={{ "grid-template-columns": `repeat(${Math.min(c().agents.length, 3)}, 1fr)` }}>
44
+ <For each={c().agents}>
45
+ {(agent) => (
46
+ <div class={`rounded-lg border-2 ${AGENT_STATUS_COLORS[agent.status] || AGENT_STATUS_COLORS.pending} p-3`}>
47
+ {/* Agent header */}
48
+ <div class="flex items-center gap-2 mb-2">
49
+ <span class="font-medium text-sm text-gray-900 dark:text-white truncate">{agent.name}</span>
50
+ <span class={`w-2 h-2 rounded-full flex-shrink-0 ${
51
+ agent.status === 'done' ? 'bg-green-500' :
52
+ agent.status === 'active' ? 'bg-blue-500 animate-pulse' :
53
+ agent.status === 'error' ? 'bg-red-500' :
54
+ 'bg-gray-400'
55
+ }`} />
56
+ </div>
57
+
58
+ {/* Steps */}
59
+ <div class="space-y-1">
60
+ <For each={agent.steps}>
61
+ {(step) => (
62
+ <div class="flex items-center gap-2 text-xs">
63
+ <span class="flex-shrink-0 w-4 text-center">{STEP_ICONS[step.status] || STEP_ICONS.pending}</span>
64
+ <span classList={{
65
+ 'text-gray-900 dark:text-white font-medium': step.status === 'active',
66
+ 'text-gray-500 dark:text-gray-400': step.status !== 'active',
67
+ 'line-through opacity-50': step.status === 'skipped',
68
+ }}>
69
+ {step.label}
70
+ </span>
71
+ </div>
72
+ )}
73
+ </For>
74
+ </div>
75
+ </div>
76
+ )}
77
+ </For>
78
+ </div>
79
+
80
+ {/* Synthesis row */}
81
+ <Show when={c().synthesis}>
82
+ {(syn) => (
83
+ <div class={`mt-3 p-3 rounded-lg border-2 text-center ${
84
+ syn().status === 'done' ? 'border-green-400 dark:border-green-600 bg-green-50 dark:bg-green-900/10' :
85
+ syn().status === 'active' ? 'border-blue-400 dark:border-blue-500 bg-blue-50 dark:bg-blue-900/10' :
86
+ 'border-dashed border-gray-300 dark:border-gray-600'
87
+ }`}>
88
+ <div class="flex items-center justify-center gap-2 text-sm">
89
+ <Show when={syn().status === 'active'}>
90
+ <div class="w-3 h-3 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
91
+ </Show>
92
+ <Show when={syn().status === 'done'}>
93
+ <span>{STEP_ICONS.done}</span>
94
+ </Show>
95
+ <Show when={syn().status === 'pending'}>
96
+ <span class="text-gray-400">{STEP_ICONS.pending}</span>
97
+ </Show>
98
+ <span classList={{
99
+ 'font-medium text-blue-700 dark:text-blue-300': syn().status === 'active',
100
+ 'font-medium text-green-700 dark:text-green-300': syn().status === 'done',
101
+ 'text-gray-500 dark:text-gray-400': syn().status === 'pending',
102
+ }}>
103
+ {syn().label}
104
+ </span>
105
+ </div>
106
+ </div>
107
+ )}
108
+ </Show>
109
+ </div>
110
+ )
111
+ }
@@ -64,6 +64,19 @@ export type { CodeBlockRendererProps } from './CodeBlockRenderer'
64
64
  export { MapRenderer } from './MapRenderer'
65
65
  export type { MapRendererProps } from './MapRenderer'
66
66
 
67
+ // Agent AITL (v4.1.0)
68
+ export { AgentCard, AgentStatusBadge } from './AgentCard'
69
+ export type { AgentCardProps, AgentStatusBadgeProps } from './AgentCard'
70
+
71
+ export { SplitStepper } from './SplitStepper'
72
+ export type { SplitStepperProps } from './SplitStepper'
73
+
74
+ export { AgentHandoff } from './AgentHandoff'
75
+ export type { AgentHandoffProps } from './AgentHandoff'
76
+
77
+ export { BriefingDiff } from './BriefingDiff'
78
+ export type { BriefingDiffProps } from './BriefingDiff'
79
+
67
80
  // Data Verification (v3.1.0 — anti-hallucination)
68
81
  export { VerifiedText } from './VerifiedText'
69
82
  export type { VerifiedTextProps } from './VerifiedText'
package/src/index.ts CHANGED
@@ -48,6 +48,12 @@ export { dispatchScratchpad, useScratchpadState } from './stores/scratchpad-stor
48
48
  export { VerifiedText } from './components/VerifiedText'
49
49
  export { DataPreviewSection } from './components/DataPreviewSection'
50
50
 
51
+ // Agent AITL Components (v4.1.0)
52
+ export { AgentCard, AgentStatusBadge } from './components/AgentCard'
53
+ export { SplitStepper } from './components/SplitStepper'
54
+ export { AgentHandoff } from './components/AgentHandoff'
55
+ export { BriefingDiff } from './components/BriefingDiff'
56
+
51
57
  // Autocomplete Components
52
58
  export { GhostText, GhostTextInput } from './components/GhostText'
53
59
  export { AutocompleteDropdown } from './components/AutocompleteDropdown'
@@ -68,6 +74,10 @@ export type { ChatPromptProps } from './components/ChatPrompt'
68
74
  export type { ScratchpadPanelProps } from './components/ScratchpadPanel'
69
75
  export type { VerifiedTextProps } from './components/VerifiedText'
70
76
  export type { DataPreviewSectionProps } from './components/DataPreviewSection'
77
+ export type { AgentCardProps, AgentStatusBadgeProps } from './components/AgentCard'
78
+ export type { SplitStepperProps } from './components/SplitStepper'
79
+ export type { AgentHandoffProps } from './components/AgentHandoff'
80
+ export type { BriefingDiffProps } from './components/BriefingDiff'
71
81
  export type { GhostTextProps, GhostTextInputProps } from './components/GhostText'
72
82
  export type { AutocompleteDropdownProps } from './components/AutocompleteDropdown'
73
83
  export type { AutocompleteFormFieldProps, AutocompleteFormFieldParams } from './components/AutocompleteFormField'
@@ -271,4 +281,9 @@ export type {
271
281
  DataPreviewColumn,
272
282
  DataPreviewContent,
273
283
  MapSectionContent,
284
+ // Agent AITL types (v4.1.0)
285
+ AgentCardContent,
286
+ SplitStepperContent,
287
+ AgentHandoffContent,
288
+ BriefingDiffContent,
274
289
  } from './types/chat-bus'
@@ -111,6 +111,10 @@ export interface ChatCommands {
111
111
  /** Change the chat mode */
112
112
  setMode: (mode: string) => void
113
113
 
114
+ // --- Agents (v4.1.0) ---
115
+ /** Trigger an agent from the chat (replaces /macro command) */
116
+ triggerAgent: (agentId: string, params?: Record<string, unknown>) => void
117
+
114
118
  // --- UI ---
115
119
  /** Scroll to a specific message */
116
120
  scrollToMessage: (messageId: string) => void
@@ -366,7 +370,7 @@ export interface ScratchpadState {
366
370
  export interface ScratchpadSection {
367
371
  id: string
368
372
  title: string
369
- type: 'data' | 'filter' | 'preview' | 'message' | 'action' | 'steps' | 'form' | 'understanding' | 'feedback' | 'prompt' | 'stepper' | 'error' | 'source_card' | 'diff' | 'verified_text' | 'data_preview' | 'map' | 'chart'
373
+ type: 'data' | 'filter' | 'preview' | 'message' | 'action' | 'steps' | 'form' | 'understanding' | 'feedback' | 'prompt' | 'stepper' | 'error' | 'source_card' | 'diff' | 'verified_text' | 'data_preview' | 'map' | 'chart' | 'agent_card' | 'split_stepper' | 'agent_handoff' | 'briefing_diff'
370
374
  content: unknown
371
375
  /** Can the human edit this section? */
372
376
  editable: boolean
@@ -541,3 +545,68 @@ export interface MapSectionContent {
541
545
  /** Map height (CSS, default: '300px') */
542
546
  height?: string
543
547
  }
548
+
549
+ // ─── Agent section types (v4.1.0 — AITL sprint) ────────────
550
+
551
+ /** Content for agent_card scratchpad section */
552
+ export interface AgentCardContent {
553
+ agentId: string
554
+ name: string
555
+ /** Avatar icon key (e.g. 'scales', 'chart', 'search') or emoji */
556
+ avatar?: string
557
+ status: 'idle' | 'running' | 'waiting' | 'done' | 'error'
558
+ /** Agent capabilities as string badges */
559
+ capabilities?: string[]
560
+ /** LLM model used */
561
+ model?: string
562
+ /** Current step info (shown when running) */
563
+ currentStep?: { id: string; label: string }
564
+ }
565
+
566
+ /** Content for split_stepper scratchpad section (parallel agents) */
567
+ export interface SplitStepperContent {
568
+ agents: Array<{
569
+ id: string
570
+ name: string
571
+ steps: Array<{ id: string; label: string; status: 'done' | 'active' | 'pending' | 'skipped' | 'error' }>
572
+ status: 'done' | 'active' | 'pending' | 'error'
573
+ }>
574
+ /** Final synthesis step (activates when all agents are done) */
575
+ synthesis?: {
576
+ status: 'done' | 'active' | 'pending'
577
+ label: string
578
+ }
579
+ }
580
+
581
+ /** Content for agent_handoff scratchpad section */
582
+ export interface AgentHandoffContent {
583
+ from: { id: string; name: string; avatar?: string }
584
+ to: { id: string; name: string; avatar?: string }
585
+ /** Data keys transferred */
586
+ dataKeys?: string[]
587
+ /** Summary of what was transferred */
588
+ summary?: string
589
+ /** Count of items transferred */
590
+ itemCount?: number
591
+ }
592
+
593
+ /** Content for briefing_diff scratchpad section */
594
+ export interface BriefingDiffContent {
595
+ /** Title of the comparison */
596
+ title?: string
597
+ /** When was the previous version */
598
+ previousDate?: string
599
+ /** When is the current version */
600
+ currentDate?: string
601
+ /** List of changes */
602
+ changes: Array<{
603
+ type: 'added' | 'removed' | 'changed'
604
+ label: string
605
+ /** Previous value (for 'changed' and 'removed') */
606
+ previous?: string
607
+ /** Current value (for 'changed' and 'added') */
608
+ current?: string
609
+ }>
610
+ /** Summary stats */
611
+ stats?: { added: number; removed: number; changed: number }
612
+ }