groove-dev 0.27.139 → 0.27.141
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.
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/integrations-registry.json +12 -44
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +225 -16
- package/node_modules/@groove-dev/daemon/src/index.js +2 -0
- package/node_modules/@groove-dev/daemon/src/integrations.js +10 -0
- package/node_modules/@groove-dev/daemon/src/introducer.js +17 -1
- package/node_modules/@groove-dev/daemon/src/journalist.js +169 -0
- package/node_modules/@groove-dev/daemon/src/keeper.js +277 -0
- package/node_modules/@groove-dev/daemon/src/model-lab.js +11 -0
- package/node_modules/@groove-dev/daemon/src/process.js +76 -0
- package/node_modules/@groove-dev/daemon/src/validate.js +9 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-A4e1gIDh.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-P1hsM27-.js +8696 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/app.jsx +4 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +41 -17
- package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +10 -3
- package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +5 -4
- package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +160 -12
- package/node_modules/@groove-dev/gui/src/components/editor/ai-panel.jsx +77 -6
- package/node_modules/@groove-dev/gui/src/components/editor/file-tree.jsx +2 -86
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +15 -4
- package/node_modules/@groove-dev/gui/src/components/keeper/global-modals.jsx +177 -0
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +11 -6
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +152 -3
- package/node_modules/@groove-dev/gui/src/components/marketplace/integration-wizard.jsx +223 -18
- package/node_modules/@groove-dev/gui/src/stores/groove.js +302 -20
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +118 -60
- package/node_modules/@groove-dev/gui/src/views/editor.jsx +67 -219
- package/node_modules/@groove-dev/gui/src/views/memory.jsx +460 -0
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +1 -6
- package/node_modules/@groove-dev/gui/src/views/models.jsx +658 -565
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/integrations-registry.json +12 -44
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +225 -16
- package/packages/daemon/src/index.js +2 -0
- package/packages/daemon/src/integrations.js +10 -0
- package/packages/daemon/src/introducer.js +17 -1
- package/packages/daemon/src/journalist.js +169 -0
- package/packages/daemon/src/keeper.js +277 -0
- package/packages/daemon/src/model-lab.js +11 -0
- package/packages/daemon/src/process.js +76 -0
- package/packages/daemon/src/validate.js +9 -0
- package/packages/gui/dist/assets/index-A4e1gIDh.css +1 -0
- package/packages/gui/dist/assets/index-P1hsM27-.js +8696 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/app.jsx +4 -0
- package/packages/gui/src/components/agents/agent-chat.jsx +41 -17
- package/packages/gui/src/components/agents/agent-file-tree.jsx +10 -3
- package/packages/gui/src/components/agents/code-review.jsx +5 -4
- package/packages/gui/src/components/agents/workspace-mode.jsx +160 -12
- package/packages/gui/src/components/editor/ai-panel.jsx +77 -6
- package/packages/gui/src/components/editor/file-tree.jsx +2 -86
- package/packages/gui/src/components/editor/terminal.jsx +15 -4
- package/packages/gui/src/components/keeper/global-modals.jsx +177 -0
- package/packages/gui/src/components/layout/activity-bar.jsx +11 -6
- package/packages/gui/src/components/layout/terminal-panel.jsx +152 -3
- package/packages/gui/src/components/marketplace/integration-wizard.jsx +223 -18
- package/packages/gui/src/stores/groove.js +302 -20
- package/packages/gui/src/views/agents.jsx +118 -60
- package/packages/gui/src/views/editor.jsx +67 -219
- package/packages/gui/src/views/memory.jsx +460 -0
- package/packages/gui/src/views/model-lab.jsx +1 -6
- package/packages/gui/src/views/models.jsx +658 -565
- package/plan_files/keeper-manual.md +295 -0
- package/plan_files/keeper-memory-system.md +223 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-AkOtskHS.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-B4uYLR57.js +0 -8694
- package/packages/gui/dist/assets/index-AkOtskHS.css +0 -1
- package/packages/gui/dist/assets/index-B4uYLR57.js +0 -8694
|
@@ -115,6 +115,12 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
115
115
|
conversationReasoningEffort: loadJSON('groove:conversationReasoningEffort'),
|
|
116
116
|
conversationVerbosity: loadJSON('groove:conversationVerbosity'),
|
|
117
117
|
|
|
118
|
+
// ── Keeper (tagged memory) ─────────────────────────────────
|
|
119
|
+
keeperItems: [],
|
|
120
|
+
keeperTree: [],
|
|
121
|
+
keeperEditing: null, // { tag, content, isNew, readOnly } — drives the editor modal
|
|
122
|
+
keeperInstructOpen: false,
|
|
123
|
+
|
|
118
124
|
// ── Approvals ─────────────────────────────────────────────
|
|
119
125
|
pendingApprovals: [],
|
|
120
126
|
resolvedApprovals: [],
|
|
@@ -207,6 +213,7 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
207
213
|
|
|
208
214
|
// ── Editor (Cursor-style) ────────────────────────────────
|
|
209
215
|
editorSelectedAgent: null,
|
|
216
|
+
editorPendingSnippet: null,
|
|
210
217
|
editorViewMode: 'code',
|
|
211
218
|
editorAiPanelOpen: false,
|
|
212
219
|
editorAiPanelWidth: Number(localStorage.getItem('groove:editorAiPanelWidth')) || 360,
|
|
@@ -659,6 +666,7 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
659
666
|
case 'lab:runtime:added':
|
|
660
667
|
case 'lab:runtime:updated':
|
|
661
668
|
case 'lab:runtime:removed':
|
|
669
|
+
case 'llama:server:stopped':
|
|
662
670
|
get().fetchLabRuntimes();
|
|
663
671
|
break;
|
|
664
672
|
|
|
@@ -1212,6 +1220,12 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1212
1220
|
set((s) => ({ networkBenchmarks: [...s.networkBenchmarks, sd].slice(-100) }));
|
|
1213
1221
|
break;
|
|
1214
1222
|
}
|
|
1223
|
+
|
|
1224
|
+
case 'keeper:saved':
|
|
1225
|
+
case 'keeper:updated':
|
|
1226
|
+
case 'keeper:deleted':
|
|
1227
|
+
get().fetchKeeperItems();
|
|
1228
|
+
break;
|
|
1215
1229
|
}
|
|
1216
1230
|
};
|
|
1217
1231
|
|
|
@@ -1230,6 +1244,189 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1230
1244
|
|
|
1231
1245
|
setActiveView(view) { set({ activeView: view }); },
|
|
1232
1246
|
|
|
1247
|
+
// ── Keeper (tagged memory) ────────────────────────────────
|
|
1248
|
+
|
|
1249
|
+
async fetchKeeperItems() {
|
|
1250
|
+
try {
|
|
1251
|
+
const data = await api.get('/keeper');
|
|
1252
|
+
const treeData = await api.get('/keeper/tree');
|
|
1253
|
+
set({ keeperItems: data.items || [], keeperTree: treeData.tree || [] });
|
|
1254
|
+
} catch { /* ignore */ }
|
|
1255
|
+
},
|
|
1256
|
+
|
|
1257
|
+
async saveKeeperItem(tag, content) {
|
|
1258
|
+
try {
|
|
1259
|
+
const item = await api.post('/keeper', { tag, content });
|
|
1260
|
+
get().fetchKeeperItems();
|
|
1261
|
+
get().addToast('success', `Saved #${item.tag}`);
|
|
1262
|
+
return item;
|
|
1263
|
+
} catch (err) {
|
|
1264
|
+
get().addToast('error', 'Failed to save memory', err.message);
|
|
1265
|
+
throw err;
|
|
1266
|
+
}
|
|
1267
|
+
},
|
|
1268
|
+
|
|
1269
|
+
async appendKeeperItem(tag, content) {
|
|
1270
|
+
try {
|
|
1271
|
+
const item = await api.post('/keeper/append', { tag, content });
|
|
1272
|
+
get().fetchKeeperItems();
|
|
1273
|
+
get().addToast('success', `Appended to #${item.tag}`);
|
|
1274
|
+
return item;
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
get().addToast('error', 'Failed to append', err.message);
|
|
1277
|
+
throw err;
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
|
|
1281
|
+
async updateKeeperItem(tag, content) {
|
|
1282
|
+
try {
|
|
1283
|
+
const item = await api.patch(`/keeper/${tag}`, { content });
|
|
1284
|
+
get().fetchKeeperItems();
|
|
1285
|
+
get().addToast('success', `Updated #${item.tag}`);
|
|
1286
|
+
return item;
|
|
1287
|
+
} catch (err) {
|
|
1288
|
+
get().addToast('error', 'Failed to update memory', err.message);
|
|
1289
|
+
throw err;
|
|
1290
|
+
}
|
|
1291
|
+
},
|
|
1292
|
+
|
|
1293
|
+
async deleteKeeperItem(tag) {
|
|
1294
|
+
try {
|
|
1295
|
+
await api.delete(`/keeper/${tag}`);
|
|
1296
|
+
get().fetchKeeperItems();
|
|
1297
|
+
get().addToast('success', `Deleted #${tag}`);
|
|
1298
|
+
} catch (err) {
|
|
1299
|
+
get().addToast('error', 'Failed to delete memory', err.message);
|
|
1300
|
+
}
|
|
1301
|
+
},
|
|
1302
|
+
|
|
1303
|
+
async getKeeperItem(tag) {
|
|
1304
|
+
try {
|
|
1305
|
+
return await api.get(`/keeper/${tag}`);
|
|
1306
|
+
} catch {
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
|
|
1311
|
+
async searchKeeper(query) {
|
|
1312
|
+
try {
|
|
1313
|
+
const data = await api.get(`/keeper/search?q=${encodeURIComponent(query)}`);
|
|
1314
|
+
return data.results || [];
|
|
1315
|
+
} catch {
|
|
1316
|
+
return [];
|
|
1317
|
+
}
|
|
1318
|
+
},
|
|
1319
|
+
|
|
1320
|
+
setKeeperEditing(editing) {
|
|
1321
|
+
set({ keeperEditing: editing });
|
|
1322
|
+
},
|
|
1323
|
+
|
|
1324
|
+
async _handleKeeperCommand(agentId, message, command) {
|
|
1325
|
+
const rest = message.replace(/\[\w+[-\w]*\]|\b(?:save|append|update|delete|view|doc|link|read|instruct)\b/i, '').trim();
|
|
1326
|
+
const tags = (rest.match(/#[\w/.-]+/g) || []).map(t => t.replace(/^#/, ''));
|
|
1327
|
+
|
|
1328
|
+
const addSystemMsg = (text) => {
|
|
1329
|
+
get().addChatMessage(agentId, 'system', text);
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
try {
|
|
1333
|
+
switch (command) {
|
|
1334
|
+
case 'instruct': {
|
|
1335
|
+
set({ keeperInstructOpen: true });
|
|
1336
|
+
return true;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
case 'save': {
|
|
1340
|
+
if (tags.length === 0) { addSystemMsg('Usage: save #tag your message here'); return true; }
|
|
1341
|
+
const content = rest.replace(/#[\w/.-]+/g, '').trim();
|
|
1342
|
+
if (!content) { addSystemMsg('Usage: save #tag your message here'); return true; }
|
|
1343
|
+
await get().saveKeeperItem(tags[0], content);
|
|
1344
|
+
addSystemMsg(`Saved to #${tags[0]}`);
|
|
1345
|
+
return { passthrough: content };
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
case 'append': {
|
|
1349
|
+
if (tags.length === 0) { addSystemMsg('Usage: append #tag content to add'); return true; }
|
|
1350
|
+
const content = rest.replace(/#[\w/.-]+/g, '').trim();
|
|
1351
|
+
if (!content) { addSystemMsg('Usage: append #tag content to add'); return true; }
|
|
1352
|
+
await get().appendKeeperItem(tags[0], content);
|
|
1353
|
+
addSystemMsg(`Appended to #${tags[0]}`);
|
|
1354
|
+
return { passthrough: content };
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
case 'update': {
|
|
1358
|
+
if (tags.length === 0) { addSystemMsg('Usage: [update] #tag'); return true; }
|
|
1359
|
+
get().addChatMessage(agentId, 'user', message, false);
|
|
1360
|
+
const existing = await get().getKeeperItem(tags[0]);
|
|
1361
|
+
set({ keeperEditing: { tag: tags[0], content: existing?.content || '', isNew: !existing } });
|
|
1362
|
+
return true;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
case 'delete': {
|
|
1366
|
+
if (tags.length === 0) { addSystemMsg('Usage: [delete] #tag'); return true; }
|
|
1367
|
+
get().addChatMessage(agentId, 'user', message, false);
|
|
1368
|
+
await get().deleteKeeperItem(tags[0]);
|
|
1369
|
+
addSystemMsg(`Deleted #${tags[0]}`);
|
|
1370
|
+
return true;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
case 'view': {
|
|
1374
|
+
if (tags.length === 0) { addSystemMsg('Usage: [view] #tag'); return true; }
|
|
1375
|
+
get().addChatMessage(agentId, 'user', message, false);
|
|
1376
|
+
const item = await get().getKeeperItem(tags[0]);
|
|
1377
|
+
if (item) {
|
|
1378
|
+
set({ keeperEditing: { tag: tags[0], content: item.content, isNew: false, readOnly: true } });
|
|
1379
|
+
} else {
|
|
1380
|
+
addSystemMsg(`#${tags[0]} not found`);
|
|
1381
|
+
}
|
|
1382
|
+
return true;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
case 'read': {
|
|
1386
|
+
if (tags.length === 0) { addSystemMsg('Usage: [read] #tag1 #tag2 ...'); return true; }
|
|
1387
|
+
get().addChatMessage(agentId, 'user', message, false);
|
|
1388
|
+
const readBrief = await api.post('/keeper/pull', { tags });
|
|
1389
|
+
if (readBrief?.brief) {
|
|
1390
|
+
await api.post(`/agents/${encodeURIComponent(agentId)}/instruct`, {
|
|
1391
|
+
message: `Here is context from my tagged memories:\n\n${readBrief.brief}`,
|
|
1392
|
+
});
|
|
1393
|
+
addSystemMsg(`Sent ${tags.map(t => '#' + t).join(', ')} to agent`);
|
|
1394
|
+
} else {
|
|
1395
|
+
addSystemMsg(`No memories found for ${tags.map(t => '#' + t).join(', ')}`);
|
|
1396
|
+
}
|
|
1397
|
+
return true;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
case 'doc': {
|
|
1401
|
+
if (tags.length === 0) { addSystemMsg('Usage: [doc] #tag'); return true; }
|
|
1402
|
+
get().addChatMessage(agentId, 'user', message, false);
|
|
1403
|
+
addSystemMsg(`Generating doc for #${tags[0]}...`);
|
|
1404
|
+
const history = get().chatHistory[agentId] || [];
|
|
1405
|
+
const result = await api.post('/keeper/doc', { tag: tags[0], chatHistory: history, agentId });
|
|
1406
|
+
if (result?.content) {
|
|
1407
|
+
addSystemMsg(`Doc #${tags[0]} generated (${result.size}B)`);
|
|
1408
|
+
set({ keeperEditing: { tag: tags[0], content: result.content, isNew: false } });
|
|
1409
|
+
}
|
|
1410
|
+
return true;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
case 'link': {
|
|
1414
|
+
const linkMatch = rest.match(/^((?:#[\w/.-]+\s*)+)\s+(.+)$/);
|
|
1415
|
+
if (!linkMatch || tags.length === 0) { addSystemMsg('Usage: [link] #tag path/to/doc'); return true; }
|
|
1416
|
+
const docPath = linkMatch[2].trim();
|
|
1417
|
+
get().addChatMessage(agentId, 'user', message, false);
|
|
1418
|
+
await api.post('/keeper/link', { tag: tags[0], docPath });
|
|
1419
|
+
addSystemMsg(`Linked #${tags[0]} → ${docPath}`);
|
|
1420
|
+
return true;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
} catch (err) {
|
|
1424
|
+
addSystemMsg(`Keeper error: ${err.message}`);
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
return false;
|
|
1428
|
+
},
|
|
1429
|
+
|
|
1233
1430
|
// ── Teams ─────────────────────────────────────────────────
|
|
1234
1431
|
|
|
1235
1432
|
async fetchTeams() {
|
|
@@ -1834,7 +2031,6 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1834
2031
|
...(tlc?.reasoningEffort != null && { teamReasoningEffort: tlc.reasoningEffort }),
|
|
1835
2032
|
...(tlc?.temperature != null && { teamTemperature: tlc.temperature }),
|
|
1836
2033
|
...(tlc?.verbosity != null && { teamVerbosity: tlc.verbosity }),
|
|
1837
|
-
...(tlc?.mode && { mode: tlc.mode }),
|
|
1838
2034
|
};
|
|
1839
2035
|
const result = await api.post('/recommended-team/launch', body);
|
|
1840
2036
|
const totalOk = (result.launched || 0) + (result.reused || 0);
|
|
@@ -2487,6 +2683,16 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
2487
2683
|
},
|
|
2488
2684
|
|
|
2489
2685
|
async instructAgent(id, message) {
|
|
2686
|
+
// ── Keeper command interception ─────────────────────────
|
|
2687
|
+
const keeperCmd = message.match(/\[(save|append|update|delete|view|doc|link|read|instruct)\]|\b(save|append|update|delete|view|doc|link|read)\b(?=\s+#[\w/.-])/i);
|
|
2688
|
+
if (keeperCmd) {
|
|
2689
|
+
const handled = await get()._handleKeeperCommand(id, message, (keeperCmd[1] || keeperCmd[2]).toLowerCase());
|
|
2690
|
+
if (handled === true) return { status: 'keeper_handled' };
|
|
2691
|
+
if (handled?.passthrough) {
|
|
2692
|
+
message = handled.passthrough;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2490
2696
|
get().addChatMessage(id, 'user', message, false);
|
|
2491
2697
|
set((s) => ({ thinkingAgents: new Set([...s.thinkingAgents, id]) }));
|
|
2492
2698
|
|
|
@@ -3022,10 +3228,27 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
3022
3228
|
set({ editorQuickSearchOpen: open });
|
|
3023
3229
|
},
|
|
3024
3230
|
|
|
3231
|
+
attachSnippet(snippet) {
|
|
3232
|
+
set({ editorPendingSnippet: snippet });
|
|
3233
|
+
if (!get().editorAiPanelOpen) {
|
|
3234
|
+
set({ editorAiPanelOpen: true });
|
|
3235
|
+
}
|
|
3236
|
+
},
|
|
3237
|
+
|
|
3238
|
+
clearSnippet() {
|
|
3239
|
+
set({ editorPendingSnippet: null });
|
|
3240
|
+
},
|
|
3241
|
+
|
|
3025
3242
|
async sendCodeToAgent(agentId, instruction, filePath, lineStart, lineEnd, selectedCode) {
|
|
3026
3243
|
if (!agentId) return;
|
|
3027
|
-
|
|
3028
|
-
|
|
3244
|
+
get().attachSnippet({
|
|
3245
|
+
type: 'code',
|
|
3246
|
+
instruction,
|
|
3247
|
+
filePath,
|
|
3248
|
+
lineStart,
|
|
3249
|
+
lineEnd,
|
|
3250
|
+
code: selectedCode,
|
|
3251
|
+
});
|
|
3029
3252
|
},
|
|
3030
3253
|
|
|
3031
3254
|
async fetchGitStatus() {
|
|
@@ -3640,11 +3863,17 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
3640
3863
|
const payload = line.slice(6);
|
|
3641
3864
|
if (payload === '[DONE]') continue;
|
|
3642
3865
|
try {
|
|
3643
|
-
const
|
|
3644
|
-
|
|
3866
|
+
const parsed = JSON.parse(payload);
|
|
3867
|
+
|
|
3868
|
+
// Support both raw OpenAI format (piped) and legacy wrapper format
|
|
3869
|
+
const delta = parsed.choices?.[0]?.delta;
|
|
3870
|
+
const reasoningText = delta?.reasoning_content || (parsed.type === 'reasoning' ? parsed.content : null);
|
|
3871
|
+
const contentText = delta?.content || (parsed.type === 'token' ? parsed.content : null);
|
|
3872
|
+
|
|
3873
|
+
if (reasoningText) {
|
|
3645
3874
|
if (!firstTokenTime) firstTokenTime = performance.now();
|
|
3646
3875
|
tokenCount++;
|
|
3647
|
-
fullReasoning +=
|
|
3876
|
+
fullReasoning += reasoningText;
|
|
3648
3877
|
set((s) => {
|
|
3649
3878
|
const sessions = s.labSessions.map((sess) => {
|
|
3650
3879
|
if (sess.id !== sessionId) return sess;
|
|
@@ -3655,10 +3884,10 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
3655
3884
|
return { labSessions: sessions };
|
|
3656
3885
|
});
|
|
3657
3886
|
}
|
|
3658
|
-
if (
|
|
3887
|
+
if (contentText) {
|
|
3659
3888
|
if (!firstTokenTime) firstTokenTime = performance.now();
|
|
3660
3889
|
tokenCount++;
|
|
3661
|
-
fullContent +=
|
|
3890
|
+
fullContent += contentText;
|
|
3662
3891
|
set((s) => {
|
|
3663
3892
|
const sessions = s.labSessions.map((sess) => {
|
|
3664
3893
|
if (sess.id !== sessionId) return sess;
|
|
@@ -3669,11 +3898,13 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
3669
3898
|
return { labSessions: sessions };
|
|
3670
3899
|
});
|
|
3671
3900
|
}
|
|
3672
|
-
|
|
3901
|
+
|
|
3902
|
+
// Handle done event (legacy wrapper) or finish_reason (raw OpenAI)
|
|
3903
|
+
if (parsed.type === 'done' && parsed.metrics) {
|
|
3673
3904
|
const elapsed = performance.now() - startTime;
|
|
3674
3905
|
const ttft = firstTokenTime ? firstTokenTime - startTime : null;
|
|
3675
3906
|
const tps = tokenCount > 0 && elapsed > 0 ? (tokenCount / (elapsed / 1000)) : null;
|
|
3676
|
-
const msgMetrics = { ttft, tokensPerSec: tps, tokens: tokenCount, generationTime: elapsed, ...
|
|
3907
|
+
const msgMetrics = { ttft, tokensPerSec: tps, tokens: tokenCount, generationTime: elapsed, ...parsed.metrics };
|
|
3677
3908
|
|
|
3678
3909
|
set((s) => {
|
|
3679
3910
|
const tpsHist = [...s.labMetrics.tokensPerSecHistory, tps].slice(-10);
|
|
@@ -3687,15 +3918,15 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
3687
3918
|
labSessions: sessions,
|
|
3688
3919
|
labMetrics: {
|
|
3689
3920
|
ttft, tokensPerSec: tps, tokensPerSecHistory: tpsHist,
|
|
3690
|
-
memory:
|
|
3691
|
-
totalTokens: s.labMetrics.totalTokens + (
|
|
3692
|
-
generationTime:
|
|
3921
|
+
memory: parsed.metrics.memoryUsage || s.labMetrics.memory,
|
|
3922
|
+
totalTokens: s.labMetrics.totalTokens + (parsed.metrics.totalTokens || tokenCount),
|
|
3923
|
+
generationTime: parsed.metrics.generationTime || elapsed,
|
|
3693
3924
|
},
|
|
3694
3925
|
};
|
|
3695
3926
|
});
|
|
3696
3927
|
}
|
|
3697
|
-
if (
|
|
3698
|
-
throw new Error(
|
|
3928
|
+
if (parsed.type === 'error') {
|
|
3929
|
+
throw new Error(parsed.error || 'Inference error');
|
|
3699
3930
|
}
|
|
3700
3931
|
} catch (e) {
|
|
3701
3932
|
if (e.message && e.message !== 'Inference error' && !e.message.startsWith('HTTP ')) continue;
|
|
@@ -3704,14 +3935,24 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
3704
3935
|
}
|
|
3705
3936
|
}
|
|
3706
3937
|
|
|
3707
|
-
//
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3938
|
+
// Compute final metrics from client-side timing
|
|
3939
|
+
const elapsed = performance.now() - startTime;
|
|
3940
|
+
const ttft = firstTokenTime ? firstTokenTime - startTime : null;
|
|
3941
|
+
const tps = tokenCount > 0 && elapsed > 0 ? (tokenCount / (elapsed / 1000)) : null;
|
|
3942
|
+
if (tokenCount > 0) {
|
|
3712
3943
|
set((s) => {
|
|
3713
3944
|
const tpsHist = [...s.labMetrics.tokensPerSecHistory, tps].slice(-10);
|
|
3945
|
+
const sessions = s.labSessions.map((sess) => {
|
|
3946
|
+
if (sess.id !== sessionId) return sess;
|
|
3947
|
+
const msgs = [...sess.messages];
|
|
3948
|
+
const last = msgs[msgs.length - 1];
|
|
3949
|
+
if (!last?.metrics) {
|
|
3950
|
+
msgs[msgs.length - 1] = { ...last, metrics: { ttft, tokensPerSec: tps, tokens: tokenCount, generationTime: elapsed } };
|
|
3951
|
+
}
|
|
3952
|
+
return { ...sess, messages: msgs };
|
|
3953
|
+
});
|
|
3714
3954
|
return {
|
|
3955
|
+
labSessions: sessions,
|
|
3715
3956
|
labMetrics: { ...s.labMetrics, ttft, tokensPerSec: tps, tokensPerSecHistory: tpsHist, totalTokens: s.labMetrics.totalTokens + tokenCount, generationTime: elapsed },
|
|
3716
3957
|
};
|
|
3717
3958
|
});
|
|
@@ -3836,4 +4077,45 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
3836
4077
|
return false;
|
|
3837
4078
|
}
|
|
3838
4079
|
},
|
|
4080
|
+
|
|
4081
|
+
// ── Integration Agent Install ────────────────────────────
|
|
4082
|
+
|
|
4083
|
+
async installViaExistingAgent(integration, agentId) {
|
|
4084
|
+
const message = buildIntegrationPrompt(integration);
|
|
4085
|
+
await get().instructAgent(agentId, message);
|
|
4086
|
+
get().setActiveView('agents');
|
|
4087
|
+
get().selectAgent(agentId);
|
|
4088
|
+
},
|
|
4089
|
+
|
|
4090
|
+
async spawnIntegrationTeam(integration) {
|
|
4091
|
+
const team = await get().createTeam(integration.name);
|
|
4092
|
+
const prompt = buildIntegrationPrompt(integration);
|
|
4093
|
+
const agent = await get().spawnAgent({ role: 'planner', prompt, teamId: team.id });
|
|
4094
|
+
get().setActiveView('agents');
|
|
4095
|
+
get().selectAgent(agent.id);
|
|
4096
|
+
return agent;
|
|
4097
|
+
},
|
|
3839
4098
|
}));
|
|
4099
|
+
|
|
4100
|
+
function buildIntegrationPrompt(integration) {
|
|
4101
|
+
const lines = [
|
|
4102
|
+
`Set up the "${integration.name}" integration for this project.`,
|
|
4103
|
+
'',
|
|
4104
|
+
];
|
|
4105
|
+
if (integration.description) lines.push(`**Description:** ${integration.description}`);
|
|
4106
|
+
if (integration.npmPackage) lines.push(`**npm package:** ${integration.npmPackage}`);
|
|
4107
|
+
if (integration.authType) lines.push(`**Auth type:** ${integration.authType}`);
|
|
4108
|
+
if (integration.envKeys?.length) {
|
|
4109
|
+
lines.push('', '**Environment keys required:**');
|
|
4110
|
+
for (const k of integration.envKeys) {
|
|
4111
|
+
lines.push(`- \`${k.key}\` — ${k.label}${k.required ? ' (required)' : ''}`);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
if (integration.setupSteps?.length) {
|
|
4115
|
+
lines.push('', '**Setup steps:**');
|
|
4116
|
+
integration.setupSteps.forEach((step, i) => lines.push(`${i + 1}. ${step}`));
|
|
4117
|
+
}
|
|
4118
|
+
if (integration.setupUrl) lines.push(``, `**Setup URL:** ${integration.setupUrl}`);
|
|
4119
|
+
if (integration.agentInstructions) lines.push('', `**Agent instructions:** ${integration.agentInstructions}`);
|
|
4120
|
+
return lines.join('\n');
|
|
4121
|
+
}
|