clawmem 0.10.6 → 0.10.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmem",
3
- "version": "0.10.6",
3
+ "version": "0.10.7",
4
4
  "description": "On-device memory layer for AI agents. Claude Code, OpenClaw, and Hermes. Hooks + MCP server + hybrid RAG search.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -422,15 +422,20 @@ class ClawMemProvider(MemoryProvider):
422
422
  if not self._bin or not query or len(query) < 5:
423
423
  return
424
424
 
425
- # Increment generation so older threads can't overwrite newer results
425
+ # Increment generation so older threads can't overwrite newer results,
426
+ # and snapshot the session id + transcript path under the same lock so a
427
+ # concurrent on_session_switch() can't make the worker read a torn
428
+ # (new id / old path) pair — the worker uses the snapshot, never live state.
426
429
  with self._prefetch_lock:
427
430
  self._prefetch_generation += 1
428
431
  my_gen = self._prefetch_generation
432
+ run_session_id = self._session_id
433
+ run_transcript_path = self._transcript_path
429
434
 
430
435
  def _run():
431
436
  hook_input = {
432
- "session_id": self._session_id,
433
- "transcript_path": self._transcript_path,
437
+ "session_id": run_session_id,
438
+ "transcript_path": run_transcript_path,
434
439
  "prompt": query,
435
440
  "hook_event_name": "UserPromptSubmit",
436
441
  }
@@ -531,6 +536,50 @@ class ClawMemProvider(MemoryProvider):
531
536
 
532
537
  logger.info("clawmem: session %s extraction complete", self._session_id[:8])
533
538
 
539
+ def on_session_switch(
540
+ self,
541
+ new_session_id: str,
542
+ *,
543
+ parent_session_id: str = "",
544
+ reset: bool = False,
545
+ **kwargs,
546
+ ) -> None:
547
+ """Refresh session-derived state when Hermes rotates session_id mid-process.
548
+
549
+ Fires on /new (reset=True), /resume, /branch, and compression (reset=False).
550
+ ClawMem reads _session_id and the session-keyed _transcript_path live in
551
+ queue_prefetch / sync_turn / on_session_end / on_pre_compress, so a switch
552
+ must repoint them and drop the prior session's prefetch + bootstrap context
553
+ (unconditional — NOT gated on reset, or stale recall leaks into the new
554
+ session). Cache coherence, not a vault write, so it runs for all contexts.
555
+ """
556
+ new_id = str(new_session_id or "").strip()
557
+ if not new_id or not self._bin:
558
+ return
559
+ # Idempotent re-fire (duplicate dispatch) with no reset is a no-op.
560
+ if new_id == self._session_id and not reset:
561
+ return
562
+
563
+ new_path = self._transcript_path
564
+ if self._hermes_home:
565
+ transcript_dir = Path(self._hermes_home) / "clawmem-transcripts"
566
+ transcript_dir.mkdir(parents=True, exist_ok=True)
567
+ new_path = str(transcript_dir / f"{new_id}.jsonl")
568
+
569
+ with self._prefetch_lock:
570
+ self._session_id = new_id
571
+ self._transcript_path = new_path
572
+ # Bump generation MONOTONICALLY (never reset to 0): an in-flight
573
+ # prefetch worker then fails its `my_gen == _prefetch_generation`
574
+ # check and discards its result instead of leaking it into the new
575
+ # session. Advancing consumed_gen drops any already-cached result.
576
+ self._prefetch_generation += 1
577
+ self._prefetch_result = ""
578
+ self._prefetch_result_gen = 0
579
+ self._prefetch_consumed_gen = self._prefetch_generation
580
+ # Startup context is session-derived; must not cross session ids.
581
+ self._bootstrap_context = ""
582
+
534
583
  def on_pre_compress(self, messages: List[Dict[str, Any]]) -> str:
535
584
  """Run precompact-extract (side effect only — Hermes ignores return).
536
585