Team coordination
Memoturn’s coordination surface is what single-vendor memory (Claude’s, Cursor’s) structurally can’t do: every agent on the same project, regardless of vendor, sees the same locks, reviews, presence, and focus state in real time.
Each primitive is a small MCP tool. The combinations are what matters.
Claims, atomic leases
Section titled “Claims, atomic leases”Two agents both decide to refactor apps/api/src/auth.ts. Without coordination they collide. With claim, the first agent acquires a TTL-bounded lease on file:apps/api/src/auth.ts; the second agent’s claim attempt fails-loud with the current held_by and expires_at, and backs off cleanly.
const a = await mt.claim({ key: "file:apps/api/src/auth.ts", holder: "cursor@alice", ttl_ms: 10 * 60 * 1000,});if (!a.ok) { console.log(`held by ${a.held_by}, retry after ${new Date(a.expires_at)}`); return;}// ... do the work ...await mt.release("file:apps/api/src/auth.ts", "cursor@alice");Optional defense in depth: gate every write with record_turn’s requires_claim. With claim_policy: "hard" the engine refuses the write if the lease lapsed or a different holder owns it. Default soft lets the write through but stamps claim_violation metadata and emits a broadcast: enough visibility for incremental adoption without breaking existing flows.
Reviews
Section titled “Reviews”request_review opens a typed gate; downstream agents subscribe to review_completed (or poll list_pending_reviews) and only proceed on approved. Idempotent: a racing reviewer that loses gets ok=false, reason: "already_decided" with the current decision, so two reviewers can’t both think they shipped.
const r = await mt.requestReview({ subject: "refactor: auth middleware", claim_key: "file:apps/api/src/auth.ts", reviewers: ["claude-code-tests@bot", "alice@example.com"], deadline_ms: 30 * 60 * 1000,});
// Reviewer agent (or human) decides:await mt.markReviewed(r.review.id, "approved", "lgtm");decided_by and decided_at are first-class on every review row, so multi-agent workflows have an audit trail of who approved what.
Focus map
Section titled “Focus map”list_focus returns the top files by team activity in the window (default 1 h). Each row carries the path, the turn count, and the distinct (tool, actor) pairs touching the file, so an agent can detect a cross-tool collision before reaching for claim.
const focus = await mt.listFocus({ since_ms: 60 * 60 * 1000 });for (const file of focus.files) { if (file.agents.length > 1) { console.warn( `collision risk on ${file.path}:`, file.agents.map((a) => `${a.tool}@${a.actor}`).join(", "), ); }}The dashboard’s /projects/{slug}/focus route renders this with badges on multi-agent files. That’s the screenshot meant to land in your team’s standup.
Presence with intent
Section titled “Presence with intent”heartbeat on a 30-second cadence carries optional intent fields (tool, current_file, current_function, current_action). They fan out on presence_updated so the dashboard paints a live “what is each agent doing right now” view without polling.
setInterval(() => { void mt.heartbeat({ tool: "cursor", current_file: "apps/api/src/auth.ts", current_function: "signupWithPassword", current_action: "editing", });}, 30_000);Rows are keyed on (tool, user_email) so the same human running both Cursor and Claude Code shows two distinct rows. Idle dim past 90 s; row drops past 5 min.
To drive presence from editor hooks instead of a timer, use npx @memoturn/cli hooks presence (stdin is one hook JSON payload, output is a Memoturn heartbeat), or run memoturn connect hooks once to merge starter snippets into local Cursor / Claude Code / Codex hook configs. Vendor docs: Claude Code hooks, Cursor hooks, Codex hooks.
Decision provenance
Section titled “Decision provenance”pin_memory accepts derived_from_turn_ids: up to 50 source turns the decision was extracted from. The dashboard’s decisions view renders the trail so a future reader can see the conversation that produced the pin.
await mt.pinMemory({ content: "Decision: use Hyperdrive for connection pooling. Rejected pgBouncer due to ops overhead.", tags: ["decision", "infra"], derived_from_turn_ids: [turn1.id, turn2.id, turn3.id, turn4.id],});Ids that don’t resolve to turns in this project are dropped (reported on derived_from_dropped) rather than rejecting the pin. A caller-side caching error doesn’t kill an audit trail.
See also
Section titled “See also”- Tool reference: every parameter, every default
- Memory rules: declarative automation on coordination broadcasts (
claim_expired,review_completed, …)