Architecture review fixes:
- Promotion accounting: only clear pending memories that survived
workspace memory normalization/cap limits. Use retainedKeys from
the returned normalized store instead of attemptedKeys.
- Shared sessionID extraction: add sessionIDFromEventProperties()
helper and use it in both session.compacted and session.deleted,
fixing the previous gap where session.deleted only read info.id.
- Strengthen compaction refresh test: seed workspace memory before
first transform so firstSystem1 is non-empty, then assert
refreshed system[1] preserves existing entries AND contains
promoted memories.
P0c fixes:
- Chinese file count regex now accepts 個/个 between number and 文件
- Admin PIN short reference (<20 chars) passes via config value allowlist
- Phase snapshot uses semantic window (.{0,20}) instead of absolute position
P0d fixes:
- Feedback key split: 500 error and port issue remain separate entries
- extractEntityKey avoids over-merging unrelated plugin configs
- chooseBetterMemory supports supersession mode (newer beats longer)
- Sort comparator now includes source priority as secondary tie-breaker
New regression tests (11 total):
- Real Admin PIN short reference passes
- Real Chinese 37 個文件 snapshot rejected
- Real pathology Phase 1-4 snapshot rejected
- Feedback 500 vs port entries not collapsed
- Unrelated plugin configs not collapsed
- Supersession prefers newer shorter over older longer
67/67 tests pass.
P0a: Parser now accepts both - [type] text and - type text formats
P0b: Prompt adds durable-content guidance to avoid session-specific snapshots
P0c: Parser quality gate rejects exact test counts, file counts, phase progress
- Only rejects phase progress when it appears early in the string (snapshot)
- Stable config values with numbers (Admin PIN, Scrypt) still pass
- Adds 7 new tests covering bracketless parsing and snapshot rejection
Root cause: OpenCode's default compaction template uses --- separators.
When our plugin adds structured context (Memory candidates: format), the
model strictly follows the template, outputting --- at position 0. The
markdown textmate grammar treats this as YAML frontmatter, applying the
'comment' syntax scope (purple + italic in themes like palenight).
Fix: Set output.prompt in the compacting hook to replace the entire
template with a ---free version. Uses only ## Markdown headings and
explicitly forbids YAML frontmatter, horizontal rules, and delimiter
lines. Preserves context from other plugins by merging output.context.
- Replace compactionContextHeader() with buildCompactionPrompt()
- Set output.prompt instead of pushing to output.context
- Merge existing output.context from other plugins before clearing
- Add 'Instructions' section to the template (per architect review)
- Update tests: verify output.prompt, ---free format, context merging
- Changed '## Memory Candidates' to 'Memory candidates:' in compaction context
- Changed '## Pending Todos' to 'Pending todos:' in todo rendering
- Updated extractCandidateBlock() to parse plain text format (primary)
- Removed stripXmlTags() function (no longer needed)
- All 42 tests pass
Root cause: Markdown headings (##) render as purple in OpenCode UI,
same issue as XML tags and HTML comments. Plain text labels avoid
all special markup rendering.
Root cause: Model was instructed to output <workspace_memory_candidates> XML
tags in the user-visible compaction summary, causing purple/italic rendering
when combined with --- delimiters in Markdown.
Fixes:
- compactionContextHeader(): Now instructs model to use HTML comment format
<!-- workspace_memory_candidates ... --> which is hidden from users
- extractCandidateBlock(): New function supports 3 formats:
1. HTML comment (preferred, hidden from user)
2. Markdown section (visible but clean)
3. Legacy XML (backward compatible)
- Added "DO NOT use XML tags" and "DO NOT start with ---" instructions
Tests:
- Verify compaction context header uses HTML comment format
- Test parser accepts all 3 formats (HTML comment, Markdown, legacy XML)
- Add stripXmlTags() to convert <workspace_memory>, <hot_session_state>, <pending_todos> to Markdown headers for compaction context
- Add [PRIVATE COMPACTION CONTEXT - DO NOT OUTPUT] wrapper to prevent model from copying input context to output
- Rename renderTodos to renderTodosForCompaction for clarity
- Add test to verify compaction context contains no XML tags
This fixes the issue where compaction summary would render with purple italic text
due to --- delimiters interacting with XML-like tags in Markdown.
- Bump version to 1.2.0
- Add package.json exports and files whitelist
- Update .gitignore to exclude .opencode/ and .opencode-agenthub/
- Fix docs: active files tracked in tool.execute.after (not before)
- Fix docs: exitCode undefined is ignored (not success)
- Fix docs: session ID is hashed in storage path
- Fix docs: workspace memory survives within same workspace
- Replace four-tier architecture with three-layer Memory V2
- Remove references to non-existent tools (core_memory_*, working_memory_*)
- Update storage paths to ~/.local/share/opencode-working-memory/
- Update configuration to LONG_TERM_LIMITS and HOT_STATE_LIMITS
- Fix installation verification to check system prompt instead of tools
## Task 1: Fix exitCode undefined false positive
- Add `typeof exitCode !== "number"` check in plugin.ts
- Only extract errors when exitCode is explicitly non-zero
- Prevent git-log/cat with "errors" text from creating false positives
## Task 2: Fix workspace memory XML truncation
- Budget-aware line-by-line rendering
- Always include closing </workspace_memory> tag
- Return empty string when budget too small
- Bonus: canonical exact deduplication with source priority
## Task 3: Remove "always" as trigger
- Replace "always" with "going forward" in patterns
- Add word boundary via `g` flag and matchAll loop
- "from now on" still works as expected
## Task 4: Verification
- 22 tests passing
- typecheck passing
Tests cover:
- git log/cat with loose "errors" ignored
- TS2345/TypeError strong signals captured
- undefined exitCode: no create, no clear
- exitCode 0: clears errors
- exitCode non-zero: creates error
- XML never truncated mid-tag
- "always" not a trigger
- Use TS2345 error instead of loose 'errors' word
- Add try/finally for temp directory cleanup
- Remove duplicate placeholder test block
- Clarify quality gate placement in parseWorkspaceMemoryCandidates()
- Replace 2000+ line monolithic index.ts with thin wrapper importing MemoryV2Plugin
- Update package.json description to reflect three-layer architecture
- Add test script for Node.js built-in test runner
- Replace sqlite3 CLI with client.session.messages() and client.session.todo()
- Fix message scan order: API returns oldest-first, now scans backwards
- Include cache.read in total token calculation
- Skip in-progress messages with zero tokens
- Silent error handling (no console.error for expected failures)