groove-dev 0.27.28 → 0.27.29
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/CLAUDE.md +7 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/journalist.js +103 -45
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +1 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-Ch1N9G4Z.js → index-CNsQ3n1t.js} +290 -290
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +2 -2
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/journalist.js +103 -45
- package/packages/gui/dist/assets/{index-Ch1N9G4Z.js → index-CNsQ3n1t.js} +290 -290
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-config.jsx +2 -2
- package/packages/gui/src/views/settings.jsx +2 -2
- package/.groove-staging/state.json +0 -3
- package/.groove-staging/timeline.json +0 -13
- package/DECENTRALIZED_NET_WP_V1.md +0 -871
- package/decentralized-net/ACTION_PLAN.md +0 -422
package/CLAUDE.md
CHANGED
|
@@ -263,3 +263,10 @@ Audit-driven release. Multi-agent orchestration system with 7 coordination layer
|
|
|
263
263
|
- Dashboard: routing donut, cache panel, context health gauges
|
|
264
264
|
- Monitor/QC agent mode (stay active, loop)
|
|
265
265
|
- Distribution: demo video, HN launch, Twitter content
|
|
266
|
+
|
|
267
|
+
<!-- GROOVE:START -->
|
|
268
|
+
## GROOVE Orchestration (auto-injected)
|
|
269
|
+
Active agents: 0
|
|
270
|
+
See AGENTS_REGISTRY.md for full agent state.
|
|
271
|
+
**Memory policy:** GROOVE manages project memory automatically. Do not read or write MEMORY.md or .groove/memory/ files directly.
|
|
272
|
+
<!-- GROOVE:END -->
|
|
@@ -166,7 +166,7 @@ export class Journalist {
|
|
|
166
166
|
return false;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
collectFilteredLogs(agents) {
|
|
169
|
+
collectFilteredLogs(agents, { since } = {}) {
|
|
170
170
|
const result = {};
|
|
171
171
|
|
|
172
172
|
for (const agent of agents) {
|
|
@@ -181,7 +181,7 @@ export class Journalist {
|
|
|
181
181
|
const size = Buffer.byteLength(content);
|
|
182
182
|
this.lastLogSizes[agent.id] = size;
|
|
183
183
|
|
|
184
|
-
const { entries, explorationEntries } = this.filterLog(content, agent);
|
|
184
|
+
const { entries, explorationEntries } = this.filterLog(content, agent, { since });
|
|
185
185
|
result[agent.id] = { agent, entries, explorationEntries };
|
|
186
186
|
} catch {
|
|
187
187
|
result[agent.id] = { agent, entries: [], explorationEntries: [] };
|
|
@@ -191,7 +191,7 @@ export class Journalist {
|
|
|
191
191
|
return result;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
filterLog(rawLog, agent) {
|
|
194
|
+
filterLog(rawLog, agent, { since } = {}) {
|
|
195
195
|
// Parse stream-json lines and extract meaningful events.
|
|
196
196
|
// Focus on PROGRESS (writes, edits, commands with results) not EXPLORATION (reads, greps).
|
|
197
197
|
// Exploration tools are tracked separately so handoff briefs can include what was examined.
|
|
@@ -200,12 +200,15 @@ export class Journalist {
|
|
|
200
200
|
const lines = rawLog.split('\n');
|
|
201
201
|
const toolResults = new Map(); // tool_use_id -> result text
|
|
202
202
|
|
|
203
|
+
const sinceDate = since ? new Date(since) : null;
|
|
204
|
+
|
|
203
205
|
// First pass: collect tool results (and error flags) so we can attach them to tool calls
|
|
204
206
|
const toolErrors = new Set(); // tool_use_ids that returned errors
|
|
205
207
|
for (const line of lines) {
|
|
206
208
|
if (!line.trim() || line.startsWith('[')) continue;
|
|
207
209
|
try {
|
|
208
210
|
const data = JSON.parse(line);
|
|
211
|
+
if (sinceDate && data.timestamp && new Date(data.timestamp) < sinceDate) continue;
|
|
209
212
|
if (data.type === 'user' && data.message?.content) {
|
|
210
213
|
const content = Array.isArray(data.message.content) ? data.message.content : [];
|
|
211
214
|
for (const block of content) {
|
|
@@ -227,6 +230,7 @@ export class Journalist {
|
|
|
227
230
|
|
|
228
231
|
try {
|
|
229
232
|
const data = JSON.parse(line);
|
|
233
|
+
if (sinceDate && data.timestamp && new Date(data.timestamp) < sinceDate) continue;
|
|
230
234
|
|
|
231
235
|
// Tool use — only keep WRITES, EDITS, and COMMANDS (progress, not exploration)
|
|
232
236
|
if (data.type === 'assistant' && data.message?.content) {
|
|
@@ -415,6 +419,56 @@ export class Journalist {
|
|
|
415
419
|
return parts.join('\n');
|
|
416
420
|
}
|
|
417
421
|
|
|
422
|
+
buildRotationSynthesisPrompt(agent, entries, options = {}) {
|
|
423
|
+
const dir = agent.workingDir ? `\nWorking directory: ${agent.workingDir}` : '';
|
|
424
|
+
const reason = options.reason || 'manual rotation';
|
|
425
|
+
|
|
426
|
+
const parts = [
|
|
427
|
+
'You are briefing the next AI agent taking over this exact role.',
|
|
428
|
+
`The previous agent (${agent.name}, role: ${agent.role}) is being rotated out.`,
|
|
429
|
+
`Scope: ${agent.scope?.join(', ') || 'unrestricted'}${dir}`,
|
|
430
|
+
`Rotation reason: ${reason}`,
|
|
431
|
+
'',
|
|
432
|
+
'Analyze the session log below and produce a structured handoff brief.',
|
|
433
|
+
'',
|
|
434
|
+
'Output EXACTLY these sections:',
|
|
435
|
+
'',
|
|
436
|
+
'## Accomplishments',
|
|
437
|
+
'(What was completed. Name files, functions, and line numbers.)',
|
|
438
|
+
'',
|
|
439
|
+
'## In Progress',
|
|
440
|
+
'(What was actively being worked on when rotation happened.)',
|
|
441
|
+
'',
|
|
442
|
+
'## Key Decisions',
|
|
443
|
+
'(Architectural or implementation choices made and why.)',
|
|
444
|
+
'',
|
|
445
|
+
'## Blockers/Errors',
|
|
446
|
+
'(Unresolved errors, failed attempts, things that did not work.)',
|
|
447
|
+
'',
|
|
448
|
+
'## Next Steps',
|
|
449
|
+
'(What should be done next, in priority order.)',
|
|
450
|
+
'',
|
|
451
|
+
'Be specific. Name files, functions, and line numbers. Do not summarize vaguely.',
|
|
452
|
+
'Keep your response under 2000 characters.',
|
|
453
|
+
'',
|
|
454
|
+
'---',
|
|
455
|
+
'',
|
|
456
|
+
`### Session Log for ${agent.name} (${agent.role})`,
|
|
457
|
+
'',
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
let totalChars = 0;
|
|
461
|
+
const cap = 30_000;
|
|
462
|
+
for (const entry of entries.slice(-200)) {
|
|
463
|
+
const line = this.formatEntry(entry);
|
|
464
|
+
if (totalChars + line.length > cap) break;
|
|
465
|
+
parts.push(line);
|
|
466
|
+
totalChars += line.length;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return parts.join('\n');
|
|
470
|
+
}
|
|
471
|
+
|
|
418
472
|
formatEntry(entry) {
|
|
419
473
|
switch (entry.type) {
|
|
420
474
|
case 'tool': {
|
|
@@ -739,35 +793,10 @@ export class Journalist {
|
|
|
739
793
|
// --- Handoff Brief for Context Rotation ---
|
|
740
794
|
|
|
741
795
|
async generateHandoffBrief(agent, options = {}) {
|
|
742
|
-
const filteredLogs = this.collectFilteredLogs([agent]);
|
|
796
|
+
const filteredLogs = this.collectFilteredLogs([agent], { since: agent.spawnedAt });
|
|
743
797
|
const agentLog = filteredLogs[agent.id];
|
|
744
798
|
const entries = agentLog?.entries || [];
|
|
745
799
|
|
|
746
|
-
const errorSummary = entries
|
|
747
|
-
.filter((e) => e.type === 'error')
|
|
748
|
-
.map((e) => `- ${e.text}`)
|
|
749
|
-
.slice(-10)
|
|
750
|
-
.join('\n');
|
|
751
|
-
|
|
752
|
-
const resultSummary = entries
|
|
753
|
-
.filter((e) => e.type === 'result')
|
|
754
|
-
.map((e) => e.text)
|
|
755
|
-
.slice(-3)
|
|
756
|
-
.join('\n');
|
|
757
|
-
|
|
758
|
-
// Build file changes section — group Edit/Write operations by file path
|
|
759
|
-
const fileChanges = {};
|
|
760
|
-
for (const e of entries.filter((e) => e.type === 'tool' && (e.tool === 'Edit' || e.tool === 'Write'))) {
|
|
761
|
-
const file = e.input || 'unknown';
|
|
762
|
-
if (!fileChanges[file]) fileChanges[file] = [];
|
|
763
|
-
if (e.diff) fileChanges[file].push(e.diff);
|
|
764
|
-
else fileChanges[file].push(e.tool === 'Write' ? 'created' : 'modified');
|
|
765
|
-
}
|
|
766
|
-
const fileChangesSummary = Object.entries(fileChanges)
|
|
767
|
-
.map(([file, changes]) => `- **${file}**: ${changes.slice(0, 3).join('; ')}`)
|
|
768
|
-
.slice(0, 20)
|
|
769
|
-
.join('\n');
|
|
770
|
-
|
|
771
800
|
// Layer 7 memory: discoveries, constraints, specializations
|
|
772
801
|
const discoveries = this.daemon.memory?.getDiscoveriesMarkdown(agent.role, 10, 2000) || '';
|
|
773
802
|
const constraints = this.daemon.memory?.getConstraintsMarkdown(2000) || '';
|
|
@@ -776,26 +805,58 @@ export class Journalist {
|
|
|
776
805
|
? `- Quality profile: ${specialization.avgQualityScore}/100 across ${specialization.sessionCount} sessions`
|
|
777
806
|
: '';
|
|
778
807
|
|
|
779
|
-
// Pull recent rotation history from persistent memory (Layer 7).
|
|
780
|
-
// Gives the new agent causal continuity: what the last 3 agents struggled
|
|
781
|
-
// with, decided, and solved — not just what the current session did.
|
|
782
808
|
const recentChain = this.daemon.memory?.getRecentHandoffMarkdown(agent.role, 3, 3000, agent.workingDir, agent.teamId) || '';
|
|
783
809
|
|
|
784
|
-
// Pull the user's recent messages scoped to this agent
|
|
785
810
|
const agentFeedback = this.getUserFeedback(agent.id).slice(-5);
|
|
786
811
|
const conversationSummary = agentFeedback.length > 0
|
|
787
812
|
? agentFeedback.map((fb) => `- "${fb.message}"`).join('\n')
|
|
788
813
|
: '';
|
|
789
814
|
|
|
790
|
-
//
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
.
|
|
794
|
-
|
|
795
|
-
|
|
815
|
+
// Try AI-synthesized session summary
|
|
816
|
+
let sessionSummary = '';
|
|
817
|
+
try {
|
|
818
|
+
const prompt = this.buildRotationSynthesisPrompt(agent, entries, options);
|
|
819
|
+
sessionSummary = await this.callHeadless(prompt, { trackAs: '__rotation__' });
|
|
820
|
+
} catch {
|
|
821
|
+
// Fallback: structural summary from raw logs
|
|
822
|
+
const errorSummary = entries
|
|
823
|
+
.filter((e) => e.type === 'error')
|
|
824
|
+
.map((e) => `- ${e.text}`)
|
|
825
|
+
.slice(-10)
|
|
826
|
+
.join('\n');
|
|
827
|
+
|
|
828
|
+
const resultSummary = entries
|
|
829
|
+
.filter((e) => e.type === 'result')
|
|
830
|
+
.map((e) => e.text)
|
|
831
|
+
.slice(-3)
|
|
832
|
+
.join('\n');
|
|
833
|
+
|
|
834
|
+
const fileChanges = {};
|
|
835
|
+
for (const e of entries.filter((e) => e.type === 'tool' && (e.tool === 'Edit' || e.tool === 'Write'))) {
|
|
836
|
+
const file = e.input || 'unknown';
|
|
837
|
+
if (!fileChanges[file]) fileChanges[file] = [];
|
|
838
|
+
if (e.diff) fileChanges[file].push(e.diff);
|
|
839
|
+
else fileChanges[file].push(e.tool === 'Write' ? 'created' : 'modified');
|
|
840
|
+
}
|
|
841
|
+
const fileChangesSummary = Object.entries(fileChanges)
|
|
842
|
+
.map(([file, changes]) => `- **${file}**: ${changes.slice(0, 3).join('; ')}`)
|
|
843
|
+
.slice(0, 20)
|
|
844
|
+
.join('\n');
|
|
845
|
+
|
|
846
|
+
const recentTools = entries
|
|
847
|
+
.filter((e) => e.type === 'tool' || e.type === 'error')
|
|
848
|
+
.slice(-5)
|
|
849
|
+
.map((e) => `- ${e.type === 'error' ? 'ERROR ' : ''}${e.tool}: ${(e.input || '').slice(0, 80)}`)
|
|
850
|
+
.join('\n');
|
|
851
|
+
|
|
852
|
+
const fallbackParts = [];
|
|
853
|
+
if (errorSummary) fallbackParts.push(`## Unresolved Errors\n\n${errorSummary}`);
|
|
854
|
+
if (recentTools) fallbackParts.push(`## Last 5 Tool Calls\n\n${recentTools}`);
|
|
855
|
+
if (fileChangesSummary) fallbackParts.push(`## Files Modified\n\n${fileChangesSummary}`);
|
|
856
|
+
if (resultSummary) fallbackParts.push(`## Accomplishments\n\n${resultSummary}`);
|
|
857
|
+
sessionSummary = fallbackParts.join('\n\n');
|
|
858
|
+
}
|
|
796
859
|
|
|
797
|
-
// Brief priority: errors > constraints > recent tools > accomplishments
|
|
798
|
-
// The rotator already wraps this with session continuation context
|
|
799
860
|
return [
|
|
800
861
|
`# Handoff Brief — ${agent.name} (${agent.role})`,
|
|
801
862
|
``,
|
|
@@ -804,13 +865,10 @@ export class Journalist {
|
|
|
804
865
|
`Rotation: ${options.reason || 'manual'}${options.qualityScore ? ` (quality: ${options.qualityScore}/100)` : ''} | Tokens: ${agent.tokensUsed}`,
|
|
805
866
|
specLine,
|
|
806
867
|
``,
|
|
807
|
-
|
|
868
|
+
sessionSummary ? `## Session Summary\n\n${sessionSummary}\n` : '',
|
|
808
869
|
constraints ? `## Project Constraints (must follow)\n\n${constraints}\n` : '',
|
|
809
870
|
discoveries ? `## Known Issues & Fixes\n\n${discoveries}\n` : '',
|
|
810
871
|
conversationSummary ? `## Recent User Messages\n\n${conversationSummary}\n` : '',
|
|
811
|
-
recentTools ? `## Last 5 Tool Calls\n\n${recentTools}\n` : '',
|
|
812
|
-
fileChangesSummary ? `## Files Modified\n\n${fileChangesSummary}\n` : '',
|
|
813
|
-
resultSummary ? `## Accomplishments\n\n${resultSummary}\n` : '',
|
|
814
872
|
recentChain ? `## Rotation History\n\n${recentChain}\n` : '',
|
|
815
873
|
agent.prompt ? `## Original Task\n\n${agent.prompt}\n` : '',
|
|
816
874
|
``,
|
|
@@ -201,7 +201,7 @@ describe('Journalist', () => {
|
|
|
201
201
|
assert.ok(brief.includes('src/api/**'));
|
|
202
202
|
assert.ok(brief.includes('5000'));
|
|
203
203
|
assert.ok(brief.includes('Build the auth API'));
|
|
204
|
-
assert.ok(brief.includes('Write'));
|
|
204
|
+
assert.ok(brief.includes('Session Summary') || brief.includes('Write'));
|
|
205
205
|
});
|
|
206
206
|
|
|
207
207
|
it('instructs the agent to deliver the output, not passively wait', async () => {
|