groove-dev 0.27.143 → 0.27.145

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 (251) hide show
  1. package/CLAUDE.md +0 -7
  2. package/node_modules/@groove-dev/cli/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/package.json +1 -1
  4. package/node_modules/@groove-dev/daemon/src/api.js +1086 -6532
  5. package/node_modules/@groove-dev/daemon/src/conversations.js +18 -48
  6. package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
  7. package/node_modules/@groove-dev/daemon/src/index.js +3 -0
  8. package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
  9. package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
  10. package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
  11. package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
  12. package/node_modules/@groove-dev/daemon/src/process.js +2 -2
  13. package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
  14. package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
  15. package/node_modules/@groove-dev/daemon/src/routes/agents.js +812 -0
  16. package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
  17. package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
  18. package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
  19. package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
  20. package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
  21. package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
  22. package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
  23. package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
  24. package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
  25. package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
  26. package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
  27. package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
  28. package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
  29. package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
  30. package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
  31. package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
  32. package/node_modules/@groove-dev/gui/dist/assets/index-Bxc0gU06.js +1006 -0
  33. package/node_modules/@groove-dev/gui/dist/assets/index-C0pztKBn.css +1 -0
  34. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  35. package/node_modules/@groove-dev/gui/package.json +1 -1
  36. package/node_modules/@groove-dev/gui/src/{app.jsx → App.jsx} +0 -2
  37. package/node_modules/@groove-dev/gui/src/app.css +35 -0
  38. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
  39. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +210 -112
  40. package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
  41. package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +2 -70
  42. package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
  43. package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
  44. package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
  45. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
  46. package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
  47. package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
  48. package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +2 -0
  49. package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +68 -66
  50. package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +4 -8
  51. package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
  52. package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
  53. package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
  54. package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  55. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
  56. package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
  57. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
  58. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  59. package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
  60. package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +39 -31
  61. package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
  62. package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
  63. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +200 -18
  64. package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +17 -14
  65. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +335 -152
  66. package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +10 -8
  67. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
  68. package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
  69. package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
  70. package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
  71. package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
  72. package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
  73. package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
  74. package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +8 -8
  75. package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
  76. package/node_modules/@groove-dev/gui/src/lib/status.js +25 -24
  77. package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
  78. package/node_modules/@groove-dev/gui/src/stores/groove.js +51 -3144
  79. package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
  80. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +459 -0
  81. package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
  82. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +226 -0
  83. package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
  84. package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
  85. package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
  86. package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
  87. package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
  88. package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
  89. package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
  90. package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
  91. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
  92. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
  93. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +54 -12
  94. package/node_modules/@groove-dev/gui/src/views/models.jsx +419 -496
  95. package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
  96. package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
  97. package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
  98. package/node_modules/axios/CHANGELOG.md +260 -0
  99. package/node_modules/axios/README.md +595 -223
  100. package/node_modules/axios/dist/axios.js +1460 -1090
  101. package/node_modules/axios/dist/axios.js.map +1 -1
  102. package/node_modules/axios/dist/axios.min.js +3 -3
  103. package/node_modules/axios/dist/axios.min.js.map +1 -1
  104. package/node_modules/axios/dist/browser/axios.cjs +1560 -1132
  105. package/node_modules/axios/dist/browser/axios.cjs.map +1 -1
  106. package/node_modules/axios/dist/esm/axios.js +1557 -1128
  107. package/node_modules/axios/dist/esm/axios.js.map +1 -1
  108. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  109. package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  110. package/node_modules/axios/dist/node/axios.cjs +1594 -1057
  111. package/node_modules/axios/dist/node/axios.cjs.map +1 -1
  112. package/node_modules/axios/index.d.cts +40 -41
  113. package/node_modules/axios/index.d.ts +151 -227
  114. package/node_modules/axios/index.js +2 -0
  115. package/node_modules/axios/lib/adapters/adapters.js +4 -2
  116. package/node_modules/axios/lib/adapters/fetch.js +147 -16
  117. package/node_modules/axios/lib/adapters/http.js +306 -58
  118. package/node_modules/axios/lib/adapters/xhr.js +6 -2
  119. package/node_modules/axios/lib/core/Axios.js +7 -3
  120. package/node_modules/axios/lib/core/AxiosError.js +120 -34
  121. package/node_modules/axios/lib/core/AxiosHeaders.js +27 -25
  122. package/node_modules/axios/lib/core/buildFullPath.js +1 -1
  123. package/node_modules/axios/lib/core/dispatchRequest.js +19 -7
  124. package/node_modules/axios/lib/core/mergeConfig.js +21 -4
  125. package/node_modules/axios/lib/core/settle.js +7 -11
  126. package/node_modules/axios/lib/defaults/index.js +14 -9
  127. package/node_modules/axios/lib/env/data.js +1 -1
  128. package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +1 -2
  129. package/node_modules/axios/lib/helpers/buildURL.js +1 -1
  130. package/node_modules/axios/lib/helpers/cookies.js +14 -2
  131. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +28 -1
  132. package/node_modules/axios/lib/helpers/formDataToJSON.js +3 -1
  133. package/node_modules/axios/lib/helpers/formDataToStream.js +3 -2
  134. package/node_modules/axios/lib/helpers/parseProtocol.js +1 -1
  135. package/node_modules/axios/lib/helpers/progressEventReducer.js +5 -5
  136. package/node_modules/axios/lib/helpers/resolveConfig.js +54 -18
  137. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +74 -2
  138. package/node_modules/axios/lib/helpers/toFormData.js +10 -2
  139. package/node_modules/axios/lib/helpers/validator.js +3 -1
  140. package/node_modules/axios/lib/utils.js +33 -21
  141. package/node_modules/axios/package.json +17 -24
  142. package/node_modules/follow-redirects/README.md +7 -5
  143. package/node_modules/follow-redirects/index.js +24 -1
  144. package/node_modules/follow-redirects/package.json +1 -1
  145. package/package.json +1 -1
  146. package/packages/cli/package.json +1 -1
  147. package/packages/daemon/package.json +1 -1
  148. package/packages/daemon/src/api.js +1086 -6532
  149. package/packages/daemon/src/conversations.js +18 -48
  150. package/packages/daemon/src/gateways/manager.js +35 -1
  151. package/packages/daemon/src/index.js +3 -0
  152. package/packages/daemon/src/journalist.js +23 -13
  153. package/packages/daemon/src/mlx-server.js +365 -0
  154. package/packages/daemon/src/model-lab.js +308 -12
  155. package/packages/daemon/src/pm.js +1 -1
  156. package/packages/daemon/src/process.js +2 -2
  157. package/packages/daemon/src/providers/local.js +36 -8
  158. package/packages/daemon/src/registry.js +21 -5
  159. package/packages/daemon/src/routes/agents.js +812 -0
  160. package/packages/daemon/src/routes/coordination.js +318 -0
  161. package/packages/daemon/src/routes/files.js +751 -0
  162. package/packages/daemon/src/routes/integrations.js +485 -0
  163. package/packages/daemon/src/routes/network.js +1784 -0
  164. package/packages/daemon/src/routes/providers.js +755 -0
  165. package/packages/daemon/src/routes/schedules.js +110 -0
  166. package/packages/daemon/src/routes/teams.js +650 -0
  167. package/packages/daemon/src/scheduler.js +456 -24
  168. package/packages/daemon/src/teams.js +1 -1
  169. package/packages/daemon/src/validate.js +38 -1
  170. package/packages/daemon/templates/mlx-setup.json +12 -0
  171. package/packages/daemon/templates/tgi-setup.json +1 -1
  172. package/packages/daemon/templates/vllm-setup.json +1 -1
  173. package/packages/gui/dist/assets/index-Bxc0gU06.js +1006 -0
  174. package/packages/gui/dist/assets/index-C0pztKBn.css +1 -0
  175. package/packages/gui/dist/index.html +2 -2
  176. package/packages/gui/package.json +1 -1
  177. package/packages/gui/src/{app.jsx → App.jsx} +0 -2
  178. package/packages/gui/src/app.css +35 -0
  179. package/packages/gui/src/components/agents/agent-config.jsx +1 -128
  180. package/packages/gui/src/components/agents/agent-feed.jsx +210 -112
  181. package/packages/gui/src/components/agents/agent-node.jsx +8 -13
  182. package/packages/gui/src/components/agents/agent-panel.jsx +2 -70
  183. package/packages/gui/src/components/agents/code-review.jsx +159 -122
  184. package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
  185. package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
  186. package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
  187. package/packages/gui/src/components/automations/automation-card.jsx +274 -0
  188. package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
  189. package/packages/gui/src/components/chat/chat-header.jsx +2 -0
  190. package/packages/gui/src/components/chat/chat-input.jsx +68 -66
  191. package/packages/gui/src/components/chat/chat-view.jsx +4 -8
  192. package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
  193. package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
  194. package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
  195. package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  196. package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
  197. package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
  198. package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
  199. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  200. package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
  201. package/packages/gui/src/components/lab/chat-playground.jsx +39 -31
  202. package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
  203. package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
  204. package/packages/gui/src/components/lab/parameter-panel.jsx +200 -18
  205. package/packages/gui/src/components/lab/preset-manager.jsx +17 -14
  206. package/packages/gui/src/components/lab/runtime-config.jsx +335 -152
  207. package/packages/gui/src/components/lab/system-prompt-editor.jsx +10 -8
  208. package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
  209. package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
  210. package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
  211. package/packages/gui/src/components/network/network-health.jsx +2 -2
  212. package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
  213. package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
  214. package/packages/gui/src/components/ui/sheet.jsx +5 -2
  215. package/packages/gui/src/components/ui/slider.jsx +8 -8
  216. package/packages/gui/src/lib/cron.js +64 -0
  217. package/packages/gui/src/lib/status.js +25 -24
  218. package/packages/gui/src/lib/theme-hex.js +1 -0
  219. package/packages/gui/src/stores/groove.js +51 -3144
  220. package/packages/gui/src/stores/helpers.js +10 -0
  221. package/packages/gui/src/stores/slices/agents-slice.js +459 -0
  222. package/packages/gui/src/stores/slices/automations-slice.js +96 -0
  223. package/packages/gui/src/stores/slices/chat-slice.js +226 -0
  224. package/packages/gui/src/stores/slices/editor-slice.js +285 -0
  225. package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
  226. package/packages/gui/src/stores/slices/network-slice.js +361 -0
  227. package/packages/gui/src/stores/slices/preview-slice.js +109 -0
  228. package/packages/gui/src/stores/slices/providers-slice.js +897 -0
  229. package/packages/gui/src/stores/slices/teams-slice.js +413 -0
  230. package/packages/gui/src/stores/slices/ui-slice.js +98 -0
  231. package/packages/gui/src/views/agents.jsx +5 -5
  232. package/packages/gui/src/views/dashboard.jsx +12 -13
  233. package/packages/gui/src/views/marketplace.jsx +191 -3
  234. package/packages/gui/src/views/model-lab.jsx +54 -12
  235. package/packages/gui/src/views/models.jsx +419 -496
  236. package/packages/gui/src/views/network.jsx +3 -3
  237. package/packages/gui/src/views/settings.jsx +81 -94
  238. package/packages/gui/src/views/teams.jsx +40 -483
  239. package/SECURITY_SWEEP.md +0 -228
  240. package/TRAINING_DATA_v4.md +0 -6
  241. package/node_modules/@groove-dev/gui/dist/assets/index-CCVvAoQn.css +0 -1
  242. package/node_modules/@groove-dev/gui/dist/assets/index-DGIv_TRm.js +0 -984
  243. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -379
  244. package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
  245. package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
  246. package/packages/gui/dist/assets/index-CCVvAoQn.css +0 -1
  247. package/packages/gui/dist/assets/index-DGIv_TRm.js +0 -984
  248. package/packages/gui/src/components/agents/agent-chat.jsx +0 -379
  249. package/packages/gui/src/views/preview.jsx +0 -6
  250. package/packages/gui/src/views/subscription-panel.jsx +0 -327
  251. package/test.py +0 -571
@@ -0,0 +1,10 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+
3
+ export function loadJSON(key, fallback = {}) {
4
+ try { return JSON.parse(localStorage.getItem(key) || JSON.stringify(fallback)); }
5
+ catch { return fallback; }
6
+ }
7
+
8
+ export function persistJSON(key, value) {
9
+ try { localStorage.setItem(key, JSON.stringify(value)); } catch { /* quota */ }
10
+ }
@@ -0,0 +1,459 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+
3
+ import { api } from '../../lib/api';
4
+ import { loadJSON, persistJSON } from '../helpers.js';
5
+
6
+ export const createAgentsSlice = (set, get) => ({
7
+ // ── Agent data ────────────────────────────────────────────
8
+ agents: [],
9
+ activityLog: loadJSON('groove:activityLog'),
10
+ chatHistory: loadJSON('groove:chatHistory'),
11
+ chatInputs: {}, // Per-agent draft input text — persists across tab switches
12
+ tokenTimeline: {},
13
+
14
+ // Track which agents are thinking (sent a message, waiting for response)
15
+ thinkingAgents: new Set(),
16
+
17
+ // ── Workspace Mode ────────────────────────────────────────
18
+ workspaceMode: localStorage.getItem('groove:workspaceMode') === 'true',
19
+ workspaceAgentId: null,
20
+ workspaceReviewMode: false,
21
+ workspaceReviewFiles: [],
22
+
23
+ // ── Keeper (tagged memory) ─────────────────────────────────
24
+ keeperItems: [],
25
+ keeperTree: [],
26
+ keeperEditing: null, // { tag, content, isNew, readOnly } — drives the editor modal
27
+ keeperInstructOpen: false,
28
+
29
+ // ── Agent Actions ─────────────────────────────────────────
30
+
31
+ selectAgent(id) {
32
+ const tid = get().activeTeamId;
33
+ const match = get().agents.find((a) => a.id === id);
34
+ if (tid && match && match.teamId && match.teamId !== tid) return;
35
+ const panel = { type: 'agent', agentId: id };
36
+ set((s) => ({ detailPanel: panel, teamDetailPanels: { ...s.teamDetailPanels, [tid]: panel } }));
37
+ },
38
+
39
+ async spawnAgent(config) {
40
+ try {
41
+ const teamId = get().activeTeamId;
42
+ const agent = await api.post('/agents', { ...config, teamId });
43
+ get().addToast('success', `Spawned ${agent.name}`);
44
+ return agent;
45
+ } catch (err) {
46
+ let detail = err.message;
47
+ if (detail?.includes('workingDir must be within project directory')) {
48
+ const projDir = get().projectDir || 'unknown';
49
+ const workDir = config.workingDir || 'default';
50
+ detail = `workingDir "${workDir}" is outside project directory "${projDir}". Change the project directory or pick a subfolder within it.`;
51
+ }
52
+ get().addToast('error', 'Spawn failed', detail);
53
+ throw err;
54
+ }
55
+ },
56
+
57
+ async killAgent(id, purge = false) {
58
+ try {
59
+ await api.delete(`/agents/${encodeURIComponent(id)}?purge=${purge}`);
60
+ if (purge) {
61
+ set((s) => {
62
+ const chatHistory = { ...s.chatHistory };
63
+ const activityLog = { ...s.activityLog };
64
+ const tokenTimeline = { ...s.tokenTimeline };
65
+ delete chatHistory[id];
66
+ delete activityLog[id];
67
+ delete tokenTimeline[id];
68
+ persistJSON('groove:chatHistory', chatHistory);
69
+ persistJSON('groove:activityLog', activityLog);
70
+ return { chatHistory, activityLog, tokenTimeline };
71
+ });
72
+ }
73
+ } catch (err) {
74
+ get().addToast('error', 'Kill failed', err.message);
75
+ }
76
+ },
77
+
78
+ async rotateAgent(id) {
79
+ try {
80
+ return await api.post(`/agents/${encodeURIComponent(id)}/rotate`);
81
+ } catch (err) {
82
+ get().addToast('error', 'Rotation failed', err.message);
83
+ throw err;
84
+ }
85
+ },
86
+
87
+ // ── Chat ──────────────────────────────────────────────────
88
+
89
+ addChatMessage(agentId, from, text, isQuery = false) {
90
+ set((s) => {
91
+ const history = { ...s.chatHistory };
92
+ if (!history[agentId]) history[agentId] = [];
93
+ history[agentId] = [...history[agentId].slice(-100), { from, text, timestamp: Date.now(), isQuery }];
94
+ persistJSON('groove:chatHistory', history);
95
+ return { chatHistory: history };
96
+ });
97
+ },
98
+
99
+ async stopAgent(id) {
100
+ try {
101
+ await api.post(`/agents/${encodeURIComponent(id)}/stop`);
102
+ // Clear thinking indicator
103
+ set((s) => {
104
+ const next = new Set(s.thinkingAgents);
105
+ next.delete(id);
106
+ return { thinkingAgents: next };
107
+ });
108
+ get().addToast('info', 'Stopped agent');
109
+ } catch (err) {
110
+ get().addToast('error', 'Stop failed', err.message);
111
+ }
112
+ },
113
+
114
+ async instructAgent(id, message) {
115
+ // ── Keeper command interception ─────────────────────────
116
+ const keeperCmd = message.match(/\[(save|append|update|delete|view|doc|link|read|instruct)\]/i);
117
+ if (keeperCmd) {
118
+ const handled = await get()._handleKeeperCommand(id, message, keeperCmd[1].toLowerCase());
119
+ if (handled === true) return { status: 'keeper_handled' };
120
+ if (handled?.passthrough) {
121
+ message = handled.passthrough;
122
+ }
123
+ }
124
+
125
+ get().addChatMessage(id, 'user', message, false);
126
+ set((s) => ({ thinkingAgents: new Set([...s.thinkingAgents, id]) }));
127
+
128
+ // Auto-attach active file context when in workspace mode
129
+ let enriched = message;
130
+ if (get().workspaceMode && get().workspaceAgentId === id && get().editorActiveFile) {
131
+ const filePath = get().editorActiveFile;
132
+ enriched = `[Active file: ${filePath}]\n\n${message}`;
133
+ }
134
+
135
+ const snapshot = {
136
+ chatHistory: [...(get().chatHistory[id] || [])],
137
+ activityLog: [...(get().activityLog[id] || [])],
138
+ tokenTimeline: [...(get().tokenTimeline[id] || [])],
139
+ };
140
+
141
+ try {
142
+ const data = await api.post(`/agents/${encodeURIComponent(id)}/instruct`, { message: enriched });
143
+
144
+ if (data.status === 'message_sent') {
145
+ return data;
146
+ }
147
+ if (data.status === 'message_queued') {
148
+ set((s) => {
149
+ const next = new Set(s.thinkingAgents);
150
+ next.delete(id);
151
+ return { thinkingAgents: next };
152
+ });
153
+ return data;
154
+ }
155
+
156
+ // CLI agent: was stopped + resumed/rotated — transfer state to new agent ID
157
+ const newAgent = data;
158
+ for (const key of ['chatHistory', 'activityLog', 'tokenTimeline']) {
159
+ if (snapshot[key]?.length) {
160
+ set((s) => ({ [key]: { ...s[key], [newAgent.id]: [...snapshot[key]] } }));
161
+ }
162
+ }
163
+ set((s) => {
164
+ const next = new Set(s.thinkingAgents);
165
+ next.delete(id);
166
+ next.add(newAgent.id);
167
+ return { thinkingAgents: next };
168
+ });
169
+ if (get().chatHistory[newAgent.id]?.length) persistJSON('groove:chatHistory', get().chatHistory);
170
+ if (get().activityLog[newAgent.id]?.length) persistJSON('groove:activityLog', get().activityLog);
171
+ if (get().labAssistantAgentId === id) {
172
+ localStorage.setItem('groove:labAssistantAgentId', newAgent.id);
173
+ set({ labAssistantAgentId: newAgent.id });
174
+ } else {
175
+ get().selectAgent(newAgent.id);
176
+ }
177
+ return newAgent;
178
+ } catch (err) {
179
+ set((s) => {
180
+ const next = new Set(s.thinkingAgents);
181
+ next.delete(id);
182
+ return { thinkingAgents: next };
183
+ });
184
+ get().addChatMessage(id, 'system', `failed: ${err.message}`);
185
+ throw err;
186
+ }
187
+ },
188
+
189
+ async queryAgent(id, message) {
190
+ get().addChatMessage(id, 'user', message, true);
191
+ try {
192
+ const data = await api.post(`/agents/${encodeURIComponent(id)}/query`, { message });
193
+ get().addChatMessage(id, 'agent', data.response);
194
+ return data;
195
+ } catch (err) {
196
+ get().addChatMessage(id, 'system', `query failed: ${err.message}`);
197
+ throw err;
198
+ }
199
+ },
200
+
201
+ // ── Workspace Mode ────────────────────────────────────────
202
+
203
+ setWorkspaceMode(on) {
204
+ set({ workspaceMode: on });
205
+ localStorage.setItem('groove:workspaceMode', String(on));
206
+ if (on) {
207
+ const teamAgents = get().agents.filter((a) => a.teamId === get().activeTeamId);
208
+ const current = get().workspaceAgentId;
209
+ const belongsToTeam = current && teamAgents.some((a) => a.id === current);
210
+ if (!belongsToTeam) {
211
+ const selected = get().detailPanel?.type === 'agent' ? get().detailPanel.agentId : null;
212
+ const selectedInTeam = selected && teamAgents.some((a) => a.id === selected);
213
+ const running = teamAgents.find((a) => a.status === 'running');
214
+ set({ workspaceAgentId: (selectedInTeam ? selected : null) || running?.id || teamAgents[0]?.id || null });
215
+ }
216
+ const agentId = get().workspaceAgentId;
217
+ if (agentId) get().selectAgent(agentId);
218
+ }
219
+ },
220
+
221
+ setWorkspaceAgent(id) {
222
+ set({ workspaceAgentId: id });
223
+ if (id) get().selectAgent(id);
224
+ },
225
+
226
+ async toggleReviewMode() {
227
+ const st = get();
228
+ if (st.workspaceReviewMode) {
229
+ set({ workspaceReviewMode: false, workspaceReviewFiles: [] });
230
+ return;
231
+ }
232
+ const agentId = st.workspaceAgentId;
233
+ if (!agentId) return;
234
+ try {
235
+ const res = await api.get(`/agents/${agentId}/files-touched`);
236
+ const touched = res.data || [];
237
+ const files = touched
238
+ .filter((f) => f.writes > 0)
239
+ .map((f) => ({ path: f.path, status: 'pending', comment: '' }));
240
+ set({ workspaceReviewMode: true, workspaceReviewFiles: files });
241
+ } catch (err) {
242
+ console.error('Failed to fetch touched files for review:', err);
243
+ }
244
+ },
245
+
246
+ approveFile(path) {
247
+ set((s) => ({
248
+ workspaceReviewFiles: s.workspaceReviewFiles.map((f) =>
249
+ f.path === path ? { ...f, status: 'approved' } : f,
250
+ ),
251
+ }));
252
+ },
253
+
254
+ rejectFile(path) {
255
+ set((s) => ({
256
+ workspaceReviewFiles: s.workspaceReviewFiles.map((f) =>
257
+ f.path === path ? { ...f, status: 'rejected' } : f,
258
+ ),
259
+ }));
260
+ },
261
+
262
+ commentFile(path, comment) {
263
+ set((s) => ({
264
+ workspaceReviewFiles: s.workspaceReviewFiles.map((f) =>
265
+ f.path === path ? { ...f, comment } : f,
266
+ ),
267
+ }));
268
+ },
269
+
270
+ // ── Keeper (tagged memory) ────────────────────────────────
271
+
272
+ async fetchKeeperItems() {
273
+ try {
274
+ const data = await api.get('/keeper');
275
+ const treeData = await api.get('/keeper/tree');
276
+ set({ keeperItems: data.items || [], keeperTree: treeData.tree || [] });
277
+ } catch { /* ignore */ }
278
+ },
279
+
280
+ async saveKeeperItem(tag, content) {
281
+ try {
282
+ const item = await api.post('/keeper', { tag, content });
283
+ get().fetchKeeperItems();
284
+ get().addToast('success', `Saved #${item.tag}`);
285
+ return item;
286
+ } catch (err) {
287
+ get().addToast('error', 'Failed to save memory', err.message);
288
+ throw err;
289
+ }
290
+ },
291
+
292
+ async appendKeeperItem(tag, content) {
293
+ try {
294
+ const item = await api.post('/keeper/append', { tag, content });
295
+ get().fetchKeeperItems();
296
+ get().addToast('success', `Appended to #${item.tag}`);
297
+ return item;
298
+ } catch (err) {
299
+ get().addToast('error', 'Failed to append', err.message);
300
+ throw err;
301
+ }
302
+ },
303
+
304
+ async updateKeeperItem(tag, content) {
305
+ try {
306
+ const item = await api.patch(`/keeper/${tag}`, { content });
307
+ get().fetchKeeperItems();
308
+ get().addToast('success', `Updated #${item.tag}`);
309
+ return item;
310
+ } catch (err) {
311
+ get().addToast('error', 'Failed to update memory', err.message);
312
+ throw err;
313
+ }
314
+ },
315
+
316
+ async deleteKeeperItem(tag) {
317
+ try {
318
+ await api.delete(`/keeper/${tag}`);
319
+ get().fetchKeeperItems();
320
+ get().addToast('success', `Deleted #${tag}`);
321
+ } catch (err) {
322
+ get().addToast('error', 'Failed to delete memory', err.message);
323
+ }
324
+ },
325
+
326
+ async getKeeperItem(tag) {
327
+ try {
328
+ return await api.get(`/keeper/${tag}`);
329
+ } catch {
330
+ return null;
331
+ }
332
+ },
333
+
334
+ async searchKeeper(query) {
335
+ try {
336
+ const data = await api.get(`/keeper/search?q=${encodeURIComponent(query)}`);
337
+ return data.results || [];
338
+ } catch {
339
+ return [];
340
+ }
341
+ },
342
+
343
+ setKeeperEditing(editing) {
344
+ set({ keeperEditing: editing });
345
+ },
346
+
347
+ async _handleKeeperCommand(agentId, message, command) {
348
+ const rest = message.replace(/\[\w+[-\w]*\]/i, '').trim();
349
+ const tags = (rest.match(/#[\w/.-]+/g) || []).map(t => t.replace(/^#/, ''));
350
+
351
+ const addSystemMsg = (text) => {
352
+ get().addChatMessage(agentId, 'system', text);
353
+ };
354
+
355
+ try {
356
+ switch (command) {
357
+ case 'instruct': {
358
+ set({ keeperInstructOpen: true });
359
+ return true;
360
+ }
361
+
362
+ case 'save': {
363
+ if (tags.length === 0) { addSystemMsg('Usage: save #tag your message here'); return true; }
364
+ const content = rest.replace(/#[\w/.-]+/g, '').trim();
365
+ if (!content) { addSystemMsg('Usage: save #tag your message here'); return true; }
366
+ await get().saveKeeperItem(tags[0], content);
367
+ addSystemMsg(`Saved to #${tags[0]}`);
368
+ return { passthrough: content };
369
+ }
370
+
371
+ case 'append': {
372
+ if (tags.length === 0) { addSystemMsg('Usage: append #tag content to add'); return true; }
373
+ const content = rest.replace(/#[\w/.-]+/g, '').trim();
374
+ if (!content) { addSystemMsg('Usage: append #tag content to add'); return true; }
375
+ await get().appendKeeperItem(tags[0], content);
376
+ addSystemMsg(`Appended to #${tags[0]}`);
377
+ return { passthrough: content };
378
+ }
379
+
380
+ case 'update': {
381
+ if (tags.length === 0) { addSystemMsg('Usage: [update] #tag'); return true; }
382
+ get().addChatMessage(agentId, 'user', message, false);
383
+ const existing = await get().getKeeperItem(tags[0]);
384
+ set({ keeperEditing: { tag: tags[0], content: existing?.content || '', isNew: !existing } });
385
+ return true;
386
+ }
387
+
388
+ case 'delete': {
389
+ if (tags.length === 0) { addSystemMsg('Usage: [delete] #tag'); return true; }
390
+ get().addChatMessage(agentId, 'user', message, false);
391
+ await get().deleteKeeperItem(tags[0]);
392
+ addSystemMsg(`Deleted #${tags[0]}`);
393
+ return true;
394
+ }
395
+
396
+ case 'view': {
397
+ if (tags.length === 0) { addSystemMsg('Usage: [view] #tag'); return true; }
398
+ get().addChatMessage(agentId, 'user', message, false);
399
+ const item = await get().getKeeperItem(tags[0]);
400
+ if (item) {
401
+ set({ keeperEditing: { tag: tags[0], content: item.content, isNew: false, readOnly: true } });
402
+ } else {
403
+ addSystemMsg(`#${tags[0]} not found`);
404
+ }
405
+ return true;
406
+ }
407
+
408
+ case 'read': {
409
+ if (tags.length === 0) { addSystemMsg('Usage: [read] #tag1 #tag2 ...'); return true; }
410
+ const userText = rest.replace(/#[\w/.-]+/g, '').trim();
411
+ get().addChatMessage(agentId, 'user', message, false);
412
+ const readBrief = await api.post('/keeper/pull', { tags });
413
+ if (readBrief?.brief) {
414
+ const memoryBlock = `\n\n---\nContext from memories (${tags.map(t => '#' + t).join(', ')}):\n\n${readBrief.brief}`;
415
+ set((s) => ({ thinkingAgents: new Set([...s.thinkingAgents, agentId]) }));
416
+ await api.post(`/agents/${encodeURIComponent(agentId)}/instruct`, {
417
+ message: userText ? `${userText}${memoryBlock}` : `Here is context from my tagged memories:\n\n${readBrief.brief}`,
418
+ });
419
+ addSystemMsg(`Sent ${tags.map(t => '#' + t).join(', ')} to agent`);
420
+ } else {
421
+ addSystemMsg(`No memories found for ${tags.map(t => '#' + t).join(', ')}`);
422
+ if (userText) {
423
+ set((s) => ({ thinkingAgents: new Set([...s.thinkingAgents, agentId]) }));
424
+ await api.post(`/agents/${encodeURIComponent(agentId)}/instruct`, { message: userText });
425
+ }
426
+ }
427
+ return true;
428
+ }
429
+
430
+ case 'doc': {
431
+ if (tags.length === 0) { addSystemMsg('Usage: [doc] #tag'); return true; }
432
+ get().addChatMessage(agentId, 'user', message, false);
433
+ addSystemMsg(`Generating doc for #${tags[0]}...`);
434
+ const history = get().chatHistory[agentId] || [];
435
+ const result = await api.post('/keeper/doc', { tag: tags[0], chatHistory: history, agentId });
436
+ if (result?.content) {
437
+ addSystemMsg(`Doc #${tags[0]} generated (${result.size}B)`);
438
+ set({ keeperEditing: { tag: tags[0], content: result.content, isNew: false } });
439
+ }
440
+ return true;
441
+ }
442
+
443
+ case 'link': {
444
+ const linkMatch = rest.match(/^((?:#[\w/.-]+\s*)+)\s+(.+)$/);
445
+ if (!linkMatch || tags.length === 0) { addSystemMsg('Usage: [link] #tag path/to/doc'); return true; }
446
+ const docPath = linkMatch[2].trim();
447
+ get().addChatMessage(agentId, 'user', message, false);
448
+ await api.post('/keeper/link', { tag: tags[0], docPath });
449
+ addSystemMsg(`Linked #${tags[0]} → ${docPath}`);
450
+ return true;
451
+ }
452
+ }
453
+ } catch (err) {
454
+ addSystemMsg(`Keeper error: ${err.message}`);
455
+ return true;
456
+ }
457
+ return false;
458
+ },
459
+ });
@@ -0,0 +1,96 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+
3
+ import { api } from '../../lib/api';
4
+
5
+ export const createAutomationsSlice = (set, get) => ({
6
+ automations: [],
7
+ automationWizardOpen: false,
8
+ editingAutomationId: null,
9
+ availableGateways: [],
10
+ availableIntegrations: [],
11
+
12
+ async fetchAutomations() {
13
+ try {
14
+ const data = await api.get('/schedules');
15
+ set({ automations: data.schedules || data || [] });
16
+ } catch { /* ignore */ }
17
+ },
18
+
19
+ async createAutomation(config) {
20
+ try {
21
+ await api.post('/schedules', config);
22
+ get().addToast('success', 'Automation created');
23
+ get().fetchAutomations();
24
+ set({ automationWizardOpen: false, editingAutomationId: null });
25
+ } catch (err) {
26
+ get().addToast('error', 'Failed to create automation', err.message);
27
+ }
28
+ },
29
+
30
+ async updateAutomation(id, updates) {
31
+ try {
32
+ await api.patch(`/schedules/${encodeURIComponent(id)}`, updates);
33
+ get().addToast('success', 'Automation updated');
34
+ get().fetchAutomations();
35
+ } catch (err) {
36
+ get().addToast('error', 'Failed to update automation', err.message);
37
+ }
38
+ },
39
+
40
+ async deleteAutomation(id) {
41
+ try {
42
+ await api.delete(`/schedules/${encodeURIComponent(id)}`);
43
+ get().addToast('info', 'Automation deleted');
44
+ get().fetchAutomations();
45
+ } catch (err) {
46
+ get().addToast('error', 'Failed to delete automation', err.message);
47
+ }
48
+ },
49
+
50
+ async toggleAutomation(id, currentEnabled) {
51
+ try {
52
+ const action = currentEnabled ? 'disable' : 'enable';
53
+ await api.post(`/schedules/${encodeURIComponent(id)}/${action}`);
54
+ get().fetchAutomations();
55
+ } catch (err) {
56
+ get().addToast('error', 'Failed to toggle automation', err.message);
57
+ }
58
+ },
59
+
60
+ async runAutomation(id) {
61
+ try {
62
+ await api.post(`/schedules/${encodeURIComponent(id)}/run`);
63
+ get().addToast('success', 'Automation triggered');
64
+ } catch (err) {
65
+ get().addToast('error', 'Failed to run automation', err.message);
66
+ }
67
+ },
68
+
69
+ async duplicateAutomation(id) {
70
+ try {
71
+ await api.post(`/schedules/${encodeURIComponent(id)}/duplicate`);
72
+ get().addToast('success', 'Automation duplicated');
73
+ get().fetchAutomations();
74
+ } catch (err) {
75
+ get().addToast('error', 'Failed to duplicate automation', err.message);
76
+ }
77
+ },
78
+
79
+ openAutomationWizard() { set({ automationWizardOpen: true }); },
80
+ closeAutomationWizard() { set({ automationWizardOpen: false, editingAutomationId: null }); },
81
+ setEditingAutomation(id) { set({ editingAutomationId: id }); },
82
+
83
+ async fetchGateways() {
84
+ try {
85
+ const data = await api.get('/gateways');
86
+ set({ availableGateways: data.gateways || data || [] });
87
+ } catch { /* ignore */ }
88
+ },
89
+
90
+ async fetchInstalledIntegrations() {
91
+ try {
92
+ const data = await api.get('/integrations/installed');
93
+ set({ availableIntegrations: data.integrations || data || [] });
94
+ } catch { /* ignore */ }
95
+ },
96
+ });