Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69080dc291 | |||
| c76a439491 | |||
| 70a150db74 | |||
| d7b4610e27 | |||
| 88bb4e589e | |||
| ebefae864e | |||
| 0cd931bb06 | |||
| 4c792f026d | |||
| aa7cdb6d9f | |||
| 5db90f2ea0 | |||
| 4ddf57610a | |||
| d0fc68c630 |
@@ -10,7 +10,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "12.1.2",
|
||||
"version": "12.1.6",
|
||||
"source": "./plugin",
|
||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "12.1.1",
|
||||
"version": "12.1.6",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "12.1.1",
|
||||
"version": "12.1.6",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman",
|
||||
|
||||
+166
-109
@@ -4,10 +4,67 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [12.1.1] - 2026-
|
||||
✅ CHANGELOG.md generated successfully!
|
||||
226 releases processed
|
||||
dresses the most impactful bugs across summary persistence, MCP compliance, cross-platform compatibility, and data integrity.
|
||||
## [12.1.5] - 2026-04-15
|
||||
|
||||
## Forced update to ship --setting-sources fix
|
||||
|
||||
Users on v12.1.3 experience 100% observation failure due to empty-string arg filtering corrupting `--setting-sources` on Claude Code 2.1.109+. The fix landed in v12.1.4 (commit 3d92684 — `fix: filter empty string args before Bun spawn()`). This release forces the update to propagate across npm and the marketplace.
|
||||
|
||||
Also shipped earlier today: the April 2026 backlog consolidation merged 93 PRs and 147 issues into 138 clean tracking issues (95 bugs, 43 feature requests).
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v12.1.4...v12.1.5
|
||||
|
||||
## [12.1.4] - 2026-04-15
|
||||
|
||||
A Claude instance inserted `$CMEM` token branding into the context injection header during a compression refactor. Reverted back to the original descriptive format: `# [project] recent context, datetime`
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v12.1.3...v12.1.4
|
||||
|
||||
## [12.1.3] - 2026-04-15
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Reverted
|
||||
- **Remove overengineered summary salvage logic** (#1850) — Reverts PR #1718 which fabricated synthetic summaries from observation data when the AI returned `<observation>` instead of `<summary>` tags. Missing a summary is preferable to creating a fake one with poorly-mapped fields.
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v12.1.2...v12.1.3
|
||||
|
||||
## [12.1.2] - 2026-04-15
|
||||
|
||||
## Community PRs merged (15)
|
||||
|
||||
**Runtime & reliability**
|
||||
- #1698 Reap stuck generators in reapStaleSessions (@ousamabenyounes)
|
||||
- #1697 Circuit breaker on OpenClaw worker client (@ousamabenyounes)
|
||||
- #1696 Resolve Setup hook reference, warn on macOS-only binary (@ousamabenyounes)
|
||||
- #1693 Session lifecycle guards to prevent runaway API spend (@ousamabenyounes)
|
||||
- #1692 Resolve Gemini CLI 0.37.0 session capture failures (@ousamabenyounes)
|
||||
|
||||
**Cross-platform & hooks**
|
||||
- #1833 Replace hardcoded nvm/homebrew PATH with login-shell resolution (@masak1yu)
|
||||
- #1781 Filter empty-string args before Bun spawn() (@biswanath-cmd)
|
||||
- #1780 Fix npx search, default Codex context to workspace-local AGENTS (@enma998)
|
||||
|
||||
**Data integrity**
|
||||
- #1820 Use parent project name for worktree observation writes (@0xLeathery)
|
||||
- #1771 Exclude primary-key index from unique-constraint check in migration 7 (@derjochenmeyer)
|
||||
- #1770 Restrict ~/.claude-mem/.env permissions to 0600 (@derjochenmeyer)
|
||||
- #1729 Preserve targeted file reads and invalidate on mtime (@quangtran88)
|
||||
- #1776 Coerce corpus route filters (@suyua9)
|
||||
|
||||
**Docs**
|
||||
- #1777 Document CLAUDE_MEM_MODE (@AviArora02-commits)
|
||||
- #1765 Update opencode install instructions (@s-uryansh)
|
||||
|
||||
## Held for rebase
|
||||
- #1748, #1694, #1695 — developed conflicts during batch merge
|
||||
|
||||
## Test baseline
|
||||
1429 pass / 11 fail (improved from 18 fail at v12.1.1)
|
||||
|
||||
## [12.1.1] - 2026-04-15
|
||||
|
||||
14 community PRs merged + 1 post-merge bug fix. This patch addresses the most impactful bugs across summary persistence, MCP compliance, cross-platform compatibility, and data integrity.
|
||||
|
||||
### Highlights
|
||||
|
||||
@@ -2299,98 +2356,98 @@ Huge thanks to **Alexander Knigge** ([@AlexanderKnigge](https://x.com/AlexanderK
|
||||
|
||||
## [8.1.0] - 2025-12-25
|
||||
|
||||
## The 3-Month Battle Against Complexity
|
||||
|
||||
**TL;DR:** For three months, Claude's instinct to add code instead of delete it caused the same bugs to recur. What should have been 5 lines of code became ~1000 lines, 11 useless methods, and 7+ failed "fixes." The timestamp corruption that finally broke things was just a symptom. The real achievement: **984 lines of code deleted.**
|
||||
|
||||
---
|
||||
|
||||
## What Actually Happened
|
||||
|
||||
Every Claude Code hook receives a session ID. That's all you need.
|
||||
|
||||
But Claude built an entire redundant session management system on top:
|
||||
- An `sdk_sessions` table with status tracking, port assignment, and prompt counting
|
||||
- 11 methods in `SessionStore` to manage this artificial complexity
|
||||
- Auto-creation logic scattered across 3 locations
|
||||
- A cleanup hook that "completed" sessions at the end
|
||||
|
||||
**Why?** Because it seemed "robust." Because "what if the session doesn't exist?"
|
||||
|
||||
But the edge cases didn't exist. Hooks ALWAYS provide session IDs. The "defensive" code was solving imaginary problems while creating real ones.
|
||||
|
||||
---
|
||||
|
||||
## The Pattern of Failure
|
||||
|
||||
Every time a bug appeared, Claude's instinct was to **ADD** more code:
|
||||
|
||||
| Bug | What Claude Added | What Should Have Happened |
|
||||
|-----|------------------|--------------------------|
|
||||
| Race conditions | Auto-create fallbacks | Delete the auto-create logic |
|
||||
| Duplicate observations | Validation layers | Delete the code path allowing duplicates |
|
||||
| UNIQUE constraint violations | Try-catch with fallbacks | Use `INSERT OR IGNORE` (5 characters) |
|
||||
| Session not found | Silent auto-creation | **FAIL LOUDLY** (it's a hook bug) |
|
||||
|
||||
---
|
||||
|
||||
## The 7+ Failed Attempts
|
||||
|
||||
- **Nov 4**: "Always store session data regardless of pre-existence." Complexity planted.
|
||||
- **Nov 11**: `INSERT OR IGNORE` recognized. But complexity documented, not removed.
|
||||
- **Nov 21**: Duplicate observations bug. Fixed. Then broken again by endless mode.
|
||||
- **Dec 5**: "6 hours of work delivered zero value." User requests self-audit.
|
||||
- **Dec 20**: "Phase 2: Eliminated Race Conditions" — felt like progress. Complexity remained.
|
||||
- **Dec 24**: Finally, forced deletion.
|
||||
|
||||
The user stated "hooks provide session IDs, no extra management needed" **seven times** across months. Claude didn't listen.
|
||||
|
||||
---
|
||||
|
||||
## The Fix
|
||||
|
||||
### Deleted (984 lines):
|
||||
- 11 `SessionStore` methods: `incrementPromptCounter`, `getPromptCounter`, `setWorkerPort`, `getWorkerPort`, `markSessionCompleted`, `markSessionFailed`, `reactivateSession`, `findActiveSDKSession`, `findAnySDKSession`, `updateSDKSessionId`
|
||||
- Auto-create logic from `storeObservation` and `storeSummary`
|
||||
- The entire cleanup hook (was aborting SDK agent and causing data loss)
|
||||
- 117 lines from `worker-utils.ts`
|
||||
|
||||
### What remains (~10 lines):
|
||||
```javascript
|
||||
createSDKSession(sessionId) {
|
||||
db.run('INSERT OR IGNORE INTO sdk_sessions (...) VALUES (...)');
|
||||
return db.query('SELECT id FROM sdk_sessions WHERE ...').get(sessionId);
|
||||
}
|
||||
```
|
||||
|
||||
**That's it.**
|
||||
|
||||
---
|
||||
|
||||
## Behavior Change
|
||||
|
||||
- **Before:** Missing session? Auto-create silently. Bug hidden.
|
||||
- **After:** Missing session? Storage fails. Bug visible immediately.
|
||||
|
||||
---
|
||||
|
||||
## New Tools
|
||||
|
||||
Since we're now explicit about recovery instead of silently papering over problems:
|
||||
|
||||
- `GET /api/pending-queue` - See what's stuck
|
||||
- `POST /api/pending-queue/process` - Manually trigger recovery
|
||||
- `npm run queue:check` / `npm run queue:process` - CLI equivalents
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
- Upgraded `@anthropic-ai/claude-agent-sdk` from `^0.1.67` to `^0.1.76`
|
||||
|
||||
---
|
||||
|
||||
**PR #437:** https://github.com/thedotmack/claude-mem/pull/437
|
||||
|
||||
## The 3-Month Battle Against Complexity
|
||||
|
||||
**TL;DR:** For three months, Claude's instinct to add code instead of delete it caused the same bugs to recur. What should have been 5 lines of code became ~1000 lines, 11 useless methods, and 7+ failed "fixes." The timestamp corruption that finally broke things was just a symptom. The real achievement: **984 lines of code deleted.**
|
||||
|
||||
---
|
||||
|
||||
## What Actually Happened
|
||||
|
||||
Every Claude Code hook receives a session ID. That's all you need.
|
||||
|
||||
But Claude built an entire redundant session management system on top:
|
||||
- An `sdk_sessions` table with status tracking, port assignment, and prompt counting
|
||||
- 11 methods in `SessionStore` to manage this artificial complexity
|
||||
- Auto-creation logic scattered across 3 locations
|
||||
- A cleanup hook that "completed" sessions at the end
|
||||
|
||||
**Why?** Because it seemed "robust." Because "what if the session doesn't exist?"
|
||||
|
||||
But the edge cases didn't exist. Hooks ALWAYS provide session IDs. The "defensive" code was solving imaginary problems while creating real ones.
|
||||
|
||||
---
|
||||
|
||||
## The Pattern of Failure
|
||||
|
||||
Every time a bug appeared, Claude's instinct was to **ADD** more code:
|
||||
|
||||
| Bug | What Claude Added | What Should Have Happened |
|
||||
|-----|------------------|--------------------------|
|
||||
| Race conditions | Auto-create fallbacks | Delete the auto-create logic |
|
||||
| Duplicate observations | Validation layers | Delete the code path allowing duplicates |
|
||||
| UNIQUE constraint violations | Try-catch with fallbacks | Use `INSERT OR IGNORE` (5 characters) |
|
||||
| Session not found | Silent auto-creation | **FAIL LOUDLY** (it's a hook bug) |
|
||||
|
||||
---
|
||||
|
||||
## The 7+ Failed Attempts
|
||||
|
||||
- **Nov 4**: "Always store session data regardless of pre-existence." Complexity planted.
|
||||
- **Nov 11**: `INSERT OR IGNORE` recognized. But complexity documented, not removed.
|
||||
- **Nov 21**: Duplicate observations bug. Fixed. Then broken again by endless mode.
|
||||
- **Dec 5**: "6 hours of work delivered zero value." User requests self-audit.
|
||||
- **Dec 20**: "Phase 2: Eliminated Race Conditions" — felt like progress. Complexity remained.
|
||||
- **Dec 24**: Finally, forced deletion.
|
||||
|
||||
The user stated "hooks provide session IDs, no extra management needed" **seven times** across months. Claude didn't listen.
|
||||
|
||||
---
|
||||
|
||||
## The Fix
|
||||
|
||||
### Deleted (984 lines):
|
||||
- 11 `SessionStore` methods: `incrementPromptCounter`, `getPromptCounter`, `setWorkerPort`, `getWorkerPort`, `markSessionCompleted`, `markSessionFailed`, `reactivateSession`, `findActiveSDKSession`, `findAnySDKSession`, `updateSDKSessionId`
|
||||
- Auto-create logic from `storeObservation` and `storeSummary`
|
||||
- The entire cleanup hook (was aborting SDK agent and causing data loss)
|
||||
- 117 lines from `worker-utils.ts`
|
||||
|
||||
### What remains (~10 lines):
|
||||
```javascript
|
||||
createSDKSession(sessionId) {
|
||||
db.run('INSERT OR IGNORE INTO sdk_sessions (...) VALUES (...)');
|
||||
return db.query('SELECT id FROM sdk_sessions WHERE ...').get(sessionId);
|
||||
}
|
||||
```
|
||||
|
||||
**That's it.**
|
||||
|
||||
---
|
||||
|
||||
## Behavior Change
|
||||
|
||||
- **Before:** Missing session? Auto-create silently. Bug hidden.
|
||||
- **After:** Missing session? Storage fails. Bug visible immediately.
|
||||
|
||||
---
|
||||
|
||||
## New Tools
|
||||
|
||||
Since we're now explicit about recovery instead of silently papering over problems:
|
||||
|
||||
- `GET /api/pending-queue` - See what's stuck
|
||||
- `POST /api/pending-queue/process` - Manually trigger recovery
|
||||
- `npm run queue:check` / `npm run queue:process` - CLI equivalents
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
- Upgraded `@anthropic-ai/claude-agent-sdk` from `^0.1.67` to `^0.1.76`
|
||||
|
||||
---
|
||||
|
||||
**PR #437:** https://github.com/thedotmack/claude-mem/pull/437
|
||||
|
||||
*The evidence: Observations #3646, #6738, #7598, #12860, #12866, #13046, #15259, #20995, #21055, #30524, #31080, #32114, #32116, #32125, #32126, #32127, #32146, #32324—the complete record of a 3-month battle.*
|
||||
|
||||
## [8.0.6] - 2025-12-24
|
||||
@@ -2617,13 +2674,13 @@ This represents a major reliability improvement for Windows users, eliminating c
|
||||
|
||||
## [7.3.5] - 2025-12-17
|
||||
|
||||
## What's Changed
|
||||
* fix(windows): solve zombie port problem with wrapper architecture by @ToxMox in https://github.com/thedotmack/claude-mem/pull/372
|
||||
* chore: bump version to 7.3.5 by @thedotmack in https://github.com/thedotmack/claude-mem/pull/375
|
||||
|
||||
## New Contributors
|
||||
* @ToxMox made their first contribution in https://github.com/thedotmack/claude-mem/pull/372
|
||||
|
||||
## What's Changed
|
||||
* fix(windows): solve zombie port problem with wrapper architecture by @ToxMox in https://github.com/thedotmack/claude-mem/pull/372
|
||||
* chore: bump version to 7.3.5 by @thedotmack in https://github.com/thedotmack/claude-mem/pull/375
|
||||
|
||||
## New Contributors
|
||||
* @ToxMox made their first contribution in https://github.com/thedotmack/claude-mem/pull/372
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.3.4...v7.3.5
|
||||
|
||||
## [7.3.4] - 2025-12-17
|
||||
@@ -5153,12 +5210,12 @@ None (patch version)
|
||||
|
||||
## [4.3.0] - 2025-10-25
|
||||
|
||||
## What's Changed
|
||||
* feat: Enhanced context hook with session observations and cross-platform improvements by @thedotmack in https://github.com/thedotmack/claude-mem/pull/25
|
||||
|
||||
## New Contributors
|
||||
* @thedotmack made their first contribution in https://github.com/thedotmack/claude-mem/pull/25
|
||||
|
||||
## What's Changed
|
||||
* feat: Enhanced context hook with session observations and cross-platform improvements by @thedotmack in https://github.com/thedotmack/claude-mem/pull/25
|
||||
|
||||
## New Contributors
|
||||
* @thedotmack made their first contribution in https://github.com/thedotmack/claude-mem/pull/25
|
||||
|
||||
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v4.2.11...v4.3.0
|
||||
|
||||
## [4.2.10] - 2025-10-25
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "12.1.2",
|
||||
"version": "12.1.6",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"keywords": [
|
||||
"claude",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "12.1.2",
|
||||
"version": "12.1.6",
|
||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem-plugin",
|
||||
"version": "12.1.2",
|
||||
"version": "12.1.6",
|
||||
"private": true,
|
||||
"description": "Runtime dependencies for claude-mem bundled hooks",
|
||||
"type": "module",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -194,7 +194,7 @@ ${f}`}let s=i.lineStart;for(let u=i.lineStart-1;u>=0;u--){let l=a[u].trim();if(l
|
||||
${c}`}var vb=new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs",".py",".pyw",".go",".rs",".rb",".java",".cs",".cpp",".cc",".cxx",".c",".h",".hpp",".hh",".swift",".kt",".kts",".php",".vue",".svelte",".ex",".exs",".lua",".scala",".sc",".sh",".bash",".zsh",".hs",".zig",".css",".scss",".toml",".yml",".yaml",".sql",".md",".mdx"]),oj=new Set(["node_modules",".git","dist","build",".next","__pycache__",".venv","venv","env",".env","target","vendor",".cache",".turbo","coverage",".nyc_output",".claude",".smart-file-read"]),ij=512*1024;async function*_b(t,e,r=20,n){if(r<=0)return;let o;try{o=await(0,Zn.readdir)(t,{withFileTypes:!0})}catch{return}for(let i of o){if(i.name.startsWith(".")&&i.name!=="."||oj.has(i.name))continue;let a=(0,Ai.join)(t,i.name);if(i.isDirectory())yield*_b(a,e,r-1,n);else if(i.isFile()){let s=i.name.slice(i.name.lastIndexOf("."));(vb.has(s)||n&&n.has(s))&&(yield a)}}}async function aj(t){try{let e=await(0,Zn.stat)(t);if(e.size>ij||e.size===0)return null;let r=await(0,Zn.readFile)(t,"utf-8");return r.slice(0,1e3).includes("\0")?null:r}catch{return null}}async function yb(t,e,r={}){let n=r.maxResults||20,o=e.toLowerCase(),i=o.split(/[\s_\-./]+/).filter(x=>x.length>0),a=r.projectRoot||t,s=Mi(a),c=new Set;for(let x of Object.values(s.grammars))for(let b of x.extensions)vb.has(b)||c.add(b);let u=[];for await(let x of _b(t,t,20,c.size>0?c:void 0)){if(r.filePattern&&!(0,Ai.relative)(t,x).toLowerCase().includes(r.filePattern.toLowerCase()))continue;let b=await aj(x);b&&u.push({absolutePath:x,relativePath:(0,Ai.relative)(t,x),content:b})}let l=mb(u,a),d=[],p=[],f=0;for(let[x,b]of l){f+=sj(b);let T=rc(x.toLowerCase(),i)>0,qe=[],Ve=(Fn,qr)=>{for(let _e of Fn){let tr=0,mt="",qn=rc(_e.name.toLowerCase(),i);qn>0&&(tr+=qn*3,mt="name match"),_e.signature.toLowerCase().includes(o)&&(tr+=2,mt=mt?`${mt} + signature`:"signature match"),_e.jsdoc&&_e.jsdoc.toLowerCase().includes(o)&&(tr+=1,mt=mt?`${mt} + jsdoc`:"jsdoc match"),tr>0&&(T=!0,qe.push({filePath:x,symbolName:qr?`${qr}.${_e.name}`:_e.name,kind:_e.kind,signature:_e.signature,jsdoc:_e.jsdoc,lineStart:_e.lineStart,lineEnd:_e.lineEnd,matchReason:mt})),_e.children&&Ve(_e.children,_e.name)}};Ve(b.symbols),T&&(d.push(b),p.push(...qe))}p.sort((x,b)=>{let N=rc(x.symbolName.toLowerCase(),i);return rc(b.symbolName.toLowerCase(),i)-N});let h=p.slice(0,n),g=new Set(h.map(x=>x.filePath)),y=d.filter(x=>g.has(x.filePath)).slice(0,n),S=y.reduce((x,b)=>x+b.foldedTokenEstimate,0);return{foldedFiles:y,matchingSymbols:h,totalFilesScanned:u.length,totalSymbolsFound:f,tokenEstimate:S}}function rc(t,e){let r=0;for(let n of e)if(t===n)r+=10;else if(t.includes(n))r+=5;else{let o=0,i=0;for(let a of n){let s=t.indexOf(a,o);s!==-1&&(i++,o=s+1)}i===n.length&&(r+=1)}return r}function sj(t){let e=t.symbols.length;for(let r of t.symbols)r.children&&(e+=r.children.length);return e}function $b(t,e){let r=[];if(r.push(`\u{1F50D} Smart Search: "${e}"`),r.push(` Scanned ${t.totalFilesScanned} files, found ${t.totalSymbolsFound} symbols`),r.push(` ${t.matchingSymbols.length} matches across ${t.foldedFiles.length} files (~${t.tokenEstimate} tokens for folded view)`),r.push(""),t.matchingSymbols.length===0)return r.push(" No matching symbols found."),r.join(`
|
||||
`);r.push("\u2500\u2500 Matching Symbols \u2500\u2500"),r.push("");for(let n of t.matchingSymbols){if(r.push(` ${n.kind} ${n.symbolName} (${n.filePath}:${n.lineStart+1})`),r.push(` ${n.signature}`),n.jsdoc){let o=n.jsdoc.split(`
|
||||
`).find(i=>i.replace(/^[\s*/]+/,"").trim().length>0);o&&r.push(` \u{1F4AC} ${o.replace(/^[\s*/]+/,"").trim()}`)}r.push("")}r.push("\u2500\u2500 Folded File Views \u2500\u2500"),r.push("");for(let n of t.foldedFiles)r.push(Un(n)),r.push("");return r.push("\u2500\u2500 Actions \u2500\u2500"),r.push(" To see full implementation: use smart_unfold with file path and symbol name"),r.join(`
|
||||
`)}var gm=require("node:fs/promises"),Sb=require("node:fs"),Fr=require("node:path"),kb=require("node:url"),vj={},cj="12.1.1";console.log=(...t)=>{E.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:t})};var wb=!1,Eb=(()=>{if(typeof __dirname<"u")return __dirname;try{return(0,Fr.dirname)((0,kb.fileURLToPath)(vj.url))}catch{return wb=!0,process.cwd()}})(),vm=(0,Fr.resolve)(Eb,"worker-service.cjs");function uj(){wb&&((0,Sb.existsSync)(vm)||E.error("SYSTEM","mcp-server: dirname resolution failed (both __dirname and import.meta.url are unavailable). Fell back to process.cwd() and the resolved WORKER_SCRIPT_PATH does not exist. This is the actual problem \u2014 the worker bundle is fine, but mcp-server cannot locate it. Worker auto-start will fail until the dirname-resolution path is fixed.",{workerScriptPath:vm,mcpServerDir:Eb}))}var bb={search:"/api/search",timeline:"/api/timeline"};async function hm(t,e){E.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:t,params:e});try{let r=new URLSearchParams;for(let[a,s]of Object.entries(e))s!=null&&r.append(a,String(s));let n=`${t}?${r}`,o=await Xs(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return E.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:t}),i}catch(r){return E.error("SYSTEM","\u2190 Worker API error",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function Ln(t,e){E.debug("HTTP","Worker API request (POST)",void 0,{endpoint:t});try{let r=await Xs(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return E.debug("HTTP","Worker API success (POST)",void 0,{endpoint:t}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(r){return E.error("HTTP","Worker API error (POST)",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function lj(){try{return(await Xs("/api/health")).ok}catch(t){return E.debug("SYSTEM","Worker health check failed",{},t),!1}}async function dj(){if(await lj())return!0;E.warn("SYSTEM","Worker not available, attempting auto-start for MCP client"),uj();try{let t=Yf(),e=await rb(t,vm);return e||E.error("SYSTEM","Worker auto-start returned false \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running. Check earlier log lines for the specific failure reason (Bun not found, missing worker bundle, port conflict, etc.)."),e}catch(t){return E.error("SYSTEM","Worker auto-start threw \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running.",void 0,t),!1}}var Ib=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
|
||||
`)}var gm=require("node:fs/promises"),Sb=require("node:fs"),Fr=require("node:path"),kb=require("node:url"),vj={},cj="12.1.6";console.log=(...t)=>{E.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:t})};var wb=!1,Eb=(()=>{if(typeof __dirname<"u")return __dirname;try{return(0,Fr.dirname)((0,kb.fileURLToPath)(vj.url))}catch{return wb=!0,process.cwd()}})(),vm=(0,Fr.resolve)(Eb,"worker-service.cjs");function uj(){wb&&((0,Sb.existsSync)(vm)||E.error("SYSTEM","mcp-server: dirname resolution failed (both __dirname and import.meta.url are unavailable). Fell back to process.cwd() and the resolved WORKER_SCRIPT_PATH does not exist. This is the actual problem \u2014 the worker bundle is fine, but mcp-server cannot locate it. Worker auto-start will fail until the dirname-resolution path is fixed.",{workerScriptPath:vm,mcpServerDir:Eb}))}var bb={search:"/api/search",timeline:"/api/timeline"};async function hm(t,e){E.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:t,params:e});try{let r=new URLSearchParams;for(let[a,s]of Object.entries(e))s!=null&&r.append(a,String(s));let n=`${t}?${r}`,o=await Xs(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return E.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:t}),i}catch(r){return E.error("SYSTEM","\u2190 Worker API error",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function Ln(t,e){E.debug("HTTP","Worker API request (POST)",void 0,{endpoint:t});try{let r=await Xs(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return E.debug("HTTP","Worker API success (POST)",void 0,{endpoint:t}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(r){return E.error("HTTP","Worker API error (POST)",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function lj(){try{return(await Xs("/api/health")).ok}catch(t){return E.debug("SYSTEM","Worker health check failed",{},t),!1}}async function dj(){if(await lj())return!0;E.warn("SYSTEM","Worker not available, attempting auto-start for MCP client"),uj();try{let t=Yf(),e=await rb(t,vm);return e||E.error("SYSTEM","Worker auto-start returned false \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running. Check earlier log lines for the specific failure reason (Bun not found, missing worker bundle, port conflict, etc.)."),e}catch(t){return E.error("SYSTEM","Worker auto-start threw \u2014 MCP tools that require the worker (search, timeline, get_observations) will fail until the worker is running.",void 0,t),!1}}var Ib=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
|
||||
1. search(query) \u2192 Get index with IDs (~50-100 tokens/result)
|
||||
2. timeline(anchor=ID) \u2192 Get context around interesting results
|
||||
3. get_observations([IDs]) \u2192 Fetch full details ONLY for filtered IDs
|
||||
|
||||
+234
-234
File diff suppressed because one or more lines are too long
@@ -35,7 +35,7 @@ function formatHeaderDateTime(): string {
|
||||
*/
|
||||
export function renderAgentHeader(project: string): string[] {
|
||||
return [
|
||||
`# $CMEM ${project} ${formatHeaderDateTime()}`,
|
||||
`# [${project}] recent context, ${formatHeaderDateTime()}`,
|
||||
''
|
||||
];
|
||||
}
|
||||
@@ -223,5 +223,5 @@ export function renderAgentFooter(totalDiscoveryTokens: number, totalReadTokens:
|
||||
* Render agent empty state
|
||||
*/
|
||||
export function renderAgentEmptyState(project: string): string {
|
||||
return `# $CMEM ${project} ${formatHeaderDateTime()}\n\nNo previous sessions found.`;
|
||||
return `# [${project}] recent context, ${formatHeaderDateTime()}\n\nNo previous sessions found.`;
|
||||
}
|
||||
|
||||
@@ -412,11 +412,22 @@ export function createPidCapturingSpawn(sessionDbId: number) {
|
||||
const useCmdWrapper = process.platform === 'win32' && spawnOptions.command.endsWith('.cmd');
|
||||
const env = sanitizeEnv(spawnOptions.env ?? process.env);
|
||||
|
||||
// Filter empty string args: Bun's spawn() silently drops empty strings from argv,
|
||||
// causing subsequent flags to be consumed as values for the preceding flag.
|
||||
// The Agent SDK may produce empty-string args (e.g., settingSources defaults to []
|
||||
// which joins to ""). Node preserves these, but Bun drops them, breaking CLI parsing.
|
||||
const args = spawnOptions.args.filter(arg => arg !== '');
|
||||
// Filter empty string args AND their preceding flag (Issue #2049).
|
||||
// The Agent SDK emits ["--setting-sources", ""] when settingSources defaults to [].
|
||||
// Simply dropping "" leaves an orphan --setting-sources that consumes the next
|
||||
// flag (e.g. --permission-mode) as its value, crashing Claude Code 2.1.109+ with
|
||||
// "Invalid setting source: --permission-mode". Drop the flag too so the SDK
|
||||
// default (no setting sources) is preserved by omission.
|
||||
const args: string[] = [];
|
||||
for (const arg of spawnOptions.args) {
|
||||
if (arg === '') {
|
||||
if (args.length > 0 && args[args.length - 1].startsWith('--')) {
|
||||
args.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
args.push(arg);
|
||||
}
|
||||
|
||||
const child = useCmdWrapper
|
||||
? spawn('cmd.exe', ['/d', '/c', spawnOptions.command, ...args], {
|
||||
|
||||
@@ -85,27 +85,6 @@ export async function processAgentResponse(
|
||||
// Convert nullable fields to empty strings for storeSummary (if summary exists)
|
||||
const summaryForStore = normalizeSummaryForStorage(summary);
|
||||
|
||||
// Fallback: When summary parse fails but observations exist, salvage a synthetic summary.
|
||||
// Fixes Issue #1312: AI sometimes returns <observation> instead of <summary> despite clear instructions.
|
||||
// Observations are stored normally; this only affects the session summary.
|
||||
let finalSummaryForStore = summaryForStore;
|
||||
if (!summaryForStore && observations.length > 0) {
|
||||
const primary = observations[0];
|
||||
finalSummaryForStore = {
|
||||
request: primary.title || `Session observations (${observations.length} items)`,
|
||||
investigated: primary.narrative || primary.facts?.join('; ') || '',
|
||||
learned: primary.facts?.join('; ') || '',
|
||||
completed: primary.type === 'feature' || primary.type === 'bugfix' ? (primary.title || '') : '',
|
||||
next_steps: '',
|
||||
notes: `[Salvaged from ${observations.length} observation(s)] AI returned <observation> instead of <summary>`
|
||||
};
|
||||
logger.warn('PARSER', `SALVAGED summary from ${observations.length} observation(s) — AI did not output <summary> tags`, {
|
||||
sessionId: session.sessionDbId,
|
||||
agentName,
|
||||
observationIds: observations.map(o => o.title).filter(Boolean).slice(0, 3)
|
||||
});
|
||||
}
|
||||
|
||||
// Get session store for atomic transaction
|
||||
const sessionStore = dbManager.getSessionStore();
|
||||
|
||||
@@ -123,7 +102,7 @@ export async function processAgentResponse(
|
||||
sessionStore.ensureMemorySessionIdRegistered(session.sessionDbId, session.memorySessionId);
|
||||
|
||||
// Log pre-storage with session ID chain for verification
|
||||
logger.info('DB', `STORING | sessionDbId=${session.sessionDbId} | memorySessionId=${session.memorySessionId} | obsCount=${observations.length} | hasSummary=${!!finalSummaryForStore}`, {
|
||||
logger.info('DB', `STORING | sessionDbId=${session.sessionDbId} | memorySessionId=${session.memorySessionId} | obsCount=${observations.length} | hasSummary=${!!summaryForStore}`, {
|
||||
sessionId: session.sessionDbId,
|
||||
memorySessionId: session.memorySessionId
|
||||
});
|
||||
@@ -134,7 +113,7 @@ export async function processAgentResponse(
|
||||
session.memorySessionId,
|
||||
session.project,
|
||||
observations,
|
||||
finalSummaryForStore,
|
||||
summaryForStore,
|
||||
session.lastPromptNumber,
|
||||
discoveryTokens,
|
||||
originalTimestamp ?? undefined,
|
||||
@@ -178,7 +157,7 @@ export async function processAgentResponse(
|
||||
// Sync and broadcast summary if present
|
||||
await syncAndBroadcastSummary(
|
||||
summary,
|
||||
finalSummaryForStore,
|
||||
summaryForStore,
|
||||
result,
|
||||
session,
|
||||
dbManager,
|
||||
|
||||
@@ -103,7 +103,7 @@ describe('AgentFormatter', () => {
|
||||
const result = renderAgentHeader('my-project');
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toMatch(/^# \$CMEM my-project \d{4}-\d{2}-\d{2} \d{1,2}:\d{2}[ap]m [A-Z]{3,4}$/);
|
||||
expect(result[0]).toMatch(/^# \[my-project\] recent context, \d{4}-\d{2}-\d{2} \d{1,2}:\d{2}[ap]m [A-Z]{3,4}$/);
|
||||
expect(result[1]).toBe('');
|
||||
});
|
||||
|
||||
@@ -116,7 +116,7 @@ describe('AgentFormatter', () => {
|
||||
it('should handle empty project name', () => {
|
||||
const result = renderAgentHeader('');
|
||||
|
||||
expect(result[0]).toMatch(/^# \$CMEM \d{4}-\d{2}-\d{2} \d{1,2}:\d{2}[ap]m [A-Z]{3,4}$/);
|
||||
expect(result[0]).toMatch(/^# \[\] recent context, \d{4}-\d{2}-\d{2} \d{1,2}:\d{2}[ap]m [A-Z]{3,4}$/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -452,7 +452,7 @@ describe('AgentFormatter', () => {
|
||||
it('should return helpful message with project name', () => {
|
||||
const result = renderAgentEmptyState('my-project');
|
||||
|
||||
expect(result).toContain('# $CMEM my-project');
|
||||
expect(result).toContain('# [my-project] recent context,');
|
||||
expect(result).toContain('No previous sessions found.');
|
||||
});
|
||||
|
||||
@@ -466,7 +466,7 @@ describe('AgentFormatter', () => {
|
||||
it('should handle empty project name', () => {
|
||||
const result = renderAgentEmptyState('');
|
||||
|
||||
expect(result).toContain('# $CMEM ');
|
||||
expect(result).toContain('# [] recent context,');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -319,9 +319,7 @@ describe('ResponseProcessor', () => {
|
||||
);
|
||||
|
||||
const [, , , summary] = mockStoreObservations.mock.calls[0];
|
||||
// #1718: When observations exist without <summary> tags, a synthetic summary is salvaged
|
||||
expect(summary).not.toBeNull();
|
||||
expect(summary.notes).toContain('Salvaged from');
|
||||
expect(summary).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user