groove-dev 0.27.142 → 0.27.144

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 (187) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/api.js +1086 -6532
  4. package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
  5. package/node_modules/@groove-dev/daemon/src/index.js +3 -0
  6. package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
  7. package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
  8. package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
  9. package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
  10. package/node_modules/@groove-dev/daemon/src/process.js +2 -2
  11. package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
  12. package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
  13. package/node_modules/@groove-dev/daemon/src/routes/agents.js +889 -0
  14. package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
  15. package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
  16. package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
  17. package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
  18. package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
  19. package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
  20. package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
  21. package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
  22. package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
  23. package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
  24. package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
  25. package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
  26. package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
  27. package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
  28. package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
  29. package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
  30. package/node_modules/@groove-dev/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  31. package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  32. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  33. package/node_modules/@groove-dev/gui/package.json +1 -1
  34. package/{packages/gui/src/app.jsx → node_modules/@groove-dev/gui/src/App.jsx} +0 -2
  35. package/node_modules/@groove-dev/gui/src/app.css +35 -0
  36. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
  37. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +144 -31
  38. package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
  39. package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
  40. package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
  41. package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
  42. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
  43. package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
  44. package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
  45. package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
  46. package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
  47. package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
  48. package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  49. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
  50. package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
  51. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
  52. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  53. package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
  54. package/node_modules/@groove-dev/gui/src/components/editor/selection-menu.jsx +2 -0
  55. package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
  56. package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
  57. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +195 -14
  58. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +286 -102
  59. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
  60. package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
  61. package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
  62. package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
  63. package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
  64. package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
  65. package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
  66. package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
  67. package/node_modules/@groove-dev/gui/src/lib/status.js +24 -24
  68. package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
  69. package/node_modules/@groove-dev/gui/src/stores/groove.js +34 -3144
  70. package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
  71. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +452 -0
  72. package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
  73. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +227 -0
  74. package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
  75. package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
  76. package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
  77. package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
  78. package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
  79. package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
  80. package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
  81. package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
  82. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
  83. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
  84. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +17 -6
  85. package/node_modules/@groove-dev/gui/src/views/models.jsx +410 -509
  86. package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
  87. package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
  88. package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
  89. package/package.json +1 -1
  90. package/packages/cli/package.json +1 -1
  91. package/packages/daemon/package.json +1 -1
  92. package/packages/daemon/src/api.js +1086 -6532
  93. package/packages/daemon/src/gateways/manager.js +35 -1
  94. package/packages/daemon/src/index.js +3 -0
  95. package/packages/daemon/src/journalist.js +23 -13
  96. package/packages/daemon/src/mlx-server.js +365 -0
  97. package/packages/daemon/src/model-lab.js +308 -12
  98. package/packages/daemon/src/pm.js +1 -1
  99. package/packages/daemon/src/process.js +2 -2
  100. package/packages/daemon/src/providers/local.js +36 -8
  101. package/packages/daemon/src/registry.js +21 -5
  102. package/packages/daemon/src/routes/agents.js +889 -0
  103. package/packages/daemon/src/routes/coordination.js +318 -0
  104. package/packages/daemon/src/routes/files.js +751 -0
  105. package/packages/daemon/src/routes/integrations.js +485 -0
  106. package/packages/daemon/src/routes/network.js +1784 -0
  107. package/packages/daemon/src/routes/providers.js +755 -0
  108. package/packages/daemon/src/routes/schedules.js +110 -0
  109. package/packages/daemon/src/routes/teams.js +650 -0
  110. package/packages/daemon/src/scheduler.js +456 -24
  111. package/packages/daemon/src/teams.js +1 -1
  112. package/packages/daemon/src/validate.js +38 -1
  113. package/packages/daemon/templates/mlx-setup.json +12 -0
  114. package/packages/daemon/templates/tgi-setup.json +1 -1
  115. package/packages/daemon/templates/vllm-setup.json +1 -1
  116. package/packages/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  117. package/packages/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  118. package/packages/gui/dist/index.html +2 -2
  119. package/packages/gui/package.json +1 -1
  120. package/{node_modules/@groove-dev/gui/src/app.jsx → packages/gui/src/App.jsx} +0 -2
  121. package/packages/gui/src/app.css +35 -0
  122. package/packages/gui/src/components/agents/agent-config.jsx +1 -128
  123. package/packages/gui/src/components/agents/agent-feed.jsx +144 -31
  124. package/packages/gui/src/components/agents/agent-node.jsx +8 -13
  125. package/packages/gui/src/components/agents/code-review.jsx +159 -122
  126. package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
  127. package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
  128. package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
  129. package/packages/gui/src/components/automations/automation-card.jsx +274 -0
  130. package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
  131. package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
  132. package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
  133. package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
  134. package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  135. package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
  136. package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
  137. package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
  138. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  139. package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
  140. package/packages/gui/src/components/editor/selection-menu.jsx +2 -0
  141. package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
  142. package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
  143. package/packages/gui/src/components/lab/parameter-panel.jsx +195 -14
  144. package/packages/gui/src/components/lab/runtime-config.jsx +286 -102
  145. package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
  146. package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
  147. package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
  148. package/packages/gui/src/components/network/network-health.jsx +2 -2
  149. package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
  150. package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
  151. package/packages/gui/src/components/ui/sheet.jsx +5 -2
  152. package/packages/gui/src/lib/cron.js +64 -0
  153. package/packages/gui/src/lib/status.js +24 -24
  154. package/packages/gui/src/lib/theme-hex.js +1 -0
  155. package/packages/gui/src/stores/groove.js +34 -3144
  156. package/packages/gui/src/stores/helpers.js +10 -0
  157. package/packages/gui/src/stores/slices/agents-slice.js +452 -0
  158. package/packages/gui/src/stores/slices/automations-slice.js +96 -0
  159. package/packages/gui/src/stores/slices/chat-slice.js +227 -0
  160. package/packages/gui/src/stores/slices/editor-slice.js +285 -0
  161. package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
  162. package/packages/gui/src/stores/slices/network-slice.js +361 -0
  163. package/packages/gui/src/stores/slices/preview-slice.js +109 -0
  164. package/packages/gui/src/stores/slices/providers-slice.js +897 -0
  165. package/packages/gui/src/stores/slices/teams-slice.js +413 -0
  166. package/packages/gui/src/stores/slices/ui-slice.js +98 -0
  167. package/packages/gui/src/views/agents.jsx +5 -5
  168. package/packages/gui/src/views/dashboard.jsx +12 -13
  169. package/packages/gui/src/views/marketplace.jsx +191 -3
  170. package/packages/gui/src/views/model-lab.jsx +17 -6
  171. package/packages/gui/src/views/models.jsx +410 -509
  172. package/packages/gui/src/views/network.jsx +3 -3
  173. package/packages/gui/src/views/settings.jsx +81 -94
  174. package/packages/gui/src/views/teams.jsx +40 -483
  175. package/SECURITY_SWEEP.md +0 -228
  176. package/TRAINING_DATA_v4.md +0 -6
  177. package/node_modules/@groove-dev/gui/dist/assets/index-Bjd91ufV.js +0 -984
  178. package/node_modules/@groove-dev/gui/dist/assets/index-BqdwIFn4.css +0 -1
  179. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -322
  180. package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
  181. package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
  182. package/packages/gui/dist/assets/index-Bjd91ufV.js +0 -984
  183. package/packages/gui/dist/assets/index-BqdwIFn4.css +0 -1
  184. package/packages/gui/src/components/agents/agent-chat.jsx +0 -322
  185. package/packages/gui/src/views/preview.jsx +0 -6
  186. package/packages/gui/src/views/subscription-panel.jsx +0 -327
  187. package/test.py +0 -571
@@ -415,9 +415,9 @@ function NetworkOverview() {
415
415
  return (
416
416
  <div className="flex flex-col h-full">
417
417
  <ComputeHeader />
418
- <div className="flex-1 min-h-0 grid" style={{ gridTemplateColumns: '2.5fr 1fr', gap: '1px', background: HEX.surface3 }}>
418
+ <div className="flex-1 min-h-0 grid [grid-template-columns:2.5fr_1fr] gap-px" style={{ background: HEX.surface3 }}>
419
419
  {/* Left column */}
420
- <div className="flex flex-col min-w-0 min-h-0" style={{ gap: '1px' }}>
420
+ <div className="flex flex-col min-w-0 min-h-0 gap-px">
421
421
  <div className="flex-1 min-h-0 overflow-hidden bg-surface-1">
422
422
  <ActivityChart />
423
423
  </div>
@@ -426,7 +426,7 @@ function NetworkOverview() {
426
426
  </div>
427
427
  </div>
428
428
  {/* Right column */}
429
- <div className="flex flex-col min-w-0 min-h-0" style={{ gap: '1px' }}>
429
+ <div className="flex flex-col min-w-0 min-h-0 gap-px">
430
430
  <div className="min-h-0 overflow-hidden bg-surface-1">
431
431
  <NodeCard />
432
432
  </div>
@@ -6,7 +6,7 @@ import { Badge } from '../components/ui/badge';
6
6
  import { ScrollArea } from '../components/ui/scroll-area';
7
7
  import { Skeleton } from '../components/ui/skeleton';
8
8
  import { StatusDot } from '../components/ui/status-dot';
9
- import { OllamaSetup } from '../components/agents/ollama-setup';
9
+
10
10
  import { FolderBrowser } from '../components/agents/folder-browser';
11
11
  import { ProviderSetupWizard } from '../components/settings/ProviderSetupWizard';
12
12
  import { Sheet, SheetContent } from '../components/ui/sheet';
@@ -56,7 +56,7 @@ function ProviderCard({ provider, onKeyChange }) {
56
56
  const [settingKey, setSettingKey] = useState(false);
57
57
  const [keyInput, setKeyInput] = useState('');
58
58
  const [showKey, setShowKey] = useState(false);
59
- const [ollamaOpen, setOllamaOpen] = useState(false);
59
+
60
60
  const [wizardOpen, setWizardOpen] = useState(false);
61
61
  const [wizardStep, setWizardStep] = useState(0);
62
62
  const [customPathOpen, setCustomPathOpen] = useState(false);
@@ -114,67 +114,6 @@ function ProviderCard({ provider, onKeyChange }) {
114
114
  setWizardOpen(true);
115
115
  }
116
116
 
117
- // Local models card
118
- if (isLocal) {
119
- const installedCount = provider.models?.filter(m => !m.disabled)?.length || 0;
120
- const goToModels = () => useGrooveStore.getState().setActiveView('models');
121
- return (
122
- <div className="flex flex-col rounded-lg border border-border-subtle bg-surface-1 overflow-hidden min-w-[220px]">
123
- <div className="flex items-center gap-2.5 px-4 py-3 border-b border-border-subtle">
124
- <StatusDot status={isReady && installedCount > 0 ? 'running' : 'crashed'} size="sm" />
125
- <span className="text-[13px] font-semibold text-text-0 font-sans">{provider.name}</span>
126
- <div className="flex-1" />
127
- {isReady && installedCount > 0 ? (
128
- <Badge variant="success" className="text-2xs gap-1"><Check size={8} /> {installedCount} models</Badge>
129
- ) : isReady ? (
130
- <Badge variant="warning" className="text-2xs">No models pulled</Badge>
131
- ) : (
132
- <Badge variant="default" className="text-2xs">Not set up</Badge>
133
- )}
134
- </div>
135
- <div className="flex-1">
136
- {ollamaOpen ? (
137
- <>
138
- <OllamaSetup isInstalled={isReady} onModelChange={onKeyChange} />
139
- <div className="px-4 py-2 border-t border-border-subtle flex gap-2">
140
- <Button variant="ghost" size="sm" onClick={() => setOllamaOpen(false)} className="flex-1 h-7 text-2xs">
141
- Back
142
- </Button>
143
- <Button variant="secondary" size="sm" onClick={() => { setOllamaOpen(false); goToModels(); }} className="flex-1 h-7 text-2xs gap-1">
144
- Models Tab
145
- </Button>
146
- </div>
147
- </>
148
- ) : (
149
- <div className="px-4 py-3 flex flex-col h-full">
150
- <div className="text-xs text-text-3 font-sans flex-1">
151
- {isReady && installedCount > 0
152
- ? 'Full agentic runtime — tool calling, context rotation, zero cloud cost'
153
- : isReady
154
- ? 'Ollama is running. Pull a model to start using local agents.'
155
- : 'Run any open-source model locally — free, private, fully offline. Requires Ollama.'}
156
- </div>
157
- <div className="flex gap-2 mt-3">
158
- {!isReady ? (
159
- <Button variant="primary" size="sm" onClick={() => setOllamaOpen(true)} className="flex-1 h-7 text-2xs gap-1.5">
160
- <Cpu size={11} /> Set Up Ollama
161
- </Button>
162
- ) : (
163
- <Button variant="primary" size="sm" onClick={() => setOllamaOpen(true)} className="flex-1 h-7 text-2xs gap-1.5">
164
- <Cpu size={11} /> {installedCount > 0 ? 'Manage' : 'Pull Models'}
165
- </Button>
166
- )}
167
- <Button variant="secondary" size="sm" onClick={goToModels} className="flex-1 h-7 text-2xs gap-1.5">
168
- Models Tab
169
- </Button>
170
- </div>
171
- </div>
172
- )}
173
- </div>
174
- </div>
175
- );
176
- }
177
-
178
117
  const isInstalling = installProgress?.installing;
179
118
  const effectivelyInstalled = provider.installed || installProgress?.done;
180
119
 
@@ -1693,8 +1632,8 @@ export default function SettingsView() {
1693
1632
  }
1694
1633
 
1695
1634
  const visibleProviders = providers.filter((p) => p.id !== 'groove-network');
1696
- const connectedCount = visibleProviders.filter((p) => {
1697
- if (p.authType === 'local') return p.installed;
1635
+ const cardProviders = visibleProviders.filter((p) => p.id !== 'local');
1636
+ const connectedCount = cardProviders.filter((p) => {
1698
1637
  if (p.authType === 'subscription') return p.installed && p.authStatus?.authenticated;
1699
1638
  return p.installed && p.hasKey;
1700
1639
  }).length;
@@ -1729,10 +1668,10 @@ export default function SettingsView() {
1729
1668
  <div className="flex items-center gap-2 mb-2.5 px-0.5">
1730
1669
  <span className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider">Providers</span>
1731
1670
  <div className="flex-1 h-px bg-border-subtle" />
1732
- <span className="text-2xs text-text-4 font-sans">{connectedCount}/{visibleProviders.length} connected</span>
1671
+ <span className="text-2xs text-text-4 font-sans">{connectedCount}/{cardProviders.length} connected</span>
1733
1672
  </div>
1734
1673
  <div className="grid grid-cols-4 gap-3">
1735
- {visibleProviders.map((p) => (
1674
+ {cardProviders.map((p) => (
1736
1675
  <ProviderCard key={p.id} provider={p} onKeyChange={loadProviders} />
1737
1676
  ))}
1738
1677
  </div>
@@ -1778,16 +1717,45 @@ export default function SettingsView() {
1778
1717
  </ConfigCard>
1779
1718
 
1780
1719
  <ConfigCard icon={Cpu} label="Default Model" description="Model used for new agents. Auto routes by role.">
1781
- <select
1782
- value={config.defaultModel || ''}
1783
- onChange={(e) => updateConfig('defaultModel', e.target.value || null)}
1784
- className="w-full h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono focus:outline-none focus:ring-1 focus:ring-accent cursor-pointer"
1785
- >
1786
- <option value="">Auto (route by role)</option>
1787
- {(providers.find((p) => p.id === (config.defaultProvider || 'claude-code'))?.models || []).map((m) => (
1788
- <option key={m.id} value={m.id}>{m.name}</option>
1789
- ))}
1790
- </select>
1720
+ {(() => {
1721
+ const selectedProvider = providers.find((p) => p.id === (config.defaultProvider || 'claude-code'));
1722
+ const allModels = selectedProvider?.models || [];
1723
+ const isLocalProvider = (config.defaultProvider || 'claude-code') === 'local';
1724
+ const ollamaModels = isLocalProvider ? allModels.filter(m => m.source === 'ollama') : [];
1725
+ const ggufModels = isLocalProvider ? allModels.filter(m => m.source === 'gguf') : [];
1726
+ const runtimeModels = isLocalProvider ? allModels.filter(m => m.source === 'runtime') : [];
1727
+ const selectedModel = config.defaultModel || '';
1728
+ const selectedMeta = allModels.find(m => m.id === selectedModel);
1729
+ const needsRuntime = isLocalProvider && selectedMeta && !selectedMeta.hasRuntime;
1730
+ return (
1731
+ <>
1732
+ <select
1733
+ value={selectedModel}
1734
+ onChange={(e) => updateConfig('defaultModel', e.target.value || null)}
1735
+ className="w-full h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono focus:outline-none focus:ring-1 focus:ring-accent cursor-pointer"
1736
+ >
1737
+ <option value="">Auto (route by role)</option>
1738
+ {isLocalProvider ? (
1739
+ <>
1740
+ {ollamaModels.length > 0 && <optgroup label="Ollama">{ollamaModels.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}</optgroup>}
1741
+ {ggufModels.length > 0 && <optgroup label="Downloaded GGUFs">{ggufModels.map(m => <option key={m.id} value={m.id}>{m.name}{m.hasRuntime ? '' : ' (no runtime)'}</option>)}</optgroup>}
1742
+ {runtimeModels.length > 0 && <optgroup label="Runtime Models">{runtimeModels.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}</optgroup>}
1743
+ </>
1744
+ ) : (
1745
+ allModels.map((m) => <option key={m.id} value={m.id}>{m.name}</option>)
1746
+ )}
1747
+ </select>
1748
+ {needsRuntime && (
1749
+ <button
1750
+ onClick={() => useGrooveStore.getState().setActiveView('model-lab')}
1751
+ className="mt-1.5 w-full text-left text-2xs text-warning font-sans px-1 hover:underline cursor-pointer"
1752
+ >
1753
+ This model needs a runtime. Open Model Lab to launch one.
1754
+ </button>
1755
+ )}
1756
+ </>
1757
+ );
1758
+ })()}
1791
1759
  </ConfigCard>
1792
1760
 
1793
1761
  <ConfigCard icon={FolderOpen} label="Working Directory" description="Default root directory for new agents.">
@@ -1833,23 +1801,42 @@ export default function SettingsView() {
1833
1801
  <option key={p.id} value={p.id}>{p.name}</option>
1834
1802
  ))}
1835
1803
  </select>
1836
- <select
1837
- value={config.defaultChatModel || ''}
1838
- onChange={(e) => updateConfig('defaultChatModel', e.target.value || null)}
1839
- className="w-full h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono focus:outline-none focus:ring-1 focus:ring-accent cursor-pointer"
1840
- >
1841
- <option value="">Auto (Sonnet)</option>
1842
- {(providers.find((p) => p.id === (config.defaultChatProvider || config.defaultProvider || 'claude-code'))?.models || [])
1843
- .filter((m) => {
1844
- const id = (typeof m === 'string' ? m : m.id || '').toLowerCase();
1845
- return !id.includes('dall-e') && !id.includes('imagen') && !id.includes('image');
1846
- })
1847
- .map((m) => {
1848
- const id = typeof m === 'string' ? m : m.id;
1849
- const name = typeof m === 'string' ? m : m.name || m.id;
1850
- return <option key={id} value={id}>{name}</option>;
1851
- })}
1852
- </select>
1804
+ {(() => {
1805
+ const chatProv = providers.find((p) => p.id === (config.defaultChatProvider || config.defaultProvider || 'claude-code'));
1806
+ const chatModels = (chatProv?.models || []).filter((m) => {
1807
+ const id = (typeof m === 'string' ? m : m.id || '').toLowerCase();
1808
+ return !id.includes('dall-e') && !id.includes('imagen') && !id.includes('image');
1809
+ });
1810
+ const isChatLocal = (config.defaultChatProvider || config.defaultProvider || 'claude-code') === 'local';
1811
+ return (
1812
+ <select
1813
+ value={config.defaultChatModel || ''}
1814
+ onChange={(e) => updateConfig('defaultChatModel', e.target.value || null)}
1815
+ className="w-full h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono focus:outline-none focus:ring-1 focus:ring-accent cursor-pointer"
1816
+ >
1817
+ <option value="">Auto (Sonnet)</option>
1818
+ {isChatLocal ? (
1819
+ <>
1820
+ {chatModels.filter(m => m.source === 'ollama').length > 0 && (
1821
+ <optgroup label="Ollama">{chatModels.filter(m => m.source === 'ollama').map(m => <option key={m.id} value={m.id}>{m.name}</option>)}</optgroup>
1822
+ )}
1823
+ {chatModels.filter(m => m.source === 'gguf').length > 0 && (
1824
+ <optgroup label="Downloaded GGUFs">{chatModels.filter(m => m.source === 'gguf').map(m => <option key={m.id} value={m.id}>{m.name}{m.hasRuntime ? '' : ' (no runtime)'}</option>)}</optgroup>
1825
+ )}
1826
+ {chatModels.filter(m => m.source === 'runtime').length > 0 && (
1827
+ <optgroup label="Runtime Models">{chatModels.filter(m => m.source === 'runtime').map(m => <option key={m.id} value={m.id}>{m.name}</option>)}</optgroup>
1828
+ )}
1829
+ </>
1830
+ ) : (
1831
+ chatModels.map((m) => {
1832
+ const id = typeof m === 'string' ? m : m.id;
1833
+ const name = typeof m === 'string' ? m : m.name || m.id;
1834
+ return <option key={id} value={id}>{name}</option>;
1835
+ })
1836
+ )}
1837
+ </select>
1838
+ );
1839
+ })()}
1853
1840
  </div>
1854
1841
  </ConfigCard>
1855
1842