Skip to content

This release adds 3 notable features for engineering teams evaluating rollout.

✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →

✓ No known CVEs patched in this version

Topics

ai ai-agents ai-memory anthropic artificial-intelligence claude
+12 more
claude-agent-sdk claude-agents claude-code-plugin claude-skills codex embeddings long-term-memory memory-engine openclaw openclaw-skills postgresql llm

Affected surfaces

breaking_upgrade rce_ssrf

Summary

AI summary

Broad release touches Files touched, Summary by CodeRabbit, Test plan, and pid.

Changes in this release

Security Medium

Runtime validation now rejects non-finite elements in daemon JSON responses.

Runtime validation now rejects non-finite elements in daemon JSON responses.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Feature Medium

Added helper tryEmbedStandalone for agents without own daemon (pi, openclaw).

Added helper tryEmbedStandalone for agents without own daemon (pi, openclaw).

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Feature Medium

OpenClaw now embeds message_embedding before INSERT using tryEmbedStandalone.

OpenClaw now embeds message_embedding before INSERT using tryEmbedStandalone.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Feature Medium

Extended maybeCleanupOwnPlaceholder to unlink empty pidfiles after spawn timeout.

Extended maybeCleanupOwnPlaceholder to unlink empty pidfiles after spawn timeout.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Feature Medium

Implemented 11-case embedding edge matrix with unit tests.

Implemented 11-case embedding edge matrix with unit tests.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: low

Dependency Medium

Updated test configuration for code coverage thresholds.

Updated test configuration for code coverage thresholds.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: low

Performance Medium

Added coverage thresholds (90/80/90/90) for client.ts tier.

Added coverage thresholds (90/80/90/90) for client.ts tier.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: low

Bugfix Medium

Fixed pi spawn-on-miss race condition causing second daemon crash.

Fixed pi spawn-on-miss race condition causing second daemon crash.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Bugfix Medium

Resolved empty-pidfile race leading to stale daemon spawning and bind errors.

Resolved empty-pidfile race leading to stale daemon spawning and bind errors.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Bugfix Medium

Fixed pidfile leak when spawn succeeds but daemon never opens socket.

Fixed pidfile leak when spawn succeeds but daemon never opens socket.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Bugfix Medium

Fixed SIGKILL edge case leaving empty pidfiles causing silent NULL embeddings.

Fixed SIGKILL edge case leaving empty pidfiles causing silent NULL embeddings.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Refactor Medium

Injected realSpawn into tryEmbedStandalone for openclaw bundle environments.

Injected realSpawn into tryEmbedStandalone for openclaw bundle environments.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: high

Other Medium

Added 8 new unit tests for embedding client and daemon behavior.

Added 8 new unit tests for embedding client and daemon behavior.

Source: granite4.1:8b-q6_K@2026-05-20

Confidence: low

Full changelog

Closes #178. Follow-up to PR #168 — surfaced during review by @kaghni who flagged that pi and openclaw had no/minimal changes despite the "embeddings fully wired across agents" framing.

What this lands

Three pieces of work, separated into focused commits per the repo's "never >3 src files in one commit across layers" rule:

1. src/embeddings/standalone-embed-client.ts + tests — c9478ec

New helper tryEmbedStandalone(text, kind) for agents that don't bundle a daemon of their own (pi extension source, openclaw plugin). Mirrors the spawn-on-miss state machine in src/embeddings/client.ts but stripped:

  • No hello/handshake. Read-only consumers never recycle a stuck daemon; recycling is the hot-path client's job, two recycle paths would race.
  • No singleton, no notification side-effects.
  • No SIGTERM on a live-PID pidfile with a missing socket — same PID-reuse risk PR #168 fixed in client.ts.

Coverage threshold added at the client.ts tier (90/80/90/90).

2. Pi spawn-on-miss bug fix — 17f9435

Pi's existing embed() called spawn(...) bare — no O_EXCL pidfile lock, no respect for an alive owner. Two concurrent pi turns (or pi racing another agent at SessionStart) both spawned a daemon; the second crashed on bind. The header comment block described the canonical behavior but the code didn't implement it.

Replaces both tryEmbedOverSocket (connect-only) and the inline spawn loop with a single spawn-on-miss state machine mirroring the shared helper. embed() collapses to env-check → empty-check → tryEmbedOverSocket.

3. OpenClaw embedding producer — 8d7df3d

OpenClaw previously omitted message_embedding from every sessions INSERT — semantic recall on openclaw sessions was broken because every row landed NULL.

Now openclaw imports tryEmbedStandalone and embeds the captured message before INSERT. The helper imports spawn from node:child_process at the top level, which the openclaw esbuild config replaces with a no-op stub. Without the real spawn, the auto-spawn-on-miss fallback silently does nothing. Fix: openclaw already has realSpawn from createRequire(import.meta.url); we inject it into the helper at module load via _setSpawnImpl (renamed from _setSpawnForTesting to reflect its two legitimate use cases — tests AND bundle environments stubbing node:child_process).

Bundle-scan regression guard in tests/openclaw/openclaw-embed-bundle.test.ts locks in: exactly one tryEmbedStandalone call site on the auto-capture path, message_embedding in the INSERT column list, _setSpawnImpl(realSpawn) called at module load, and no INSERT that hardcodes literal NULL.

4. Codex pre-merge review fixes — bb9df97

Pre-merge codex review flagged 2 P1 + 1 P2:

  • P1 #1 — Empty-pidfile race. openSync(path, "wx") creates the lock file BEFORE writeSync(pid) lands. A second caller observing the gap saw Number("") === 0 → null → "stale", unlinked, and re-opened. Both callers spawned a daemon; the second crashed on bind. Fix: readPidFile now returns a tristate (number | "empty" | null); trySpawnDaemon treats "empty" as "writer in progress, wait", never unlinks. Pi's inline version also switched from writeFileSync(path, ...) to writeSync(fd, ...) so a racing unlink can't clobber.
  • P1 #2 — Pidfile leak when spawn succeeds but daemon never opens socket. Placeholder PID stayed in the file with our (still-alive) process PID; future callers saw a "live owner" and waited forever. Fix: new maybeCleanupOwnPlaceholder unlinks ONLY if pidfile still contains process.pid.
  • P2 — Runtime validation at the socket boundary. Daemon JSON is untrusted at runtime even though TypeScript types claim number[]. Both implementations now reject any non-finite element before returning the vector.

4 new unit tests (empty pidfile = no respawn, retry-after-cleanup recovery, non-finite array → null, NaN/Infinity → null) + 3 source-level regression guards in pi.

5. Codex follow-up: stuck empty pidfile — f04f00a

Codex's second pass confirmed all 3 fixes correct but flagged a residual edge: a process SIGKILL'd exactly between openSync(wx) and writeSync(pid) leaves an empty pidfile that every subsequent caller treats as "writer in progress" — silent NULL embeddings for that uid forever. Extended maybeCleanupOwnPlaceholder to also unlink an empty pidfile after the spawnWaitMs (5s) timeout — orders of magnitude longer than the legitimate openSync→writeSync gap.

11-case edge matrix (all unit-tested)

| # | Scenario | Expected |
|---|---|---|
| 1 | Binary missing | NULL, no spawn |
| 2 | Binary present, no socket / pid | Spawn → wait → embed |
| 3 | Socket alive | Connect → embed |
| 4 | Stale socket, no daemon | Spawn (daemon unlinks on bind) |
| 5 | Dead PID in pidfile | Cleanup → spawn |
| 6 | Live PID, no socket | Wait, no SIGTERM |
| 7 | Two callers race | O_EXCL: one spawns, other waits |
| 8 | spawn() throws | NULL, pidfile rolled back |
| 9 | Daemon never opens socket | 5s timeout → NULL + cleanup |
| 10 | Embed request times out | NULL |
| 11 | Daemon returns unknown-op | NULL |

Test plan

  • [x] npm test — 2741 / 2741 pass (was 2733 before this branch; added 8)
  • [x] npm run build clean
  • [x] npx tsc --noEmit clean
  • [x] codex review — final pass returned "No new [P1] or [P2] findings"
  • [x] Per-file coverage on src/embeddings/standalone-embed-client.ts: 96.52% statements / 84.61% branches / 94.73% functions / 100% lines (≥ 90/80/90/90 threshold added in this PR)
  • [ ] E2E on test_plugin/default/sessions_test (NEVER prod) — manual pre-merge step using the /tmp/e2e-embed-check.mjs pattern from PR #168 (socket p50=10ms, write p50=402ms, semantic recall TOP-1 @ 0.7409). Will run before merge.

Files touched

  • src/embeddings/standalone-embed-client.ts (new, 305 LOC)
  • tests/claude-code/standalone-embed-client.test.ts (new, 22 tests)
  • pi/extension-source/hivemind.ts (replaces tryEmbedOverSocket + embed() spawn logic)
  • tests/pi/pi-extension-source.test.ts (5 new regression guards)
  • openclaw/src/index.ts (embed call + _setSpawnImpl injection)
  • tests/openclaw/openclaw-embed-bundle.test.ts (new bundle-scan)
  • tests/claude-code/skillify-session-start-injection.test.ts (regex window bump)
  • vitest.config.ts (coverage threshold)

Summary by CodeRabbit

  • New Features

    • Added message embedding to the auto-capture pipeline with automatic daemon spawning and graceful NULL fallback on failures.
    • Implemented improved daemon lifecycle management with race-condition safety and per-user isolation.
  • Tests

    • Added comprehensive test coverage for embedding client functionality and daemon behavior.
    • Added integration tests to prevent regressions in embedding wiring.
  • Chores

    • Updated test configuration for code coverage thresholds.

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track Hivemind turns agent traces into skills and shares with your team

Get notified when new releases ship.

Sign up free

About Hivemind turns agent traces into skills and shares with your team

All releases →

Related context

Earlier breaking changes

  • v0.7.52 Removes `hivemind tasks` CLI and related code surfaces.
  • v0.7.51 Removes `hivemind tasks` CLI and related code surfaces.
  • v0.7.19 Module name skilify replaced with skillify; affects all imports
  • v0.7.19 CLI command skilify removed; renamed to skillify without deprecation alias
  • v0.7.18 CLI subcommand renamed from `skilify` to `skillify`; no deprecation alias.

Beta — feedback welcome: [email protected]