Skip to content

feat(sdk): v0.9.0 — @memmesh/sdk rename, raw-text observe, knowledge graph - #21

Merged
rrader26 merged 1 commit into
mainfrom
feat/sdk-0.9.0-graph-and-observe
Aug 19, 2026
Merged

feat(sdk): v0.9.0 — @memmesh/sdk rename, raw-text observe, knowledge graph#21
rrader26 merged 1 commit into
mainfrom
feat/sdk-0.9.0-graph-and-observe

Conversation

@rrader26

Copy link
Copy Markdown
Contributor

Brings the SDK up to what the API actually supports, and unblocks the
chat-with-memory example, which needs both the raw-text observe pipeline and
the graph counters.

@thinkfleet/memory-sdk tops out at 0.7.1 on npm, so this release also
carries the unpublished 0.8.0 work (procedural memory, precedence policy).

What changed

memory.observe() takes raw text

const { saved, candidateCount } = await tf.memory.observe({
  text: "I just moved to Denver and I'm still vegetarian.",
  role: 'user',
  userId: 'user-123',
  sessionId: 'thread-456',
})

Hand it the turn verbatim — the engine runs extraction, dedupe, graph
wiring, and embedding, and keeps only what is worth remembering.
candidateCount is what extraction proposed, saved is what survived. Filler
comes back as saved: [].

Identity fields are forwarded

userId / agentId / sessionId now reach the server. The route has always
accepted them; the SDK was silently dropping them, so provenance never
arrived on anything written through observe().

They are provenance, not a tenancy boundary — admin.search filters
chatIdentityId IS NULL OR = $1, permissive by design so project-wide memories
stay visible. Isolating one end user's memories from another's still needs a
project per tenant. Documented as such in the README and the type.

New tf.memory.admin.graph

Method Endpoint
stats() GET /admin/memory/graph/stats
listEntities(params?) GET /admin/memory/entities
getEntity(id, params?) GET /admin/memory/entities/:id
listEdges(params?) GET /admin/memory/graph/edges
traverse(id, params?) POST /admin/memory/graph/traverse

There was no graph surface at all before, so the structural half of memory was
unreachable from the SDK. Read-only on purpose: entities and edges are written
by extraction during observe(), and exposing the manual create/retire routes
would invite hand-maintained graphs — the work the engine exists to do.

Use stats(), not listEntities().length, for any "how big is it" question:
the list routes page, so their length is the page size rather than the total.
(That exact bug was live in the example app before this change.)

Also

  • MemMesh exported as the brand-consistent client alias; ThinkFleetMemory
    retained for back-compat.
  • README documents observe() for the first time — it was the primary write
    path and absent from the resource tables entirely.
  • Tag-triggered npm publish workflow (v*npm publish).

Breaking

  • Package renamed to @memmesh/sdk. Consumers of @thinkfleet/memory-sdk
    do not get this via a version bump; they must change the dependency name.
  • observe() return type changed from MemoryItem to
    { saved, candidateCount }, on the legacy content path too. test-app.ts
    is updated accordingly.

Verification

  • npm run typecheck, npm run build, npm run check:proto (47 RPCs — 42
    covered, 5 internal) all pass.
  • Packed the tarball and drove a full chat turn through it end to end:
    admin.search → Claude → observe (both halves) → graph.stats. Confirmed
    on the wire that identity now arrives:
    {"text":"I just moved to Denver.","role":"user","userId":"u-verify-123","sessionId":"s-verify-456"}

Before merging / after

  • The @memmesh npm org must exist and NPM_TOKEN must be set in repo
    secrets — I could not verify either (npm is not authenticated locally).
  • Publish is a tag push: git tag v0.9.0 && git push origin v0.9.0.
  • memmesh-examples has four examples still on @thinkfleet/memory-sdk
    (three ^0.4.0, one ^0.7.1). They keep working, but the repo now spans two
    package names — worth migrating once this is on npm.

…graph

Publishes as `@memmesh/sdk`. `@thinkfleet/memory-sdk` tops out at 0.7.1, so
this release also carries the unpublished 0.8.0 work.

`memory.observe()` now takes raw text and returns what the engine kept:

  const { saved, candidateCount } = await tf.memory.observe({
    text, role: 'user', userId, sessionId,
  })

Hand it the turn verbatim and the engine runs extraction, dedupe, graph
wiring, and embedding. `candidateCount` is what extraction proposed;
`saved` is what survived. The legacy `content` path still stores a
pre-decided fact verbatim, so existing callers keep working — but its
return type changes from `MemoryItem` to `{ saved, candidateCount }`.

`userId` / `agentId` / `sessionId` are now forwarded. The server route has
always accepted them; the SDK was dropping them, so provenance never
arrived. They are provenance only, NOT a tenancy boundary: `admin.search`
filters `chatIdentityId IS NULL OR = $1`, permissively by design.

New `tf.memory.admin.graph` — stats, listEntities, getEntity, listEdges,
traverse. There was no graph surface at all before, so the structural half
of memory was unreachable from the SDK. Read-only: entities and edges are
written by extraction during observe, and a hand-maintained graph is the
work the engine exists to do.

Prefer `graph.stats()` over `listEntities().length` for any size question —
the list routes page, so their length is the page size, not the total.

Also: `MemMesh` exported as the brand-consistent client alias
(`ThinkFleetMemory` retained), README documents `observe()` for the first
time, and a tag-triggered npm publish workflow.

BREAKING: package renamed; `observe()` return type changed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants