@geminilight/mindos 0.6.29 → 0.6.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 (71) hide show
  1. package/README.md +10 -4
  2. package/app/app/api/acp/config/route.ts +82 -0
  3. package/app/app/api/acp/detect/route.ts +71 -48
  4. package/app/app/api/acp/install/route.ts +51 -0
  5. package/app/app/api/acp/session/route.ts +141 -11
  6. package/app/app/api/ask/route.ts +116 -13
  7. package/app/app/api/workflows/route.ts +156 -0
  8. package/app/app/page.tsx +7 -2
  9. package/app/components/ActivityBar.tsx +12 -4
  10. package/app/components/AskModal.tsx +4 -1
  11. package/app/components/FileTree.tsx +21 -10
  12. package/app/components/HomeContent.tsx +1 -0
  13. package/app/components/Panel.tsx +1 -0
  14. package/app/components/RightAskPanel.tsx +5 -1
  15. package/app/components/SidebarLayout.tsx +6 -0
  16. package/app/components/agents/AgentDetailContent.tsx +263 -47
  17. package/app/components/agents/AgentsContentPage.tsx +11 -0
  18. package/app/components/agents/AgentsPanelA2aTab.tsx +285 -46
  19. package/app/components/agents/AgentsPanelSessionsTab.tsx +166 -0
  20. package/app/components/agents/agents-content-model.ts +2 -2
  21. package/app/components/ask/AgentSelectorCapsule.tsx +218 -0
  22. package/app/components/ask/AskContent.tsx +197 -239
  23. package/app/components/ask/FileChip.tsx +82 -17
  24. package/app/components/ask/MentionPopover.tsx +21 -3
  25. package/app/components/ask/MessageList.tsx +30 -9
  26. package/app/components/ask/SlashCommandPopover.tsx +21 -3
  27. package/app/components/panels/AgentsPanel.tsx +1 -0
  28. package/app/components/panels/AgentsPanelHubNav.tsx +9 -2
  29. package/app/components/panels/WorkflowsPanel.tsx +206 -0
  30. package/app/components/renderers/workflow-yaml/StepEditor.tsx +157 -0
  31. package/app/components/renderers/workflow-yaml/WorkflowEditor.tsx +201 -0
  32. package/app/components/renderers/workflow-yaml/WorkflowRunner.tsx +226 -0
  33. package/app/components/renderers/workflow-yaml/WorkflowYamlRenderer.tsx +126 -0
  34. package/app/components/renderers/workflow-yaml/execution.ts +177 -0
  35. package/app/components/renderers/workflow-yaml/index.ts +6 -0
  36. package/app/components/renderers/workflow-yaml/manifest.ts +21 -0
  37. package/app/components/renderers/workflow-yaml/parser.ts +172 -0
  38. package/app/components/renderers/workflow-yaml/selectors.tsx +522 -0
  39. package/app/components/renderers/workflow-yaml/serializer.ts +56 -0
  40. package/app/components/renderers/workflow-yaml/types.ts +46 -0
  41. package/app/hooks/useAcpConfig.ts +96 -0
  42. package/app/hooks/useAcpDetection.ts +69 -14
  43. package/app/hooks/useAcpRegistry.ts +46 -11
  44. package/app/hooks/useAskModal.ts +12 -5
  45. package/app/hooks/useAskPanel.ts +8 -5
  46. package/app/hooks/useAskSession.ts +19 -2
  47. package/app/hooks/useImageUpload.ts +152 -0
  48. package/app/lib/acp/acp-tools.ts +3 -1
  49. package/app/lib/acp/agent-descriptors.ts +274 -0
  50. package/app/lib/acp/bridge.ts +6 -0
  51. package/app/lib/acp/index.ts +20 -4
  52. package/app/lib/acp/registry.ts +74 -7
  53. package/app/lib/acp/session.ts +481 -28
  54. package/app/lib/acp/subprocess.ts +307 -21
  55. package/app/lib/acp/types.ts +158 -20
  56. package/app/lib/agent/model.ts +18 -3
  57. package/app/lib/agent/to-agent-messages.ts +25 -2
  58. package/app/lib/i18n/modules/knowledge.ts +4 -0
  59. package/app/lib/i18n/modules/navigation.ts +2 -0
  60. package/app/lib/i18n/modules/panels.ts +146 -2
  61. package/app/lib/pi-integration/skills.ts +21 -6
  62. package/app/lib/renderers/index.ts +2 -2
  63. package/app/lib/settings.ts +10 -0
  64. package/app/lib/types.ts +12 -1
  65. package/app/next-env.d.ts +1 -1
  66. package/app/package.json +3 -1
  67. package/package.json +1 -1
  68. package/templates/en/.mindos/workflows/Sprint Release.flow.yaml +130 -0
  69. package/templates/zh/.mindos/workflows//345/221/250/350/277/255/344/273/243/346/243/200/346/237/245.flow.yaml +84 -0
  70. package/app/components/renderers/workflow/WorkflowRenderer.tsx +0 -409
  71. package/app/components/renderers/workflow/manifest.ts +0 -14
@@ -0,0 +1,218 @@
1
+ 'use client';
2
+
3
+ import { useState, useRef, useEffect, useCallback } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { Bot, ChevronDown, X, Check } from 'lucide-react';
6
+ import type { AcpAgentSelection } from '@/hooks/useAskModal';
7
+ import type { DetectedAgent } from '@/hooks/useAcpDetection';
8
+ import { useLocale } from '@/lib/LocaleContext';
9
+
10
+ interface AgentSelectorCapsuleProps {
11
+ selectedAgent: AcpAgentSelection | null;
12
+ onSelect: (agent: AcpAgentSelection | null) => void;
13
+ installedAgents: DetectedAgent[];
14
+ loading?: boolean;
15
+ }
16
+
17
+ interface DropdownPos {
18
+ top: number;
19
+ left: number;
20
+ direction: 'up' | 'down';
21
+ }
22
+
23
+ export default function AgentSelectorCapsule({
24
+ selectedAgent,
25
+ onSelect,
26
+ installedAgents,
27
+ loading = false,
28
+ }: AgentSelectorCapsuleProps) {
29
+ const { t } = useLocale();
30
+ const p = t.panels.agents;
31
+ const [open, setOpen] = useState(false);
32
+ const [pos, setPos] = useState<DropdownPos | null>(null);
33
+ const triggerRef = useRef<HTMLButtonElement>(null);
34
+ const dropdownRef = useRef<HTMLDivElement>(null);
35
+
36
+ // Position the dropdown relative to the trigger, rendered via portal
37
+ useEffect(() => {
38
+ if (!open || !triggerRef.current) return;
39
+ const rect = triggerRef.current.getBoundingClientRect();
40
+ const spaceAbove = rect.top;
41
+ const spaceBelow = window.innerHeight - rect.bottom;
42
+ const estimatedH = 200; // approximate dropdown height
43
+ const direction: 'up' | 'down' = spaceAbove > spaceBelow && spaceAbove > estimatedH ? 'up' : 'down';
44
+
45
+ setPos({
46
+ left: rect.left,
47
+ top: direction === 'up' ? rect.top - 6 : rect.bottom + 6,
48
+ direction,
49
+ });
50
+ }, [open]);
51
+
52
+ // Close dropdown on outside click
53
+ useEffect(() => {
54
+ if (!open) return;
55
+ const handler = (e: MouseEvent) => {
56
+ const target = e.target as Node;
57
+ if (
58
+ triggerRef.current && !triggerRef.current.contains(target) &&
59
+ dropdownRef.current && !dropdownRef.current.contains(target)
60
+ ) {
61
+ setOpen(false);
62
+ }
63
+ };
64
+ document.addEventListener('mousedown', handler);
65
+ return () => document.removeEventListener('mousedown', handler);
66
+ }, [open]);
67
+
68
+ // Close on Escape
69
+ useEffect(() => {
70
+ if (!open) return;
71
+ const handler = (e: KeyboardEvent) => {
72
+ if (e.key === 'Escape') setOpen(false);
73
+ };
74
+ document.addEventListener('keydown', handler);
75
+ return () => document.removeEventListener('keydown', handler);
76
+ }, [open]);
77
+
78
+ // Reposition on scroll/resize while open
79
+ useEffect(() => {
80
+ if (!open || !triggerRef.current) return;
81
+ const reposition = () => {
82
+ if (!triggerRef.current) return;
83
+ const rect = triggerRef.current.getBoundingClientRect();
84
+ const spaceAbove = rect.top;
85
+ const spaceBelow = window.innerHeight - rect.bottom;
86
+ const estimatedH = 200;
87
+ const direction: 'up' | 'down' = spaceAbove > spaceBelow && spaceAbove > estimatedH ? 'up' : 'down';
88
+ setPos({
89
+ left: rect.left,
90
+ top: direction === 'up' ? rect.top - 6 : rect.bottom + 6,
91
+ direction,
92
+ });
93
+ };
94
+ window.addEventListener('scroll', reposition, true);
95
+ window.addEventListener('resize', reposition);
96
+ return () => {
97
+ window.removeEventListener('scroll', reposition, true);
98
+ window.removeEventListener('resize', reposition);
99
+ };
100
+ }, [open]);
101
+
102
+ const handleSelectDefault = useCallback(() => {
103
+ onSelect(null);
104
+ setOpen(false);
105
+ }, [onSelect]);
106
+
107
+ const handleSelectAgent = useCallback((agent: DetectedAgent) => {
108
+ onSelect({ id: agent.id, name: agent.name });
109
+ setOpen(false);
110
+ }, [onSelect]);
111
+
112
+ const handleClear = useCallback((e: React.MouseEvent) => {
113
+ e.stopPropagation();
114
+ onSelect(null);
115
+ }, [onSelect]);
116
+
117
+ const isDefault = !selectedAgent;
118
+ const displayName = selectedAgent?.name ?? p.acpDefaultAgent;
119
+
120
+ // Only show if there are installed agents to choose from
121
+ if (!loading && installedAgents.length === 0 && !selectedAgent) return null;
122
+
123
+ const dropdown = open && pos ? (
124
+ <div
125
+ ref={dropdownRef}
126
+ role="listbox"
127
+ aria-label={p.acpSelectAgent}
128
+ className="fixed z-50 min-w-[180px] max-w-[240px] rounded-lg border border-border bg-card shadow-lg py-1 animate-in fade-in-0 zoom-in-95 duration-100"
129
+ style={{
130
+ left: pos.left,
131
+ ...(pos.direction === 'up'
132
+ ? { bottom: window.innerHeight - pos.top }
133
+ : { top: pos.top }),
134
+ }}
135
+ >
136
+ {/* Default MindOS Agent option */}
137
+ <button
138
+ type="button"
139
+ role="option"
140
+ aria-selected={isDefault}
141
+ onClick={handleSelectDefault}
142
+ className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-left transition-colors hover:bg-muted"
143
+ >
144
+ <Bot size={12} className="shrink-0 text-muted-foreground" />
145
+ <span className="flex-1 truncate font-medium">{p.acpDefaultAgent}</span>
146
+ {isDefault && <Check size={11} className="shrink-0 text-[var(--amber)]" />}
147
+ </button>
148
+
149
+ {/* Divider */}
150
+ {installedAgents.length > 0 && (
151
+ <div className="mx-2 my-1 border-t border-border/60" />
152
+ )}
153
+
154
+ {/* Installed ACP agents */}
155
+ {installedAgents.map((agent) => {
156
+ const isSelected = selectedAgent?.id === agent.id;
157
+ return (
158
+ <button
159
+ key={agent.id}
160
+ type="button"
161
+ role="option"
162
+ aria-selected={isSelected}
163
+ onClick={() => handleSelectAgent(agent)}
164
+ className="flex w-full items-center gap-2 px-3 py-1.5 text-xs text-left transition-colors hover:bg-muted"
165
+ >
166
+ <span className="w-2 h-2 rounded-full bg-[var(--success)] shrink-0" />
167
+ <span className="flex-1 truncate">{agent.name}</span>
168
+ {isSelected && <Check size={11} className="shrink-0 text-[var(--amber)]" />}
169
+ </button>
170
+ );
171
+ })}
172
+ </div>
173
+ ) : null;
174
+
175
+ return (
176
+ <>
177
+ <button
178
+ ref={triggerRef}
179
+ type="button"
180
+ onClick={() => setOpen(v => !v)}
181
+ className={`
182
+ inline-flex items-center gap-1 rounded-full px-2.5 py-0.5
183
+ text-2xs font-medium transition-colors
184
+ border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
185
+ ${isDefault
186
+ ? 'bg-muted/50 border-border/50 text-muted-foreground hover:bg-muted hover:text-foreground'
187
+ : 'bg-[var(--amber)]/10 border-[var(--amber)]/25 text-foreground hover:bg-[var(--amber)]/15'
188
+ }
189
+ `}
190
+ title={p.acpChangeAgent}
191
+ aria-expanded={open}
192
+ aria-haspopup="listbox"
193
+ >
194
+ {isDefault ? (
195
+ <Bot size={11} className="shrink-0 text-muted-foreground" />
196
+ ) : (
197
+ <span className="w-1.5 h-1.5 rounded-full bg-[var(--success)] shrink-0" />
198
+ )}
199
+ <span className="truncate max-w-[120px]">{displayName}</span>
200
+ {selectedAgent ? (
201
+ <span
202
+ role="button"
203
+ tabIndex={0}
204
+ onClick={handleClear}
205
+ onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClear(e as unknown as React.MouseEvent); } }}
206
+ className="p-0.5 -mr-1 rounded-full text-muted-foreground hover:text-foreground transition-colors"
207
+ aria-label={`Remove ${selectedAgent.name}`}
208
+ >
209
+ <X size={9} />
210
+ </span>
211
+ ) : (
212
+ <ChevronDown size={10} className="shrink-0 text-muted-foreground" />
213
+ )}
214
+ </button>
215
+ {typeof document !== 'undefined' && dropdown && createPortal(dropdown, document.body)}
216
+ </>
217
+ );
218
+ }