groove-dev 0.27.3 → 0.27.5
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/CHANGELOG.md +34 -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/introducer.js +11 -92
- package/node_modules/@groove-dev/daemon/src/journalist.js +20 -12
- package/node_modules/@groove-dev/daemon/src/process.js +2 -4
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +30 -0
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/introducer.js +11 -92
- package/packages/daemon/src/journalist.js +20 -12
- package/packages/daemon/src/process.js +2 -4
- package/packages/gui/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.27.5 — Revert planner/introducer changes (2026-04-12)
|
|
4
|
+
|
|
5
|
+
Planner flow stopped producing output after v0.27.x intro changes — agents would do partial exploration and stop without outputting a plan. After several failed targeted fixes (v0.27.3, v0.27.4), reverting `introducer.js` and the planner role prompt in `process.js` back to their v0.26.39 state rather than continue iterating on a broken premise. The planner flow that worked through 275M tokens is what should ship.
|
|
6
|
+
|
|
7
|
+
**What's reverted**
|
|
8
|
+
- `packages/daemon/src/introducer.js` — fully restored to v0.26.39. This removes:
|
|
9
|
+
- Project Memory injection at spawn (memory still accumulates, just not injected)
|
|
10
|
+
- "Ready to resume" team section enhancement from v0.27.3
|
|
11
|
+
- HTTP-based coordination protocol rewrite (back to `.groove/coordination.md` advisory)
|
|
12
|
+
- Memory API contribution note
|
|
13
|
+
- `packages/daemon/src/process.js` planner role prompt — fully restored to v0.26.39.
|
|
14
|
+
|
|
15
|
+
**What's preserved**
|
|
16
|
+
- All backend infrastructure: MemoryStore module, safety token ceiling + role multipliers, token tracking, cache formula fix, dashboard, tests.
|
|
17
|
+
- Journalist handoff brief v0.27.4 rewrite (only affects rotations; safety ceiling is 50M for planners so rotations should be extremely rare).
|
|
18
|
+
- Specialization updates on agent completion (re-added to process.js after revert).
|
|
19
|
+
- `__negotiator__` token tracking on task negotiation calls (re-added).
|
|
20
|
+
|
|
21
|
+
Memory accumulation still works — the rotator writes handoff chains, agent completion updates specializations. The data is captured for future use, it's just not injected into every new agent's intro context. If the injection experiment is worth retrying, it'll be in a separate release with careful A/B testing, not a surprise change.
|
|
22
|
+
|
|
23
|
+
**Apologies.** I should have done this revert two versions ago when the bug persisted. Trying to patch the symptoms kept the planner in a broken state for longer than necessary.
|
|
24
|
+
|
|
25
|
+
## v0.27.4 — Fix rotated agents abandoning mid-task work (2026-04-12)
|
|
26
|
+
|
|
27
|
+
**The bug I introduced in v0.27.1.** The rotation handoff brief told agents: *"Wait for the user's next message, then answer it directly."* That instruction was intended to prevent "Resuming after rotation" announcements — but for an agent that was mid-task when rotation fired (e.g., a planner planning, a backend writing code), it said: *stop the work, wait for the user.* The user gave a direct feature request, the planner burned 3M tokens exploring before rotation, the new planner read "wait for next message" and delivered nothing.
|
|
28
|
+
|
|
29
|
+
**Fix**
|
|
30
|
+
- Rewritten handoff brief flips the instruction: *"Finish what you were doing. The user is waiting for YOUR output. Produce it. No preamble, no announcement."*
|
|
31
|
+
- Explicitly calls out common mid-task states: "If you were a planner about to output a plan, output the plan now. If you were a builder about to make edits, make the edits."
|
|
32
|
+
- Recent User Messages section is now labeled "what they've been asking for — deliver this."
|
|
33
|
+
- Memory API contribution note de-emphasized (no longer prompts agents to proactively POST to `/api/memory/*` — it's opt-in, not a requirement).
|
|
34
|
+
|
|
35
|
+
**Regression test added** to lock the fix: any brief containing "wait for the user's next message" now fails CI.
|
|
36
|
+
|
|
3
37
|
## v0.27.3 — Planner sees ready-to-resume teammates (2026-04-12)
|
|
4
38
|
|
|
5
39
|
Fixes a Mode 1 / Mode 2 detection bug where a planner spawned duplicate agents instead of routing work to existing teammates.
|
|
@@ -16,30 +16,10 @@ export class Introducer {
|
|
|
16
16
|
generateContext(newAgent, options = {}) {
|
|
17
17
|
const { taskNegotiation } = options;
|
|
18
18
|
const agents = this.daemon.registry.getAll();
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// until resumed. Hiding them from the new agent's context makes planners
|
|
24
|
-
// falsely conclude "I'm alone" and spawn duplicate roles.
|
|
25
|
-
//
|
|
26
|
-
// Scope to the same team so one team's agents don't leak into another's
|
|
27
|
-
// context. Completed teammates get a 1-hour freshness cutoff so truly
|
|
28
|
-
// stale ones don't clutter the intro.
|
|
29
|
-
const COMPLETED_WINDOW_MS = 60 * 60 * 1000;
|
|
30
|
-
const sameTeam = (a) =>
|
|
31
|
-
a.id !== newAgent.id &&
|
|
32
|
-
(!newAgent.teamId || a.teamId === newAgent.teamId);
|
|
33
|
-
const activeOthers = agents.filter((a) =>
|
|
34
|
-
sameTeam(a) && (a.status === 'running' || a.status === 'starting')
|
|
35
|
-
);
|
|
36
|
-
const recentCompleted = agents.filter((a) => {
|
|
37
|
-
if (!sameTeam(a)) return false;
|
|
38
|
-
if (a.status !== 'completed') return false;
|
|
39
|
-
const ts = a.lastActivity ? new Date(a.lastActivity).getTime() : 0;
|
|
40
|
-
return Date.now() - ts < COMPLETED_WINDOW_MS;
|
|
41
|
-
});
|
|
42
|
-
const others = [...activeOthers, ...recentCompleted];
|
|
19
|
+
// Only include ACTIVE agents — not completed/killed ones from previous sessions
|
|
20
|
+
// Completed agents' work is captured in the journalist's project map, not here
|
|
21
|
+
const others = agents.filter((a) => a.id !== newAgent.id &&
|
|
22
|
+
(a.status === 'running' || a.status === 'starting'));
|
|
43
23
|
|
|
44
24
|
const lines = [
|
|
45
25
|
`# GROOVE Agent Context`,
|
|
@@ -62,17 +42,8 @@ export class Introducer {
|
|
|
62
42
|
if (others.length === 0) {
|
|
63
43
|
lines.push('You are the only agent on this project right now.');
|
|
64
44
|
} else {
|
|
65
|
-
|
|
66
|
-
const readyCount = recentCompleted.length;
|
|
67
|
-
const parts = [];
|
|
68
|
-
if (activeCount > 0) parts.push(`${activeCount} active`);
|
|
69
|
-
if (readyCount > 0) parts.push(`${readyCount} ready to resume`);
|
|
70
|
-
lines.push(`## Team (${others.length} teammate${others.length > 1 ? 's' : ''} — ${parts.join(', ')})`);
|
|
45
|
+
lines.push(`## Team (${others.length} other agent${others.length > 1 ? 's' : ''})`);
|
|
71
46
|
lines.push('');
|
|
72
|
-
if (readyCount > 0) {
|
|
73
|
-
lines.push(`**Teammates marked "ready" are part of your team.** They finished their last task and will resume their session when assigned new work. If you're a planner, route new tasks to them by role — do NOT spawn duplicates.`);
|
|
74
|
-
lines.push('');
|
|
75
|
-
}
|
|
76
47
|
|
|
77
48
|
// Collect all files created by teammates for the project files section
|
|
78
49
|
const allTeamFiles = [];
|
|
@@ -80,8 +51,7 @@ export class Introducer {
|
|
|
80
51
|
for (const other of others) {
|
|
81
52
|
const scope = other.scope?.length > 0 ? other.scope.join(', ') : 'unrestricted';
|
|
82
53
|
const dir = other.workingDir ? ` — dir: ${other.workingDir}` : '';
|
|
83
|
-
|
|
84
|
-
lines.push(`- **${other.name}** (${other.role}) — scope: ${scope}${dir} — ${statusLabel}`);
|
|
54
|
+
lines.push(`- **${other.name}** (${other.role}) — scope: ${scope}${dir} — ${other.status}`);
|
|
85
55
|
|
|
86
56
|
// Get files this agent created/modified
|
|
87
57
|
const files = this.daemon.journalist?.getAgentFiles(other) || [];
|
|
@@ -137,47 +107,6 @@ export class Introducer {
|
|
|
137
107
|
}
|
|
138
108
|
}
|
|
139
109
|
|
|
140
|
-
// Project memory (Layer 7) — accumulated wisdom across all prior rotations.
|
|
141
|
-
// Constraints, recent role handoffs, known error→fix patterns. Total cap ~12K chars.
|
|
142
|
-
if (this.daemon.memory) {
|
|
143
|
-
const constraints = this.daemon.memory.getConstraintsMarkdown(4000);
|
|
144
|
-
const recentChain = this.daemon.memory.getRecentHandoffMarkdown(newAgent.role, 3, 4000);
|
|
145
|
-
const discoveries = this.daemon.memory.getDiscoveriesMarkdown(newAgent.role, 20, 4000);
|
|
146
|
-
|
|
147
|
-
if (constraints || recentChain || discoveries) {
|
|
148
|
-
lines.push('');
|
|
149
|
-
lines.push(`## Project Memory`);
|
|
150
|
-
lines.push('');
|
|
151
|
-
lines.push(`This is accumulated knowledge from prior agents working on this project. Read carefully — it will save you from rediscovering what others already learned.`);
|
|
152
|
-
|
|
153
|
-
if (constraints) {
|
|
154
|
-
lines.push('');
|
|
155
|
-
lines.push(`### Constraints`);
|
|
156
|
-
lines.push('');
|
|
157
|
-
lines.push(constraints);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (recentChain) {
|
|
161
|
-
lines.push('');
|
|
162
|
-
lines.push(`### Recent ${newAgent.role} handoffs`);
|
|
163
|
-
lines.push('');
|
|
164
|
-
lines.push(recentChain);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (discoveries) {
|
|
168
|
-
lines.push('');
|
|
169
|
-
lines.push(`### Known patterns (from prior ${newAgent.role} agents)`);
|
|
170
|
-
lines.push('');
|
|
171
|
-
lines.push(discoveries);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
lines.push('');
|
|
175
|
-
lines.push(`You can contribute to this memory via:`);
|
|
176
|
-
lines.push(`- \`POST /api/memory/discoveries\` — share an error→fix you found`);
|
|
177
|
-
lines.push(`- \`POST /api/memory/constraints\` — declare a project rule you discovered`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
110
|
// Project files section — tell the new agent what exists and what to read
|
|
182
111
|
if (allTeamFiles.length > 0) {
|
|
183
112
|
lines.push('');
|
|
@@ -220,21 +149,11 @@ export class Introducer {
|
|
|
220
149
|
lines.push('');
|
|
221
150
|
lines.push(`## Coordination Protocol`);
|
|
222
151
|
lines.push('');
|
|
223
|
-
lines.push(`Before performing shared/destructive actions (restart server, npm install/build, modify package.json, modify shared config),
|
|
224
|
-
lines.push(
|
|
225
|
-
lines.push(`
|
|
226
|
-
lines.push(
|
|
227
|
-
lines.push(`
|
|
228
|
-
lines.push(`{ "agentId": "${newAgent.id}", "operation": "npm install", "resources": ["package.json", "node_modules"] }`);
|
|
229
|
-
lines.push('```');
|
|
230
|
-
lines.push('');
|
|
231
|
-
lines.push(`Complete (always call this when done, even on failure):`);
|
|
232
|
-
lines.push('```');
|
|
233
|
-
lines.push(`POST http://127.0.0.1:31415/api/coordination/complete`);
|
|
234
|
-
lines.push(`{ "agentId": "${newAgent.id}" }`);
|
|
235
|
-
lines.push('```');
|
|
236
|
-
lines.push('');
|
|
237
|
-
lines.push(`Operations auto-expire after 10 minutes to prevent deadlock.`);
|
|
152
|
+
lines.push(`Before performing shared/destructive actions (restart server, npm install/build, modify package.json, modify shared config), coordinate with your team:`);
|
|
153
|
+
lines.push(`1. Read \`.groove/coordination.md\` to check for active operations`);
|
|
154
|
+
lines.push(`2. Write your intent to \`.groove/coordination.md\` (e.g., "backend-1: restarting server")`);
|
|
155
|
+
lines.push(`3. Proceed only if no conflicting operations are active`);
|
|
156
|
+
lines.push(`4. Clear your entry from \`.groove/coordination.md\` when done`);
|
|
238
157
|
}
|
|
239
158
|
|
|
240
159
|
// File safety — prevent agents from deleting files they didn't create
|
|
@@ -706,14 +706,23 @@ export class Journalist {
|
|
|
706
706
|
`You are **${agent.name}** (role: ${agent.role}). This is an internal context refresh — `,
|
|
707
707
|
`the conversation with the user is ongoing and must feel seamless to them. They cannot see this brief.`,
|
|
708
708
|
``,
|
|
709
|
-
`## CRITICAL:
|
|
709
|
+
`## CRITICAL: Finish What You Were Doing`,
|
|
710
|
+
``,
|
|
711
|
+
`The previous session was mid-work when this refresh happened. YOUR JOB IS TO COMPLETE THAT WORK.`,
|
|
712
|
+
``,
|
|
713
|
+
`- If Recent User Messages show an unanswered request, deliver the answer.`,
|
|
714
|
+
`- If Recent Tool Calls show exploration/planning/building in progress, finish it and deliver the output.`,
|
|
715
|
+
`- If you were a planner about to output a plan, output the plan now.`,
|
|
716
|
+
`- If you were a builder about to make edits, make the edits.`,
|
|
710
717
|
``,
|
|
711
718
|
`Do NOT announce a restart, rotation, or resumption. Do NOT greet the user. Do NOT say `,
|
|
712
|
-
`"resuming", "
|
|
713
|
-
`
|
|
714
|
-
`
|
|
719
|
+
`"resuming", "let me check state again", "here's where I was", or anything that signals a `,
|
|
720
|
+
`break. Do NOT ask "what would you like me to do" — the user already told you. Do NOT `,
|
|
721
|
+
`just wait — the user is waiting for YOUR output.`,
|
|
722
|
+
``,
|
|
723
|
+
`From the user's perspective, the conversation never paused. Act accordingly.`,
|
|
715
724
|
``,
|
|
716
|
-
conversationSummary ? `## Recent User Messages (what they've been asking for)\n\n${conversationSummary}\n` : '',
|
|
725
|
+
conversationSummary ? `## Recent User Messages (what they've been asking for — deliver this)\n\n${conversationSummary}\n` : '',
|
|
717
726
|
recentChain ? `## Rotation History (recent)\n\n${recentChain}\n` : '',
|
|
718
727
|
`## Your Identity`,
|
|
719
728
|
`- Role: ${agent.role}`,
|
|
@@ -722,21 +731,20 @@ export class Journalist {
|
|
|
722
731
|
agent.workingDir ? `- Working directory: ${agent.workingDir}` : '',
|
|
723
732
|
``,
|
|
724
733
|
`## Session State`,
|
|
725
|
-
`- Tokens used: ${agent.tokensUsed}`,
|
|
726
|
-
`- Tool calls: ${entries.filter((e) => e.type === 'tool').length}`,
|
|
734
|
+
`- Tokens used before refresh: ${agent.tokensUsed}`,
|
|
735
|
+
`- Tool calls completed: ${entries.filter((e) => e.type === 'tool').length}`,
|
|
727
736
|
``,
|
|
728
|
-
toolSummary ? `### Recent tool calls\n${toolSummary}\n` : '',
|
|
737
|
+
toolSummary ? `### Recent tool calls (what you were doing)\n${toolSummary}\n` : '',
|
|
729
738
|
resultSummary ? `### Last results\n${resultSummary}\n` : '',
|
|
730
739
|
errorSummary ? `### Unresolved errors\n${errorSummary}\n` : '',
|
|
731
740
|
`## Current Project State`,
|
|
732
741
|
``,
|
|
733
742
|
projectMap ? projectMap.slice(0, 10000) : 'No project map available yet.',
|
|
734
743
|
``,
|
|
735
|
-
`##
|
|
744
|
+
`## Your Next Action`,
|
|
736
745
|
``,
|
|
737
|
-
`
|
|
738
|
-
`
|
|
739
|
-
`that naturally. Match the tone and continuity of a conversation that never paused.`,
|
|
746
|
+
`Look at Recent User Messages and Recent Tool Calls. Decide what output the user is waiting `,
|
|
747
|
+
`for right now. Produce it. No preamble, no announcement, just the answer or work product.`,
|
|
740
748
|
agent.workingDir ? `Stay within your working directory: ${agent.workingDir}.` : '',
|
|
741
749
|
agent.prompt ? `\nOriginal task context: ${agent.prompt}` : '',
|
|
742
750
|
].filter(Boolean).join('\n');
|
|
@@ -196,10 +196,8 @@ Do NOT re-explore the entire codebase. You already know it from team creation.
|
|
|
196
196
|
Just read the specific files related to the bug/feature, decide which existing agent should handle it, and write the routing config. This should be FAST — under 5 tool calls.
|
|
197
197
|
|
|
198
198
|
HOW TO DETECT WHICH MODE:
|
|
199
|
-
-
|
|
200
|
-
- If no
|
|
201
|
-
- Teammates listed as "ready to resume" are REAL agents on your team. They finished their last task and await new instructions. They WILL pick up new work when you route it to them via recommended-team.json. Do NOT treat them as absent.
|
|
202
|
-
- NEVER spawn a new agent of a role that already exists in your team. A second backend when a backend already exists is always a bug.
|
|
199
|
+
- Read AGENTS_REGISTRY.md. If it lists agents with roles matching your team (frontend, backend, fullstack), you are in MODE 2.
|
|
200
|
+
- If no agents exist or only a planner exists, you are in MODE 1.
|
|
203
201
|
|
|
204
202
|
After completing your plan, you MUST write .groove/recommended-team.json — EVERY TIME, no exceptions.
|
|
205
203
|
|
|
@@ -201,6 +201,36 @@ describe('Journalist', () => {
|
|
|
201
201
|
assert.ok(brief.includes('Build the auth API'));
|
|
202
202
|
assert.ok(brief.includes('Write'));
|
|
203
203
|
});
|
|
204
|
+
|
|
205
|
+
it('instructs the agent to deliver the output, not passively wait', async () => {
|
|
206
|
+
// Regression test: v0.27.1 brief told the agent to "wait for the user's
|
|
207
|
+
// next message" which caused planners to sit idle mid-plan after a
|
|
208
|
+
// rotation, burning tokens without producing output. Agents must be
|
|
209
|
+
// told to finish the work in flight, not wait for further prompting.
|
|
210
|
+
const { daemon, grooveDir } = createMockDaemon();
|
|
211
|
+
const journalist = new Journalist(daemon);
|
|
212
|
+
writeFileSync(join(grooveDir, 'logs', 'planner-1.log'), JSON.stringify({
|
|
213
|
+
type: 'assistant',
|
|
214
|
+
message: { content: [{ type: 'tool_use', name: 'Read', input: { file_path: 'src/index.js' } }] },
|
|
215
|
+
}));
|
|
216
|
+
|
|
217
|
+
const agent = {
|
|
218
|
+
id: 'p1', name: 'planner-1', role: 'planner',
|
|
219
|
+
provider: 'claude-code', scope: [],
|
|
220
|
+
tokensUsed: 2_000_000, prompt: 'Plan voice integrations',
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const brief = await journalist.generateHandoffBrief(agent);
|
|
224
|
+
|
|
225
|
+
// The brief MUST NOT contain the old passive instruction
|
|
226
|
+
assert.ok(!/wait for the user's next message/i.test(brief),
|
|
227
|
+
'brief must not tell agent to wait for next message');
|
|
228
|
+
// The brief MUST tell the agent to complete/deliver the work
|
|
229
|
+
assert.ok(/finish|deliver|complete|produce it/i.test(brief),
|
|
230
|
+
'brief must instruct the agent to finish the work');
|
|
231
|
+
// Pass-through: planner-specific instruction
|
|
232
|
+
assert.ok(/output the plan|deliver/i.test(brief));
|
|
233
|
+
});
|
|
204
234
|
});
|
|
205
235
|
|
|
206
236
|
describe('status', () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.5",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -16,30 +16,10 @@ export class Introducer {
|
|
|
16
16
|
generateContext(newAgent, options = {}) {
|
|
17
17
|
const { taskNegotiation } = options;
|
|
18
18
|
const agents = this.daemon.registry.getAll();
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// until resumed. Hiding them from the new agent's context makes planners
|
|
24
|
-
// falsely conclude "I'm alone" and spawn duplicate roles.
|
|
25
|
-
//
|
|
26
|
-
// Scope to the same team so one team's agents don't leak into another's
|
|
27
|
-
// context. Completed teammates get a 1-hour freshness cutoff so truly
|
|
28
|
-
// stale ones don't clutter the intro.
|
|
29
|
-
const COMPLETED_WINDOW_MS = 60 * 60 * 1000;
|
|
30
|
-
const sameTeam = (a) =>
|
|
31
|
-
a.id !== newAgent.id &&
|
|
32
|
-
(!newAgent.teamId || a.teamId === newAgent.teamId);
|
|
33
|
-
const activeOthers = agents.filter((a) =>
|
|
34
|
-
sameTeam(a) && (a.status === 'running' || a.status === 'starting')
|
|
35
|
-
);
|
|
36
|
-
const recentCompleted = agents.filter((a) => {
|
|
37
|
-
if (!sameTeam(a)) return false;
|
|
38
|
-
if (a.status !== 'completed') return false;
|
|
39
|
-
const ts = a.lastActivity ? new Date(a.lastActivity).getTime() : 0;
|
|
40
|
-
return Date.now() - ts < COMPLETED_WINDOW_MS;
|
|
41
|
-
});
|
|
42
|
-
const others = [...activeOthers, ...recentCompleted];
|
|
19
|
+
// Only include ACTIVE agents — not completed/killed ones from previous sessions
|
|
20
|
+
// Completed agents' work is captured in the journalist's project map, not here
|
|
21
|
+
const others = agents.filter((a) => a.id !== newAgent.id &&
|
|
22
|
+
(a.status === 'running' || a.status === 'starting'));
|
|
43
23
|
|
|
44
24
|
const lines = [
|
|
45
25
|
`# GROOVE Agent Context`,
|
|
@@ -62,17 +42,8 @@ export class Introducer {
|
|
|
62
42
|
if (others.length === 0) {
|
|
63
43
|
lines.push('You are the only agent on this project right now.');
|
|
64
44
|
} else {
|
|
65
|
-
|
|
66
|
-
const readyCount = recentCompleted.length;
|
|
67
|
-
const parts = [];
|
|
68
|
-
if (activeCount > 0) parts.push(`${activeCount} active`);
|
|
69
|
-
if (readyCount > 0) parts.push(`${readyCount} ready to resume`);
|
|
70
|
-
lines.push(`## Team (${others.length} teammate${others.length > 1 ? 's' : ''} — ${parts.join(', ')})`);
|
|
45
|
+
lines.push(`## Team (${others.length} other agent${others.length > 1 ? 's' : ''})`);
|
|
71
46
|
lines.push('');
|
|
72
|
-
if (readyCount > 0) {
|
|
73
|
-
lines.push(`**Teammates marked "ready" are part of your team.** They finished their last task and will resume their session when assigned new work. If you're a planner, route new tasks to them by role — do NOT spawn duplicates.`);
|
|
74
|
-
lines.push('');
|
|
75
|
-
}
|
|
76
47
|
|
|
77
48
|
// Collect all files created by teammates for the project files section
|
|
78
49
|
const allTeamFiles = [];
|
|
@@ -80,8 +51,7 @@ export class Introducer {
|
|
|
80
51
|
for (const other of others) {
|
|
81
52
|
const scope = other.scope?.length > 0 ? other.scope.join(', ') : 'unrestricted';
|
|
82
53
|
const dir = other.workingDir ? ` — dir: ${other.workingDir}` : '';
|
|
83
|
-
|
|
84
|
-
lines.push(`- **${other.name}** (${other.role}) — scope: ${scope}${dir} — ${statusLabel}`);
|
|
54
|
+
lines.push(`- **${other.name}** (${other.role}) — scope: ${scope}${dir} — ${other.status}`);
|
|
85
55
|
|
|
86
56
|
// Get files this agent created/modified
|
|
87
57
|
const files = this.daemon.journalist?.getAgentFiles(other) || [];
|
|
@@ -137,47 +107,6 @@ export class Introducer {
|
|
|
137
107
|
}
|
|
138
108
|
}
|
|
139
109
|
|
|
140
|
-
// Project memory (Layer 7) — accumulated wisdom across all prior rotations.
|
|
141
|
-
// Constraints, recent role handoffs, known error→fix patterns. Total cap ~12K chars.
|
|
142
|
-
if (this.daemon.memory) {
|
|
143
|
-
const constraints = this.daemon.memory.getConstraintsMarkdown(4000);
|
|
144
|
-
const recentChain = this.daemon.memory.getRecentHandoffMarkdown(newAgent.role, 3, 4000);
|
|
145
|
-
const discoveries = this.daemon.memory.getDiscoveriesMarkdown(newAgent.role, 20, 4000);
|
|
146
|
-
|
|
147
|
-
if (constraints || recentChain || discoveries) {
|
|
148
|
-
lines.push('');
|
|
149
|
-
lines.push(`## Project Memory`);
|
|
150
|
-
lines.push('');
|
|
151
|
-
lines.push(`This is accumulated knowledge from prior agents working on this project. Read carefully — it will save you from rediscovering what others already learned.`);
|
|
152
|
-
|
|
153
|
-
if (constraints) {
|
|
154
|
-
lines.push('');
|
|
155
|
-
lines.push(`### Constraints`);
|
|
156
|
-
lines.push('');
|
|
157
|
-
lines.push(constraints);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (recentChain) {
|
|
161
|
-
lines.push('');
|
|
162
|
-
lines.push(`### Recent ${newAgent.role} handoffs`);
|
|
163
|
-
lines.push('');
|
|
164
|
-
lines.push(recentChain);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (discoveries) {
|
|
168
|
-
lines.push('');
|
|
169
|
-
lines.push(`### Known patterns (from prior ${newAgent.role} agents)`);
|
|
170
|
-
lines.push('');
|
|
171
|
-
lines.push(discoveries);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
lines.push('');
|
|
175
|
-
lines.push(`You can contribute to this memory via:`);
|
|
176
|
-
lines.push(`- \`POST /api/memory/discoveries\` — share an error→fix you found`);
|
|
177
|
-
lines.push(`- \`POST /api/memory/constraints\` — declare a project rule you discovered`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
110
|
// Project files section — tell the new agent what exists and what to read
|
|
182
111
|
if (allTeamFiles.length > 0) {
|
|
183
112
|
lines.push('');
|
|
@@ -220,21 +149,11 @@ export class Introducer {
|
|
|
220
149
|
lines.push('');
|
|
221
150
|
lines.push(`## Coordination Protocol`);
|
|
222
151
|
lines.push('');
|
|
223
|
-
lines.push(`Before performing shared/destructive actions (restart server, npm install/build, modify package.json, modify shared config),
|
|
224
|
-
lines.push(
|
|
225
|
-
lines.push(`
|
|
226
|
-
lines.push(
|
|
227
|
-
lines.push(`
|
|
228
|
-
lines.push(`{ "agentId": "${newAgent.id}", "operation": "npm install", "resources": ["package.json", "node_modules"] }`);
|
|
229
|
-
lines.push('```');
|
|
230
|
-
lines.push('');
|
|
231
|
-
lines.push(`Complete (always call this when done, even on failure):`);
|
|
232
|
-
lines.push('```');
|
|
233
|
-
lines.push(`POST http://127.0.0.1:31415/api/coordination/complete`);
|
|
234
|
-
lines.push(`{ "agentId": "${newAgent.id}" }`);
|
|
235
|
-
lines.push('```');
|
|
236
|
-
lines.push('');
|
|
237
|
-
lines.push(`Operations auto-expire after 10 minutes to prevent deadlock.`);
|
|
152
|
+
lines.push(`Before performing shared/destructive actions (restart server, npm install/build, modify package.json, modify shared config), coordinate with your team:`);
|
|
153
|
+
lines.push(`1. Read \`.groove/coordination.md\` to check for active operations`);
|
|
154
|
+
lines.push(`2. Write your intent to \`.groove/coordination.md\` (e.g., "backend-1: restarting server")`);
|
|
155
|
+
lines.push(`3. Proceed only if no conflicting operations are active`);
|
|
156
|
+
lines.push(`4. Clear your entry from \`.groove/coordination.md\` when done`);
|
|
238
157
|
}
|
|
239
158
|
|
|
240
159
|
// File safety — prevent agents from deleting files they didn't create
|
|
@@ -706,14 +706,23 @@ export class Journalist {
|
|
|
706
706
|
`You are **${agent.name}** (role: ${agent.role}). This is an internal context refresh — `,
|
|
707
707
|
`the conversation with the user is ongoing and must feel seamless to them. They cannot see this brief.`,
|
|
708
708
|
``,
|
|
709
|
-
`## CRITICAL:
|
|
709
|
+
`## CRITICAL: Finish What You Were Doing`,
|
|
710
|
+
``,
|
|
711
|
+
`The previous session was mid-work when this refresh happened. YOUR JOB IS TO COMPLETE THAT WORK.`,
|
|
712
|
+
``,
|
|
713
|
+
`- If Recent User Messages show an unanswered request, deliver the answer.`,
|
|
714
|
+
`- If Recent Tool Calls show exploration/planning/building in progress, finish it and deliver the output.`,
|
|
715
|
+
`- If you were a planner about to output a plan, output the plan now.`,
|
|
716
|
+
`- If you were a builder about to make edits, make the edits.`,
|
|
710
717
|
``,
|
|
711
718
|
`Do NOT announce a restart, rotation, or resumption. Do NOT greet the user. Do NOT say `,
|
|
712
|
-
`"resuming", "
|
|
713
|
-
`
|
|
714
|
-
`
|
|
719
|
+
`"resuming", "let me check state again", "here's where I was", or anything that signals a `,
|
|
720
|
+
`break. Do NOT ask "what would you like me to do" — the user already told you. Do NOT `,
|
|
721
|
+
`just wait — the user is waiting for YOUR output.`,
|
|
722
|
+
``,
|
|
723
|
+
`From the user's perspective, the conversation never paused. Act accordingly.`,
|
|
715
724
|
``,
|
|
716
|
-
conversationSummary ? `## Recent User Messages (what they've been asking for)\n\n${conversationSummary}\n` : '',
|
|
725
|
+
conversationSummary ? `## Recent User Messages (what they've been asking for — deliver this)\n\n${conversationSummary}\n` : '',
|
|
717
726
|
recentChain ? `## Rotation History (recent)\n\n${recentChain}\n` : '',
|
|
718
727
|
`## Your Identity`,
|
|
719
728
|
`- Role: ${agent.role}`,
|
|
@@ -722,21 +731,20 @@ export class Journalist {
|
|
|
722
731
|
agent.workingDir ? `- Working directory: ${agent.workingDir}` : '',
|
|
723
732
|
``,
|
|
724
733
|
`## Session State`,
|
|
725
|
-
`- Tokens used: ${agent.tokensUsed}`,
|
|
726
|
-
`- Tool calls: ${entries.filter((e) => e.type === 'tool').length}`,
|
|
734
|
+
`- Tokens used before refresh: ${agent.tokensUsed}`,
|
|
735
|
+
`- Tool calls completed: ${entries.filter((e) => e.type === 'tool').length}`,
|
|
727
736
|
``,
|
|
728
|
-
toolSummary ? `### Recent tool calls\n${toolSummary}\n` : '',
|
|
737
|
+
toolSummary ? `### Recent tool calls (what you were doing)\n${toolSummary}\n` : '',
|
|
729
738
|
resultSummary ? `### Last results\n${resultSummary}\n` : '',
|
|
730
739
|
errorSummary ? `### Unresolved errors\n${errorSummary}\n` : '',
|
|
731
740
|
`## Current Project State`,
|
|
732
741
|
``,
|
|
733
742
|
projectMap ? projectMap.slice(0, 10000) : 'No project map available yet.',
|
|
734
743
|
``,
|
|
735
|
-
`##
|
|
744
|
+
`## Your Next Action`,
|
|
736
745
|
``,
|
|
737
|
-
`
|
|
738
|
-
`
|
|
739
|
-
`that naturally. Match the tone and continuity of a conversation that never paused.`,
|
|
746
|
+
`Look at Recent User Messages and Recent Tool Calls. Decide what output the user is waiting `,
|
|
747
|
+
`for right now. Produce it. No preamble, no announcement, just the answer or work product.`,
|
|
740
748
|
agent.workingDir ? `Stay within your working directory: ${agent.workingDir}.` : '',
|
|
741
749
|
agent.prompt ? `\nOriginal task context: ${agent.prompt}` : '',
|
|
742
750
|
].filter(Boolean).join('\n');
|
|
@@ -196,10 +196,8 @@ Do NOT re-explore the entire codebase. You already know it from team creation.
|
|
|
196
196
|
Just read the specific files related to the bug/feature, decide which existing agent should handle it, and write the routing config. This should be FAST — under 5 tool calls.
|
|
197
197
|
|
|
198
198
|
HOW TO DETECT WHICH MODE:
|
|
199
|
-
-
|
|
200
|
-
- If no
|
|
201
|
-
- Teammates listed as "ready to resume" are REAL agents on your team. They finished their last task and await new instructions. They WILL pick up new work when you route it to them via recommended-team.json. Do NOT treat them as absent.
|
|
202
|
-
- NEVER spawn a new agent of a role that already exists in your team. A second backend when a backend already exists is always a bug.
|
|
199
|
+
- Read AGENTS_REGISTRY.md. If it lists agents with roles matching your team (frontend, backend, fullstack), you are in MODE 2.
|
|
200
|
+
- If no agents exist or only a planner exists, you are in MODE 1.
|
|
203
201
|
|
|
204
202
|
After completing your plan, you MUST write .groove/recommended-team.json — EVERY TIME, no exceptions.
|
|
205
203
|
|