Remove stale add-compact skill, improve memory handoff with group-aware search

- Delete .agent/skills/add-compact/ (superseded by real implementation)
- Remove from library.yaml and ASPIRATIONAL_SKILLS
- compactSession now tags memories with groupFolder for targeted recall
- Memory handoff uses searchMemories(group) before falling back to getImportantMemories

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mevy Assistant 2026-04-20 11:23:46 +00:00
parent ef0baf9e95
commit 7d06df9bf9
5 changed files with 31 additions and 112 deletions

View file

@ -1,97 +0,0 @@
---
name: add-compact
description: Add /compact command for manual session reset with context summary. Reduces context bloat in long pi sessions by summarizing the conversation and starting a fresh pi session, preserving key context as a handoff prompt.
---
# Add /compact Command
Adds a `/compact` session command that fights context rot in long-running sessions. Unlike NanoClaw's Claude-SDK-based compaction, Clawdie uses pi as the agent runtime — which has no built-in `/compact`. Instead, we implement it by:
1. Asking pi to summarize the current session as a structured handoff
2. Starting a fresh pi session (`--no-session`) with the summary injected as context
3. Returning the new session ID to the orchestrator
**Main-group or trusted sender only.**
## Phase 1: Pre-flight
Check if `src/session-commands.ts` exists:
```bash
test -f src/session-commands.ts && echo "Already applied" || echo "Not applied"
```
If already applied, skip to Phase 3 (Verify).
## Phase 2: Apply Code Changes
### 1. Create `src/session-commands.ts`
Handles detection and authorization of slash commands in incoming messages:
```typescript
// Detect /compact in message text
// Returns true only for main-group or is_from_me (admin) senders
export function isCompactCommand(text: string): boolean { ... }
export function isAuthorizedForSessionCommands(isMain: boolean, isFromMe: boolean): boolean { ... }
```
### 2. Create `src/session-commands.test.ts`
Unit tests for command parsing and auth logic.
### 3. Intercept in `src/index.ts`
In `processGroupMessages`, before passing the prompt to `runJailAgent`:
```typescript
if (isCompactCommand(messageText) && isAuthorizedForSessionCommands(isMain, isFromMe)) {
// 1. Ask pi to summarize the current session
const summaryOutput = await runJailAgent(group, {
prompt: 'Summarize our conversation so far in 2-3 paragraphs covering key context, decisions made, and any open tasks. Be concise.',
sessionId: currentSessionId,
groupFolder, chatJid, isMain,
}, onProcess);
// 2. Start fresh session with summary as context
const compactOutput = await runJailAgent(group, {
prompt: `[Compacted context from previous session]\n\n${summaryOutput.result}\n\n[New session starts here. Continue from this context.]`,
sessionId: undefined, // fresh session
groupFolder, chatJid, isMain,
}, onProcess);
// 3. Send acknowledgement
await sendMessage(chatJid, 'Session compacted. Continuing with summarized context.');
return;
}
```
### Validate
```bash
npm test
npm run build
```
## Phase 3: Verify
1. Start Clawdie: `sudo service clawdie start`
2. Have a multi-turn conversation in the **main group** to build up context.
3. Send: `/compact`
4. Verify:
- Bot acknowledges compaction
- Session continues with summarized context
- `groups/{folder}/sessions/` shows a new `.jsonl` session file
5. From a **non-main group** as non-admin, send: `@<assistant> /compact`
6. Verify: bot responds "Session commands require admin access."
## Security Constraints
- **Main-group or admin only.** Non-main groups are untrusted.
- **No auto-compaction.** Manual only.
- **Summary step uses the existing session** — no data loss before reset.
## Troubleshooting
- **Summary is empty:** Pi may have hit an error or token limit. Check `groups/{folder}/logs/agent-*.log`.
- **New session not created:** Verify `PI_TUI_NO_SESSION` is not set in `.env`.

View file

@ -262,11 +262,6 @@ features:
tags: [channel, voice, ml]
manifest: manifest.yaml
- id: feature/add-compact
source: local:.agent/skills/add-compact/
description: Add /compact command for manual session reset with context summary handoff
tags: [agent, memory]
- id: feature/add-parallel
source: local:.agent/skills/add-parallel/
description: Parallel AI MCP integration for multi-step web research

View file

@ -33,7 +33,7 @@ import { resolveGroupFolderPath, resolveGroupIpcPath } from './group-folder.js';
import { markJailRunFinished, markJailRunStarted } from './health.js';
import { incCounter, incLabeledCounter, registerGauge } from './metrics.js';
import { compactSession } from './session-compaction.js';
import { getImportantMemories } from './memory-pg.js';
import { getImportantMemories, searchMemories } from './memory-pg.js';
const METRICS_PREFIX = `${AGENT_NAME}_`;
import { logger } from './logger.js';
@ -256,7 +256,9 @@ export async function runJailAgent(
if (oversize && input.sessionId) {
const sessionFile = path.join(sessionDir, input.sessionId);
try {
const compactResult = await compactSession(sessionFile);
const compactResult = await compactSession(sessionFile, {
groupFolder: input.groupFolder,
});
if (compactResult.compacted) {
logger.info(
{
@ -310,15 +312,32 @@ export async function runJailAgent(
// so the agent doesn't amnesia completely.
if (needsMemoryHandoff) {
try {
const memories = await getImportantMemories(5);
if (memories.length > 0) {
const handoffLines = memories.map((m) => {
const summary =
// Try group-aware search first (finds compaction summaries tagged with this group),
// fall back to generic important memories if search returns nothing.
const searchResults = await searchMemories(
`session-compaction ${input.groupFolder}`,
5,
).catch(() => []);
const handoffLines: string[] = [];
if (searchResults.length > 0) {
for (const r of searchResults) {
const text =
r.summary.length > 300
? r.summary.slice(0, 300) + '...'
: r.summary;
handoffLines.push(`- ${text}`);
}
} else {
const memories = await getImportantMemories(5);
for (const m of memories) {
const text =
m.summary.length > 300
? m.summary.slice(0, 300) + '...'
: m.summary;
return `- ${summary}`;
});
handoffLines.push(`- ${text}`);
}
}
if (handoffLines.length > 0) {
const handoff = [
'<session-reset-context>',
'Previous session was reset. Key context from memory:',

View file

@ -217,6 +217,7 @@ export async function compactSession(
keepTurns?: number;
minEntries?: number;
enabled?: boolean;
groupFolder?: string;
} = {},
): Promise<CompactionResult> {
const {
@ -289,8 +290,10 @@ export async function compactSession(
let memoryStored = false;
try {
const topics = ['session-compaction'];
if (options.groupFolder) topics.push(options.groupFolder);
await storeMemory(summary, {
topics: ['session-compaction'],
topics,
importance: 4,
keyFacts: old
.filter((e) => e.result === 'success' && e.task)

View file

@ -108,7 +108,6 @@ function extractRepoPaths(content: string): string[] {
* files the skill will create when applied.
*/
const ASPIRATIONAL_SKILLS = new Set([
'add-compact',
'add-discord',
'add-gmail',
'add-parallel',