opencode-working-memory 1.0.1 → 1.1.0
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/index.ts +103 -78
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -1122,7 +1122,7 @@ Recent session context (auto-managed, sorted by relevance):
|
|
|
1122
1122
|
|
|
1123
1123
|
${sections.join("\n\n")}
|
|
1124
1124
|
|
|
1125
|
-
(${totalItems} items shown
|
|
1125
|
+
(${totalItems} items shown)
|
|
1126
1126
|
</working_memory>
|
|
1127
1127
|
`.trim();
|
|
1128
1128
|
}
|
|
@@ -1463,18 +1463,9 @@ ${context.value || "[No project context set - add relevant file paths, conventio
|
|
|
1463
1463
|
</context>
|
|
1464
1464
|
|
|
1465
1465
|
IMPORTANT: These blocks persist across conversation resets and compaction.
|
|
1466
|
-
Update them regularly using core_memory_update tool
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
- Important project context is discovered (file structures, patterns, conventions)
|
|
1470
|
-
|
|
1471
|
-
When memory blocks approach their character limits, compress or rephrase content.
|
|
1472
|
-
|
|
1473
|
-
**Usage Discipline** (see Core Memory Usage Guidelines above for details):
|
|
1474
|
-
- goal: ONE specific task, not project-wide goals
|
|
1475
|
-
- progress: Checklist format, NO line numbers/commit hashes/API signatures
|
|
1476
|
-
- context: ONLY files you're currently working on, NO type definitions/function signatures
|
|
1477
|
-
- NEVER store: API docs, library types, function signatures (read source instead)
|
|
1466
|
+
Update them regularly using core_memory_update tool. When blocks approach their character limits, compress or rephrase content.
|
|
1467
|
+
|
|
1468
|
+
To mark decisions for automatic capture into working memory, write inline: [Decision: chose X over Y because Z]
|
|
1478
1469
|
</core_memory>
|
|
1479
1470
|
`.trim();
|
|
1480
1471
|
}
|
|
@@ -1488,75 +1479,35 @@ export default async function WorkingMemoryPlugin(
|
|
|
1488
1479
|
): Promise<ReturnType<Plugin>> {
|
|
1489
1480
|
const { directory, client } = input;
|
|
1490
1481
|
|
|
1482
|
+
// Cache for sub-agent detection — avoids repeated API calls per session.
|
|
1483
|
+
// Maps sessionID → parentID (string) or null (root session).
|
|
1484
|
+
const sessionParentCache = new Map<string, string | null>();
|
|
1485
|
+
|
|
1486
|
+
async function isSubAgent(sessionID: string): Promise<boolean> {
|
|
1487
|
+
if (sessionParentCache.has(sessionID)) {
|
|
1488
|
+
return sessionParentCache.get(sessionID) !== null;
|
|
1489
|
+
}
|
|
1490
|
+
try {
|
|
1491
|
+
const result = await client.session.get({ path: { id: sessionID } });
|
|
1492
|
+
const parentID = result.data?.parentID ?? null;
|
|
1493
|
+
sessionParentCache.set(sessionID, parentID);
|
|
1494
|
+
return parentID !== null;
|
|
1495
|
+
} catch {
|
|
1496
|
+
// If we can't determine, assume it's NOT a sub-agent (safe default).
|
|
1497
|
+
sessionParentCache.set(sessionID, null);
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1491
1502
|
return {
|
|
1492
|
-
// Inject core memory
|
|
1493
|
-
//
|
|
1503
|
+
// Inject pressure warnings, core memory, and working memory into the system prompt each turn.
|
|
1504
|
+
// Core memory usage guidelines are in the core_memory_update tool description instead.
|
|
1494
1505
|
"experimental.chat.system.transform": async (hookInput, output) => {
|
|
1495
1506
|
const { sessionID, model } = hookInput;
|
|
1496
1507
|
if (!sessionID) return;
|
|
1497
1508
|
|
|
1498
|
-
//
|
|
1499
|
-
|
|
1500
|
-
const coreMemoryGuidelines = `
|
|
1501
|
-
# Core Memory Usage Guidelines
|
|
1502
|
-
|
|
1503
|
-
The Working Memory Plugin provides persistent core_memory blocks. **USE THEM CORRECTLY**:
|
|
1504
|
-
|
|
1505
|
-
## goal block (1000 chars)
|
|
1506
|
-
**Purpose**: ONE specific task you're working on RIGHT NOW
|
|
1507
|
-
|
|
1508
|
-
✅ **GOOD Examples**:
|
|
1509
|
-
- "Fix pruning bug where items with relevanceScore <0.01 are incorrectly excluded"
|
|
1510
|
-
- "Add new tool: working_memory_search to query pool items by keyword"
|
|
1511
|
-
- "Investigate why pressure warnings not showing in system prompt"
|
|
1512
|
-
|
|
1513
|
-
❌ **BAD Examples**:
|
|
1514
|
-
- "Complete Phase 1-4 development and testing" (too broad, likely already done)
|
|
1515
|
-
- "Build a working memory system for OpenCode" (project-level goal, not task-level)
|
|
1516
|
-
|
|
1517
|
-
## progress block (2000 chars)
|
|
1518
|
-
**Purpose**: Checklist of done/in-progress/blocked items + key decisions
|
|
1519
|
-
|
|
1520
|
-
✅ **GOOD Examples**:
|
|
1521
|
-
- "✅ Found bug in applyDecay() line 856\\n⏳ Testing fix with gamma=0.85\\n❓ Need to verify edge case: score=0"
|
|
1522
|
-
- "✅ Phase 1-3 complete\\n⏳ Phase 4 intervention testing\\n⚠️ BLOCKED: Need promptAsync docs"
|
|
1523
|
-
|
|
1524
|
-
❌ **BAD Examples**:
|
|
1525
|
-
- "Function sendPressureInterventionMessage() @ working-memory.ts:L1286-1354" (line numbers useless after edits)
|
|
1526
|
-
- "Commit 2f42f1b implemented promptAsync integration" (commit hash irrelevant)
|
|
1527
|
-
- "API: client.session.promptAsync({ path: {id}, body: {...} })" (API signature, not progress)
|
|
1528
|
-
|
|
1529
|
-
## context block (1500 chars)
|
|
1530
|
-
**Purpose**: Files you're CURRENTLY editing + key patterns/conventions
|
|
1531
|
-
|
|
1532
|
-
✅ **GOOD Examples**:
|
|
1533
|
-
- "Editing: .opencode/plugins/working-memory.ts (main plugin, 1706 lines)\\nRelated: WORKING_MEMORY.md, TEST_PHASE4.md"
|
|
1534
|
-
- "Key paths: .opencode/memory-core/ (persistent blocks), memory-working/ (session data)"
|
|
1535
|
-
- "Pattern: All async file ops use mkdir({recursive:true}) before writeFile"
|
|
1536
|
-
|
|
1537
|
-
❌ **BAD Examples**:
|
|
1538
|
-
- "OpenCode SDK types: TextPartInput = { type: 'text', text: string, synthetic?: boolean }" (type definition)
|
|
1539
|
-
- "Function signature: async function loadCoreMemory(directory: string, sessionID: string): Promise<CoreMemory | null>" (function signature)
|
|
1540
|
-
- "Method client.session.promptAsync() returns 204 No Content" (API behavior, read docs instead)
|
|
1541
|
-
|
|
1542
|
-
## ⚠️ NEVER Store in Core Memory
|
|
1543
|
-
- API documentation (read source/docs when needed)
|
|
1544
|
-
- Type definitions from libraries (import them)
|
|
1545
|
-
- Function signatures (read source code)
|
|
1546
|
-
- Implementation details (belong in code comments)
|
|
1547
|
-
- Completed goals (clear them immediately)
|
|
1548
|
-
|
|
1549
|
-
## ✅ Update Core Memory Immediately When
|
|
1550
|
-
- **Starting new task**: Clear old goal, set new specific goal
|
|
1551
|
-
- **Making progress**: Update progress checklist (keep concise)
|
|
1552
|
-
- **Switching files**: Update context with current working files
|
|
1553
|
-
- **Task completed**: Clear goal/progress, set next task
|
|
1554
|
-
- **Approaching char limit**: Compress or remove outdated info
|
|
1555
|
-
|
|
1556
|
-
**Remember**: Core Memory is your **working scratchpad**, not a reference manual.
|
|
1557
|
-
`.trim();
|
|
1558
|
-
|
|
1559
|
-
output.system.push(coreMemoryGuidelines);
|
|
1509
|
+
// Sub-agents are short-lived — skip entire memory system.
|
|
1510
|
+
if (await isSubAgent(sessionID)) return;
|
|
1560
1511
|
|
|
1561
1512
|
// Check for memory pressure and inject warning into system prompt.
|
|
1562
1513
|
// Skip if model just changed (avoids false alarms with different limits).
|
|
@@ -1630,6 +1581,9 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1630
1581
|
const { sessionID, callID, tool: toolName, args } = hookInput;
|
|
1631
1582
|
const { output: toolOutput } = hookOutput;
|
|
1632
1583
|
|
|
1584
|
+
// Sub-agents don't need working memory tracking.
|
|
1585
|
+
if (await isSubAgent(sessionID)) return;
|
|
1586
|
+
|
|
1633
1587
|
await cacheToolOutput(directory, {
|
|
1634
1588
|
callID,
|
|
1635
1589
|
sessionID,
|
|
@@ -1654,6 +1608,9 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1654
1608
|
"experimental.chat.messages.transform": async (hookInput, output) => {
|
|
1655
1609
|
const sessionID = output.messages[0]?.info?.sessionID || "";
|
|
1656
1610
|
|
|
1611
|
+
// Sub-agents don't need smart pruning.
|
|
1612
|
+
if (sessionID && await isSubAgent(sessionID)) return;
|
|
1613
|
+
|
|
1657
1614
|
// Load current pressure info to get pressure-aware pruning config
|
|
1658
1615
|
const currentPressure = await loadModelPressureInfo(directory, sessionID);
|
|
1659
1616
|
const pressureLevel = currentPressure?.current?.pressure || 0;
|
|
@@ -1690,6 +1647,30 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1690
1647
|
}
|
|
1691
1648
|
},
|
|
1692
1649
|
|
|
1650
|
+
// Auto-capture [Decision: ...] markers from agent responses.
|
|
1651
|
+
"experimental.text.complete": async (hookInput, output) => {
|
|
1652
|
+
const { sessionID } = hookInput;
|
|
1653
|
+
if (!sessionID) return;
|
|
1654
|
+
|
|
1655
|
+
// Sub-agents are short-lived — skip decision tracking.
|
|
1656
|
+
if (await isSubAgent(sessionID)) return;
|
|
1657
|
+
|
|
1658
|
+
// Extract all [Decision: ...] markers from the completed text.
|
|
1659
|
+
const matches = output.text.matchAll(/\[Decision:\s*([^\]]+)\]/gi);
|
|
1660
|
+
for (const match of matches) {
|
|
1661
|
+
const description = match[1].trim();
|
|
1662
|
+
if (!description) continue;
|
|
1663
|
+
|
|
1664
|
+
await addToWorkingMemory(directory, sessionID, {
|
|
1665
|
+
type: "decision",
|
|
1666
|
+
content: description,
|
|
1667
|
+
source: "auto:text",
|
|
1668
|
+
timestamp: Date.now(),
|
|
1669
|
+
mentions: 1,
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
},
|
|
1673
|
+
|
|
1693
1674
|
// Clean up all session artifacts on session deletion.
|
|
1694
1675
|
event: async ({ event }) => {
|
|
1695
1676
|
if (event.type === "session.deleted") {
|
|
@@ -1704,6 +1685,9 @@ The Working Memory Plugin provides persistent core_memory blocks. **USE THEM COR
|
|
|
1704
1685
|
"experimental.session.compacting": async (hookInput, output) => {
|
|
1705
1686
|
const { sessionID } = hookInput;
|
|
1706
1687
|
|
|
1688
|
+
// Sub-agents don't need compaction support.
|
|
1689
|
+
if (await isSubAgent(sessionID)) return;
|
|
1690
|
+
|
|
1707
1691
|
// Preserve only the most relevant working memory items
|
|
1708
1692
|
const preservedItems = await preserveRelevantItems(directory, sessionID, 0.5);
|
|
1709
1693
|
|
|
@@ -1795,7 +1779,48 @@ Operations:
|
|
|
1795
1779
|
- append: Add content to the end of the block (automatically adds newline)
|
|
1796
1780
|
|
|
1797
1781
|
These blocks are ALWAYS visible to you in every message, even after compaction.
|
|
1798
|
-
Update them regularly to maintain continuity across long conversations
|
|
1782
|
+
Update them regularly to maintain continuity across long conversations.
|
|
1783
|
+
|
|
1784
|
+
---
|
|
1785
|
+
|
|
1786
|
+
## Usage Guidelines
|
|
1787
|
+
|
|
1788
|
+
### goal block (1000 chars)
|
|
1789
|
+
**Purpose**: ONE specific task you're working on RIGHT NOW
|
|
1790
|
+
|
|
1791
|
+
✅ GOOD: "Fix pruning bug where items with relevanceScore <0.01 are incorrectly excluded"
|
|
1792
|
+
✅ GOOD: "Add new tool: working_memory_search to query pool items by keyword"
|
|
1793
|
+
❌ BAD: "Complete Phase 1-4 development and testing" (too broad, likely already done)
|
|
1794
|
+
❌ BAD: "Build a working memory system for OpenCode" (project-level goal, not task-level)
|
|
1795
|
+
|
|
1796
|
+
### progress block (2000 chars)
|
|
1797
|
+
**Purpose**: Checklist of done/in-progress/blocked items + key decisions
|
|
1798
|
+
|
|
1799
|
+
✅ GOOD: "✅ Found bug in applyDecay()\\n⏳ Testing fix with gamma=0.85\\n❓ Need to verify edge case"
|
|
1800
|
+
✅ GOOD: "✅ Phase 1-3 complete\\n⏳ Phase 4 intervention testing\\n⚠️ BLOCKED: Need promptAsync docs"
|
|
1801
|
+
❌ BAD: "Function foo() @ file.ts:L1286-1354" (line numbers useless after edits)
|
|
1802
|
+
❌ BAD: "Commit 2f42f1b implemented X" (commit hash irrelevant)
|
|
1803
|
+
❌ BAD: "API: client.session.promptAsync({ ... })" (API signatures belong in source)
|
|
1804
|
+
|
|
1805
|
+
### context block (1500 chars)
|
|
1806
|
+
**Purpose**: Files you're CURRENTLY editing + key patterns/conventions
|
|
1807
|
+
|
|
1808
|
+
✅ GOOD: "Editing: src/plugin.ts\\nKey paths: .opencode/memory-core/ (persistent blocks)"
|
|
1809
|
+
✅ GOOD: "Pattern: All async file ops use mkdir({recursive:true}) before writeFile"
|
|
1810
|
+
❌ BAD: Type definitions, function signatures, API docs (read source/docs instead)
|
|
1811
|
+
|
|
1812
|
+
### NEVER store in core memory
|
|
1813
|
+
- API documentation (read source/docs when needed)
|
|
1814
|
+
- Type definitions from libraries (import them)
|
|
1815
|
+
- Function signatures (read source code)
|
|
1816
|
+
- Completed goals (clear them immediately)
|
|
1817
|
+
|
|
1818
|
+
### Update core memory immediately when
|
|
1819
|
+
- Starting new task: clear old goal, set new specific goal
|
|
1820
|
+
- Making progress: update progress checklist (keep concise)
|
|
1821
|
+
- Switching files: update context with current working files
|
|
1822
|
+
- Task completed: clear goal/progress, set next task
|
|
1823
|
+
- Approaching char limit: compress or remove outdated info`,
|
|
1799
1824
|
args: {
|
|
1800
1825
|
block: tool.schema.enum(["goal", "progress", "context"]).describe(
|
|
1801
1826
|
"Which memory block to update (goal/progress/context)"
|
package/package.json
CHANGED