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.
Files changed (2) hide show
  1. package/index.ts +103 -78
  2. 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, updated: ${new Date(memory.updatedAt).toLocaleTimeString()})
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 when:
1467
- - Goals change or new objectives are identified
1468
- - Significant progress is made or tasks are completed
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 usage guidelines, pressure warnings, core memory,
1493
- // and working memory into the system prompt each turn.
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
- // Inject core memory usage guidelines.
1499
- // Inserted before <core_memory> so the agent reads guidance first.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-working-memory",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Advanced four-tier memory architecture for OpenCode with intelligent pressure monitoring and auto-storage governance",
5
5
  "type": "module",
6
6
  "main": "index.ts",