groove-dev 0.27.196 → 0.27.198

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 (106) hide show
  1. package/axom-integration/Screenshot_2026-07-25_at_5.25.30_PM.png +0 -0
  2. package/axom-integration/Screenshot_2026-07-25_at_5.33.26_PM.png +0 -0
  3. package/axom-integration/Screenshot_2026-07-25_at_6.11.53_PM.png +0 -0
  4. package/node_modules/@groove-dev/cli/package.json +1 -1
  5. package/node_modules/@groove-dev/daemon/package.json +1 -1
  6. package/node_modules/@groove-dev/daemon/src/api.js +5 -5
  7. package/node_modules/@groove-dev/daemon/src/axom-connector.js +340 -0
  8. package/node_modules/@groove-dev/daemon/src/axom-install.js +140 -0
  9. package/node_modules/@groove-dev/daemon/src/axom-server.js +229 -0
  10. package/node_modules/@groove-dev/daemon/src/chatstore.js +104 -0
  11. package/node_modules/@groove-dev/daemon/src/federation.js +6 -0
  12. package/node_modules/@groove-dev/daemon/src/index.js +14 -2
  13. package/node_modules/@groove-dev/daemon/src/innerchat-docs.js +8 -1
  14. package/node_modules/@groove-dev/daemon/src/innerchat-relay.js +89 -0
  15. package/node_modules/@groove-dev/daemon/src/innerchat.js +261 -1
  16. package/node_modules/@groove-dev/daemon/src/introducer.js +1 -1
  17. package/node_modules/@groove-dev/daemon/src/network-guard.js +56 -0
  18. package/node_modules/@groove-dev/daemon/src/process.js +2 -2
  19. package/node_modules/@groove-dev/daemon/src/providers/axom.js +66 -0
  20. package/node_modules/@groove-dev/daemon/src/providers/index.js +2 -0
  21. package/node_modules/@groove-dev/daemon/src/routes/agents.js +1 -175
  22. package/node_modules/@groove-dev/daemon/src/routes/axom.js +196 -0
  23. package/node_modules/@groove-dev/daemon/src/routes/chat-history.js +34 -0
  24. package/node_modules/@groove-dev/daemon/src/routes/innerchat.js +152 -20
  25. package/node_modules/@groove-dev/daemon/test/axom-connector.test.js +412 -0
  26. package/node_modules/@groove-dev/daemon/test/axom-server.test.js +187 -0
  27. package/node_modules/@groove-dev/daemon/test/chatstore.test.js +86 -0
  28. package/node_modules/@groove-dev/daemon/test/innerchat-relay.test.js +251 -0
  29. package/node_modules/@groove-dev/gui/dist/assets/index-RbtaI6l7.css +1 -0
  30. package/node_modules/@groove-dev/gui/dist/assets/index-b9dKN6cq.js +1074 -0
  31. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  32. package/node_modules/@groove-dev/gui/package.json +1 -1
  33. package/node_modules/@groove-dev/gui/src/App.jsx +2 -2
  34. package/node_modules/@groove-dev/gui/src/app.css +53 -0
  35. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +2 -2
  36. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +3 -3
  37. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +3 -3
  38. package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
  39. package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +1 -1
  40. package/node_modules/@groove-dev/gui/src/stores/groove.js +24 -163
  41. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +52 -2
  42. package/node_modules/@groove-dev/gui/src/stores/slices/axom-slice.js +197 -0
  43. package/node_modules/@groove-dev/gui/src/views/axom.jsx +1060 -0
  44. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +1 -1
  45. package/node_modules/@groove-dev/gui/src/views/settings.jsx +178 -64
  46. package/package.json +2 -2
  47. package/packages/cli/package.json +1 -1
  48. package/packages/daemon/package.json +1 -1
  49. package/packages/daemon/src/api.js +5 -5
  50. package/packages/daemon/src/axom-connector.js +340 -0
  51. package/packages/daemon/src/axom-install.js +140 -0
  52. package/packages/daemon/src/axom-server.js +229 -0
  53. package/packages/daemon/src/chatstore.js +104 -0
  54. package/packages/daemon/src/federation.js +6 -0
  55. package/packages/daemon/src/index.js +14 -2
  56. package/packages/daemon/src/innerchat-docs.js +8 -1
  57. package/packages/daemon/src/innerchat-relay.js +89 -0
  58. package/packages/daemon/src/innerchat.js +261 -1
  59. package/packages/daemon/src/introducer.js +1 -1
  60. package/packages/daemon/src/network-guard.js +56 -0
  61. package/packages/daemon/src/process.js +2 -2
  62. package/packages/daemon/src/providers/axom.js +66 -0
  63. package/packages/daemon/src/providers/index.js +2 -0
  64. package/packages/daemon/src/routes/agents.js +1 -175
  65. package/packages/daemon/src/routes/axom.js +196 -0
  66. package/packages/daemon/src/routes/chat-history.js +34 -0
  67. package/packages/daemon/src/routes/innerchat.js +152 -20
  68. package/packages/gui/dist/assets/index-RbtaI6l7.css +1 -0
  69. package/packages/gui/dist/assets/index-b9dKN6cq.js +1074 -0
  70. package/packages/gui/dist/index.html +2 -2
  71. package/packages/gui/package.json +1 -1
  72. package/packages/gui/src/App.jsx +2 -2
  73. package/packages/gui/src/app.css +53 -0
  74. package/packages/gui/src/components/agents/agent-config.jsx +2 -2
  75. package/packages/gui/src/components/agents/spawn-wizard.jsx +3 -3
  76. package/packages/gui/src/components/layout/activity-bar.jsx +3 -3
  77. package/packages/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
  78. package/packages/gui/src/components/layout/command-palette.jsx +1 -1
  79. package/packages/gui/src/stores/groove.js +24 -163
  80. package/packages/gui/src/stores/slices/agents-slice.js +52 -2
  81. package/packages/gui/src/stores/slices/axom-slice.js +197 -0
  82. package/packages/gui/src/views/axom.jsx +1060 -0
  83. package/packages/gui/src/views/marketplace.jsx +1 -1
  84. package/packages/gui/src/views/settings.jsx +178 -64
  85. package/node_modules/@groove-dev/daemon/src/conversations.js +0 -576
  86. package/node_modules/@groove-dev/gui/dist/assets/index-Cat5pJUx.css +0 -1
  87. package/node_modules/@groove-dev/gui/dist/assets/index-L8xSnvHj.js +0 -1070
  88. package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +0 -198
  89. package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +0 -367
  90. package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +0 -527
  91. package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +0 -212
  92. package/node_modules/@groove-dev/gui/src/components/chat/conversation-list.jsx +0 -163
  93. package/node_modules/@groove-dev/gui/src/components/chat/model-picker.jsx +0 -201
  94. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +0 -227
  95. package/node_modules/@groove-dev/gui/src/views/chat.jsx +0 -6
  96. package/packages/daemon/src/conversations.js +0 -576
  97. package/packages/gui/dist/assets/index-Cat5pJUx.css +0 -1
  98. package/packages/gui/dist/assets/index-L8xSnvHj.js +0 -1070
  99. package/packages/gui/src/components/chat/chat-header.jsx +0 -198
  100. package/packages/gui/src/components/chat/chat-input.jsx +0 -367
  101. package/packages/gui/src/components/chat/chat-messages.jsx +0 -527
  102. package/packages/gui/src/components/chat/chat-view.jsx +0 -212
  103. package/packages/gui/src/components/chat/conversation-list.jsx +0 -163
  104. package/packages/gui/src/components/chat/model-picker.jsx +0 -201
  105. package/packages/gui/src/stores/slices/chat-slice.js +0 -227
  106. package/packages/gui/src/views/chat.jsx +0 -6
@@ -581,7 +581,7 @@ function MyLibrary() {
581
581
  ];
582
582
 
583
583
  const emptyMessages = {
584
- all: 'No skills or integrations installed yet. Visit the Marketplace to get started.',
584
+ all: 'No skills or integrations installed yet. Browse the Integrations tab to get started.',
585
585
  skills: 'No skills installed.',
586
586
  integrations: 'No integrations installed.',
587
587
  };
@@ -52,6 +52,15 @@ const KEY_PLACEHOLDERS = {
52
52
  grok: 'xai-...',
53
53
  };
54
54
 
55
+ // installCommand is a string for most providers, but some (Ollama) return
56
+ // {command, alt, platform} — normalize so an object can never reach JSX as a
57
+ // child (React #31 white-screens the whole tab).
58
+ function installCommandText(installCommand) {
59
+ if (!installCommand) return null;
60
+ if (typeof installCommand === 'string') return installCommand;
61
+ return installCommand.command || null;
62
+ }
63
+
55
64
  function ProviderCard({ provider, onKeyChange }) {
56
65
  const [settingKey, setSettingKey] = useState(false);
57
66
  const [keyInput, setKeyInput] = useState('');
@@ -182,15 +191,15 @@ function ProviderCard({ provider, onKeyChange }) {
182
191
  </Button>
183
192
 
184
193
  {/* Manual install command */}
185
- {provider.installCommand && (
194
+ {installCommandText(provider.installCommand) && (
186
195
  <div className="space-y-1">
187
196
  <p className="text-2xs text-text-4 font-sans">Or install manually in your terminal:</p>
188
197
  <div className="flex items-center gap-1">
189
198
  <code className="flex-1 px-2 py-1.5 bg-surface-0 border border-border-subtle rounded text-2xs font-mono text-text-2 select-all">
190
- {provider.installCommand}
199
+ {installCommandText(provider.installCommand)}
191
200
  </code>
192
201
  <button
193
- onClick={() => { navigator.clipboard.writeText(provider.installCommand); addToast('success', 'Copied'); }}
202
+ onClick={() => { navigator.clipboard.writeText(installCommandText(provider.installCommand)); addToast('success', 'Copied'); }}
194
203
  className="p-1.5 text-text-4 hover:text-text-2 cursor-pointer"
195
204
  >
196
205
  <Copy size={10} />
@@ -1491,6 +1500,169 @@ function TrainingDataSection() {
1491
1500
  );
1492
1501
  }
1493
1502
 
1503
+ /* ── InnerChat Peers Section ──────────────────────────────── */
1504
+
1505
+ // Cross-daemon InnerChat: agents on this daemon can reach agents on a peer
1506
+ // daemon as `name@alias`. A peer is another Groove daemon you can already
1507
+ // reach (a tunnel-forwarded localhost port, a Tailscale address). Trust is
1508
+ // mutual — both machines must list each other, and signatures ride the
1509
+ // federation keypair — so this panel also surfaces THIS daemon's id to hand
1510
+ // to the other side.
1511
+ function InnerChatPeersSection() {
1512
+ const addToast = useGrooveStore((s) => s.addToast);
1513
+ const [peers, setPeers] = useState([]);
1514
+ const [ownId, setOwnId] = useState(null);
1515
+ const [loading, setLoading] = useState(true);
1516
+ const [adding, setAdding] = useState(false);
1517
+ const [form, setForm] = useState({ alias: '', url: '', daemonId: '' });
1518
+ const [busy, setBusy] = useState(false);
1519
+
1520
+ const load = () => {
1521
+ api.get('/innerchat/peers')
1522
+ .then((d) => setPeers(Array.isArray(d?.peers) ? d.peers : []))
1523
+ .catch(() => {})
1524
+ .finally(() => setLoading(false));
1525
+ api.get('/federation').then((d) => setOwnId(d?.id || null)).catch(() => {});
1526
+ };
1527
+ useEffect(() => { load(); }, []);
1528
+
1529
+ const canSubmit = form.alias.trim() && form.url.trim() && form.daemonId.trim();
1530
+
1531
+ async function addPeer() {
1532
+ if (!canSubmit) return;
1533
+ setBusy(true);
1534
+ try {
1535
+ const d = await api.post('/innerchat/peers', {
1536
+ alias: form.alias.trim(),
1537
+ url: form.url.trim(),
1538
+ daemonId: form.daemonId.trim().toLowerCase(),
1539
+ });
1540
+ setPeers(Array.isArray(d?.peers) ? d.peers : []);
1541
+ setForm({ alias: '', url: '', daemonId: '' });
1542
+ setAdding(false);
1543
+ addToast('success', `Peer "${form.alias.trim()}" added`);
1544
+ } catch (err) {
1545
+ addToast('error', 'Could not add peer', err.message);
1546
+ } finally {
1547
+ setBusy(false);
1548
+ }
1549
+ }
1550
+
1551
+ async function removePeer(alias) {
1552
+ try {
1553
+ const d = await api.delete(`/innerchat/peers/${encodeURIComponent(alias)}`);
1554
+ setPeers(Array.isArray(d?.peers) ? d.peers : []);
1555
+ addToast('info', `Removed peer "${alias}"`);
1556
+ } catch (err) {
1557
+ addToast('error', 'Remove failed', err.message);
1558
+ }
1559
+ }
1560
+
1561
+ return (
1562
+ <div>
1563
+ <div className="flex items-center gap-2 mb-2.5 px-0.5">
1564
+ <span className="text-2xs font-semibold text-text-3 font-sans uppercase tracking-wider">InnerChat Peers</span>
1565
+ <div className="flex-1 h-px bg-border-subtle" />
1566
+ <span className="text-2xs text-text-4 font-sans">
1567
+ {peers.length} peer{peers.length !== 1 ? 's' : ''} · address as <code className="text-text-3">name@alias</code>
1568
+ </span>
1569
+ </div>
1570
+
1571
+ <div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5 flex flex-col gap-3">
1572
+ {/* This daemon's identity — to hand to the peer machine */}
1573
+ <div className="flex items-start gap-2.5">
1574
+ <div className="w-6 h-6 rounded bg-indigo/10 flex items-center justify-center flex-shrink-0 mt-0.5">
1575
+ <Share2 size={12} className="text-indigo" />
1576
+ </div>
1577
+ <div className="flex-1 min-w-0">
1578
+ <div className="text-[13px] font-medium text-text-0 font-sans leading-tight">Cross-daemon agent chat</div>
1579
+ <div className="text-2xs text-text-4 font-sans leading-relaxed mt-0.5">
1580
+ Agents reach a peer's agents as <code className="text-text-3">name@alias</code>. Add the peer on both
1581
+ machines — each lists the other's URL and daemon id. This daemon's id:
1582
+ </div>
1583
+ <div className="flex items-center gap-1.5 mt-1.5">
1584
+ <code className="h-7 px-2 flex items-center bg-surface-0 border border-border-subtle rounded-md text-2xs font-mono text-text-2 truncate min-w-0">
1585
+ {ownId || '—'}
1586
+ </code>
1587
+ {ownId && (
1588
+ <Button variant="secondary" size="sm" onClick={() => { navigator.clipboard.writeText(ownId); addToast('success', 'Copied daemon id'); }} className="h-7 px-2 flex-shrink-0">
1589
+ <Copy size={11} />
1590
+ </Button>
1591
+ )}
1592
+ </div>
1593
+ </div>
1594
+ </div>
1595
+
1596
+ {/* Peer list */}
1597
+ {loading ? (
1598
+ <Skeleton className="h-10 rounded-md" />
1599
+ ) : peers.length > 0 ? (
1600
+ <div className="flex flex-col gap-1.5">
1601
+ {peers.map((p) => (
1602
+ <div key={p.alias} className="flex items-center gap-2.5 px-2.5 py-2 rounded-md bg-surface-0 border border-border-subtle">
1603
+ <MessageCircle size={12} className="text-indigo flex-shrink-0" />
1604
+ <Badge variant="secondary" className="font-mono flex-shrink-0">@{p.alias}</Badge>
1605
+ <code className="text-2xs font-mono text-text-2 truncate min-w-0 flex-1">{p.url}</code>
1606
+ <code className="text-2xs font-mono text-text-4 flex-shrink-0 hidden sm:inline" title={p.daemonId}>{String(p.daemonId).slice(0, 8)}…</code>
1607
+ <button
1608
+ onClick={() => removePeer(p.alias)}
1609
+ className="text-text-4 hover:text-danger transition-colors flex-shrink-0 cursor-pointer"
1610
+ title={`Remove ${p.alias}`}
1611
+ >
1612
+ <Trash2 size={12} />
1613
+ </button>
1614
+ </div>
1615
+ ))}
1616
+ </div>
1617
+ ) : (
1618
+ <div className="text-2xs text-text-4 font-sans px-1 py-1">No peers configured — add one to let agents talk across machines.</div>
1619
+ )}
1620
+
1621
+ {/* Add form */}
1622
+ {adding ? (
1623
+ <div className="flex flex-col gap-2 pt-1 border-t border-border-subtle">
1624
+ <div className="grid grid-cols-[1fr_2fr] gap-2">
1625
+ <input
1626
+ value={form.alias}
1627
+ onChange={(e) => setForm((f) => ({ ...f, alias: e.target.value }))}
1628
+ placeholder="alias (e.g. spark)"
1629
+ className="h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent"
1630
+ />
1631
+ <input
1632
+ value={form.url}
1633
+ onChange={(e) => setForm((f) => ({ ...f, url: e.target.value }))}
1634
+ placeholder="http://localhost:62686 (tunnel / Tailscale URL)"
1635
+ className="h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent"
1636
+ />
1637
+ </div>
1638
+ <input
1639
+ value={form.daemonId}
1640
+ onChange={(e) => setForm((f) => ({ ...f, daemonId: e.target.value }))}
1641
+ placeholder="peer daemon id (from the other machine's Settings)"
1642
+ className="h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent"
1643
+ />
1644
+ <div className="flex items-center justify-end gap-2">
1645
+ <Button variant="ghost" size="sm" onClick={() => { setAdding(false); setForm({ alias: '', url: '', daemonId: '' }); }} disabled={busy}>Cancel</Button>
1646
+ <Button variant="primary" size="sm" onClick={addPeer} disabled={!canSubmit || busy}>
1647
+ {busy ? <Loader2 size={12} className="animate-spin" /> : <Check size={12} />}
1648
+ Add Peer
1649
+ </Button>
1650
+ </div>
1651
+ </div>
1652
+ ) : (
1653
+ <button
1654
+ onClick={() => setAdding(true)}
1655
+ className="flex items-center justify-center gap-1.5 h-8 rounded-md border border-dashed border-border-subtle text-2xs font-sans text-text-3 hover:text-accent hover:border-accent/40 transition-colors cursor-pointer"
1656
+ >
1657
+ <Plus size={12} />
1658
+ Add Peer
1659
+ </button>
1660
+ )}
1661
+ </div>
1662
+ </div>
1663
+ );
1664
+ }
1665
+
1494
1666
  /* ── Early Access Section ─────────────────────────────────── */
1495
1667
 
1496
1668
  function EarlyAccessSection() {
@@ -1787,71 +1959,13 @@ export default function SettingsView() {
1787
1959
  </div>
1788
1960
  </ConfigCard>
1789
1961
 
1790
- <ConfigCard icon={MessageSquare} label="Default Chat Model" description="Provider and model for new chat conversations.">
1791
- <div className="space-y-2">
1792
- <select
1793
- value={config.defaultChatProvider || config.defaultProvider || 'claude-code'}
1794
- onChange={(e) => {
1795
- updateConfig('defaultChatProvider', e.target.value);
1796
- const prov = providers.find((p) => p.id === e.target.value);
1797
- const chatModels = (prov?.models || []).filter((m) => {
1798
- const id = (typeof m === 'string' ? m : m.id || '').toLowerCase();
1799
- return !id.includes('dall-e') && !id.includes('imagen') && !id.includes('image');
1800
- });
1801
- if (chatModels.length > 0) {
1802
- const first = typeof chatModels[0] === 'string' ? chatModels[0] : chatModels[0].id;
1803
- updateConfig('defaultChatModel', first);
1804
- }
1805
- }}
1806
- 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"
1807
- >
1808
- {visibleProviders.filter((p) => p.installed && (p.authType === 'local' || (p.authType === 'subscription' && p.authStatus?.authenticated) || p.hasKey)).map((p) => (
1809
- <option key={p.id} value={p.id}>{p.name}</option>
1810
- ))}
1811
- </select>
1812
- {(() => {
1813
- const chatProv = providers.find((p) => p.id === (config.defaultChatProvider || config.defaultProvider || 'claude-code'));
1814
- const chatModels = (chatProv?.models || []).filter((m) => {
1815
- const id = (typeof m === 'string' ? m : m.id || '').toLowerCase();
1816
- return !id.includes('dall-e') && !id.includes('imagen') && !id.includes('image');
1817
- });
1818
- const isChatLocal = (config.defaultChatProvider || config.defaultProvider || 'claude-code') === 'local';
1819
- return (
1820
- <select
1821
- value={config.defaultChatModel || ''}
1822
- onChange={(e) => updateConfig('defaultChatModel', e.target.value || null)}
1823
- 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"
1824
- >
1825
- <option value="">Auto (Sonnet)</option>
1826
- {isChatLocal ? (
1827
- <>
1828
- {chatModels.filter(m => m.source === 'ollama').length > 0 && (
1829
- <optgroup label="Ollama">{chatModels.filter(m => m.source === 'ollama').map(m => <option key={m.id} value={m.id}>{m.name}</option>)}</optgroup>
1830
- )}
1831
- {chatModels.filter(m => m.source === 'gguf').length > 0 && (
1832
- <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>
1833
- )}
1834
- {chatModels.filter(m => m.source === 'runtime').length > 0 && (
1835
- <optgroup label="Runtime Models">{chatModels.filter(m => m.source === 'runtime').map(m => <option key={m.id} value={m.id}>{m.name}</option>)}</optgroup>
1836
- )}
1837
- </>
1838
- ) : (
1839
- chatModels.map((m) => {
1840
- const id = typeof m === 'string' ? m : m.id;
1841
- const name = typeof m === 'string' ? m : m.name || m.id;
1842
- return <option key={id} value={id}>{name}</option>;
1843
- })
1844
- )}
1845
- </select>
1846
- );
1847
- })()}
1848
- </div>
1849
- </ConfigCard>
1850
-
1851
1962
  </div>
1852
1963
  </div>
1853
1964
  )}
1854
1965
 
1966
+ {/* ═══════ INNERCHAT PEERS ═══════ */}
1967
+ <InnerChatPeersSection />
1968
+
1855
1969
  {/* ═══════ EARLY ACCESS ═══════ */}
1856
1970
  <EarlyAccessSection />
1857
1971