← Plugin catalog
Developer Tools

AiAkiv Memory

AiAkiv v2.0.0

AiAkiv is long-term memory that AI agents and teammates share over MCP. It saves the important parts of your conversations as a knowledge graph and recalls them across sessions, tools, and teammates — so context one AI saves, the next one picks up, and a whole team works from one shared memory instead of scattered private chats. Save with an explicit command ("ak save"), recall by meaning with semantic search. Hosted remote MCP server with OAuth sign-in; Korean and English supported.

Language: English · Automatically detected from descriptions.

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package author
AiAkiv

Package observed Sep 30, 2026.

Files & skills

File archives

Plugin package6 files · 10.9 KBBrowse files →
Skill instructions
aiakiv-cards5.64 KB

View saved version →

---
name: aiakiv-cards
description: >-
  Create a public AiAkiv card from the user's memory. Use when the user asks to
  make a card, share a memory as a link/page, publish a summary card, or asks
  what AiAkiv cards are or how to fix/delete one. A card is a one-page public
  site (card.aiakiv.com) written by YOU from memory search results and baked by
  the card service — live on the open web the moment it is created.
---

# AiAkiv cards

A **card** is a one-page public summary of the user's AiAkiv memory: title,
3–5 bullets, a one-line conclusion, optional long body — plus two share images
with the short URL stamped in. Anyone with the address can view it, no login.
You create it through the shared app gateway; the server never calls a model —
**you write the content**.

## The one fact that governs everything

**Creating IS publishing.** The card is live on the open web the instant the
create call returns. Unlisted (nobody finds it without the address) but NOT
private. There is no draft state and no undo except deletion in the console.
So: filter before you create, and relay the response's `notice` to the user
first, verbatim.

## Procedure

1. **Search memory first** (`search_memory`). The card summarizes what is
   actually stored — do not invent content. Set `source_count` to the number
   of memories you actually used.
2. **Filter for sensitivity** — personal names, emails, internal addresses,
   decisions not yet public. If the topic is at all sensitive, show the user a
   draft and get a go-ahead before creating.
3. **Get the live format**: `run_aiakiv_app_action(app="card", action="describe")`.
   Only `describe` and `create` exist — this tool cannot delete, edit, or
   change search visibility.
4. **Write to fit the image, not just the limits.** `describe` returns
   `bullets.renders` with the measured safe line width (~30 Hangul chars) and
   max lines per bullet. Bullets share one fixed block of vertical space:
   5 bullets ≈ one safe line each; 3 bullets can run 2–3 lines each. Staying
   under `max_chars_each` (120) is NOT enough. Aim to fit on the first try —
   re-creating issues a NEW key and leaves the old card published and counted
   against quota until the user deletes it in the console.
5. **Create**: `run_aiakiv_app_action(app="card", action="create", data={...})`.
   Fields: `title` (≤80), `bullets` (3–5), `conclusion` (≤200),
   `source_count`, `tags` (≤3, ≤20 chars each — these are the search terms on
   card.aiakiv.com), `body` (optional, ≤30k chars, restricted markdown:
   paragraphs, `##`, lists, `>`, code, bold/italic, http(s) links, `---`;
   anything else renders as plain text). Title/bullets/conclusion/tags are
   single-line — newlines collapse. If a long body gets mangled in transport,
   pass `data` as one JSON **string**.

## Handling the response

- **`notice` comes first.** Relay it to the user before the link, before any
  summary, uncompressed. It states the card is already public and how to take
  it down.
- **`url`** — show verbatim as a clickable link. `og_url` / `square_url` are
  the share images (Instagram / boards that don't unfurl links).
- **`fit`** (present only when something was cut) — report it as-is:
  `bullets_truncated` / `bullets_dropped` are original bullet numbers, `hint`
  says how much to shorten. The PAGE always shows full text; only the images
  crop. Let the user decide whether to shorten and re-create — and if they do,
  remind them the old card stays up until deleted.
- **Errors** (`{error, hint, field}`): `invalid_card` → fix the named field
  and retry; `payload_too_large` → shrink the body; `quota_exceeded` (50 held /
  10 per hour / 30 per day) → tell the user; anything else → relay and stop.
  On any error the card was NOT created — never say it was.

## What you must not do

- Create a card the user didn't ask for ("summarize this" is not a card request).
- Fill a card with content that isn't in memory.
- Claim you deleted a card or toggled search visibility — you can't. Manage-
  ment (list, copy link, images, delete, search-visibility toggle) lives in
  the AiAkiv console → Data → Cards (app.aiakiv.com).
- Offer to set the user's public nickname or attach filing tags — you can't.
  Those are console-only too (see below). `data.tags` is a different thing:
  card tags are baked into the image at create time and cannot be changed.
- Quietly re-create after a `fit` warning — that leaves two public cards.
- Paste the card's URL anywhere on the user's behalf; sharing is their call.

## Useful context for the user

- KakaoTalk / Slack / Discord / Notion / blogs unfurl the bare URL into a card.
  Instagram and some board sites don't — upload the square/wide image instead;
  the URL is stamped inside it.
- Search visibility is OFF by default. Turning it on (console) lists the card
  on card.aiakiv.com search by title/tags; turning it off later un-lists it
  but the card stays public to anyone with the address.
- card.aiakiv.com search narrows three ways — text, author nickname, filing
  tag — and the address bar tracks whatever is narrowed, so any view can be
  shared as a link. **Nickname and filing tags are set in the console, by the
  user, not by you.** A nickname is optional; without one nothing identifying
  the user is published. Their email is never published in any case. Filing
  tags show on the card page and group cards in search; they never change the
  card image.
- Deleting removes the page immediately, but third-party preview caches can
  linger. Deleting the account does NOT delete cards — clean up in the console
  first.
aiakiv-graph-query7.66 KB

View saved version →

---
name: aiakiv-graph-query
description: >-
  Write graph queries (Cypher subset) against AiAkiv/MWeft memory with
  query_memory_graph (and query_partner_memory_graph across a link). Use when the
  user asks HOW memories are connected — shared entities, bridges, threads,
  timelines, multi-hop paths, "similar but structurally related", entity
  co-occurrence — or when a search hint block contains a `graph_query`
  example. Not for plain recall (use search_memory).
---

# AiAkiv graph query

`query_memory_graph` runs a **read-only Cypher subset** over the user's memory
graph. It answers *structure* questions that flat search cannot: which events
share entities, what bridges two topics, what happened next in a thread, what
is semantically far but structurally connected.

**When to reach for it** — the question is about connections, paths, shared
participants, sequences, or "one step beyond these search results". A search
response may hand you a ready-made query in `hint.graph_query.example` with
`params` — run it as-is, then adapt.

**When NOT to** — plain recall ("what did we decide about X") is
`search_memory`. If `query_memory_graph` is not in the tool list, the server has
it disabled; say so instead of inventing it.

## Grammar in one screen

```
START a = events(text: $q, k: 5)        ← anchor set is ALWAYS events
START a = events(entity: "name or id")
START a = events(ids: [$id1, $id2])
MATCH (a)-[s:SHARES {min: 2}]-(b)       ← one or more MATCH clauses
WHERE b.id <> a.id                       ← optional
RETURN b, s.count, s.via ORDER BY s.weight DESC LIMIT 20
```

- No `WITH`, no `CREATE`/`SET` (read-only). Conditions that would need `WITH`
  go into relation params like `{min: 3}`.
- `$name` params are passed via the `params` argument — always parameterize
  user text; never inline it.
- Aggregates (`count(DISTINCT …)`) live in `RETURN`. `cos(a, b)` gives vector
  cosine between two event variables.
- Variable-length: `-[n:NEXT*1..3]->` (hop count comes back as `n.hops`).

## Relations (the whole vocabulary)

| Relation | Between | Meaning / params |
|---|---|---|
| `SHARES {min, min_w}` | event–event | share ≥min entities; carries `s.count`, `s.weight` (rarity-weighted), `s.via` (the shared entities = bridges) |
| `SIMILAR {k, min}` | event–event | vector nearest neighbours; `f.cos` |
| `FAR {max}` | event–event | vector distance filter (cos < max) — pair with SHARES for "connected but semantically far" |
| `NEXT` | event→event | conversation/thread order (directional, supports `*1..n`) |
| `PARTICIPATED_IN` | event–entity | membership; walk event→entity→event for co-participation |
| `MEMBER_OF` | event–tag | category/tag membership |
| `CONNECTED` | entity–entity | stored entity co-occurrence edge |
| `SAME_AS` | entity–entity | stored alias edge (exact identity, not fuzzy match) |

## Recipes

- **Structurally close but semantically far** (the highest-value walk from
  search results — surfaces non-obvious connections):
  `START a = events(ids: $ids) MATCH (a)-[s:SHARES {min: 2}]-(b)-[f:FAR {max: 0.5}]-(a) RETURN b, s.count, s.via, f.cos ORDER BY s.weight DESC LIMIT 10`
- **What bridges these results** — same query; read `s.via` (shared entities),
  then `find_memories_by_entity` the interesting ones.
- **Thread / what happened next**:
  `START a = events(ids: $ids) MATCH (a)-[n:NEXT*1..3]->(b) RETURN b.id, b.summary, n.hops ORDER BY n.hops LIMIT 30`
- **Entity co-participation ranking**:
  `START a = events(text: $q, k: 5) MATCH (a)-[:PARTICIPATED_IN]-(e)-[:PARTICIPATED_IN]-(b) WHERE b.id <> a.id RETURN e.name, count(DISTINCT b) AS n ORDER BY n DESC LIMIT 15`

Rows are projected small (Event → `{id, summary, timestamp, order_index}`);
open full content with `get_memory_content`.

## Reading the response, handling refusals

- `partial` / `truncated` report budget caps — **never silent**. If truncated,
  narrow instead of retrying the same query: fewer anchors (`k`), tighter
  `SHARES {min}` / `FAR {max}`, smaller `LIMIT`.
- A rejection returns `{error, blocked_by: syntax|grammar|params, hint?,
  allowed_*}` — read `hint` and the `allowed_*` lists, fix the query once;
  do not loop blind retries.
- Budget errors ("statement timeout") mean the walk was too wide, not that
  the tool is broken.

## Across a link (partner org)

`query_partner_memory_graph(link_id, query, params)` — same language over your org
plus a linked partner org. `link_id` comes from `list_partner_links`. `START`
resolves in YOUR org only; the walk crosses sides through entities the link
has **aliased** (they count as one entity for `PARTICIPATED_IN`/`SHARES`),
and `SIMILAR`/`FAR` cross by vector. `NEXT`/`MEMBER_OF`/`CONNECTED` stay
within a side. Returned events carry `side` (`remote` = partner); open remote
content with `get_partner_memory_content`. Remote budgets are tighter —
prefer small `k` and `LIMIT` first. Not every server exposes this tool; if
absent, only home queries are available.

## Probe: relation modes and expansion rounds

`find_memory_connections` walks the same entity-event graph without a query language. It
takes a `relation` instead of a `MATCH` clause, and its candidates are
unverified pointers — the same standing as anything the recipes above return.

Entry is not one thing. A sentence-only call rides the vector ranking, so it
recovers when you have no ids yet; an `anchor_entity_id` call walks from the
entity directly and does not depend on the ranking at all. A useful loop is to
read a bridge entity out of one response and re-call with it as the anchor.
That chaining is yours to steer — it is not something the API does for you.

The relations:

- `similar` (default) — time-stratified neighbours of what the sentence found.
- `before` / `after` — one side of a point in time. Exactly one of
  `reference_time` (ISO-8601) or `anchor_event_id` is required; there is no
  implicit "now".
- `evolution` — stratifies the candidate pool's own timeline into first
  sighting, transition points (the largest content shift between adjacent
  sightings), and latest sighting. It answers "how did this develop". It works
  on the pool your sentence built and returns a handful of stratified picks,
  whereas `list_memory_timeline` returns one entity's full chronology.
- `contrast` — narrows by cosine and hands back candidates. The engine does
  not judge opposition: semantic tension lives as small displacements inside
  high cosine, not as opposite vectors, so geometry cannot see it. Read the
  candidates and judge contradiction yourself;
  `grounding_basis.contrast_protocol` restates this per call.
- `explore` — question-free wandering. Requires `anchor_entity_id`, forbids
  `sentence`. There is no cosine axis: candidates are ordered by bridge lift
  (specificity) and recency, with the earliest sighting stratified in, and
  `grounding` comes back as `"exploration"` because the existence gate does
  not apply.

`expansion_rounds=2` re-expands from the round-1 picks through bridges that
round 1 did not use, reaching things two structural steps away. `top_n` caps
candidates *per round*, so a 2-round call can return up to twice `top_n` in
total, and each candidate carries its `round` so you can tell how far out it
sits. Round 2 costs a second walk — reach for it on relational and
`evolution`-shaped questions rather than on direct lookups.

Across a link, `find_partner_memory_connections` takes the same inputs plus `link_id`. Entry
happens in your org only; the partner side is reached through bridges alone,
which is why it never reports `absent` — check
`grounding_basis.remote_entry_not_searched` instead.
aiakiv-links4.69 KB

View saved version →

---
name: aiakiv-links
description: >-
  Read a LINKED partner org's memory through the partner memory tools. Use when
  the user asks what a partner/linked org/team knows, wants to compare their
  memory with a partner's, mentions a link or partner org, or when a probe
  candidate carries side=remote. Covers choosing between search_partner_memory,
  find_partner_memory_connections and query_partner_memory_graph, reading results, and what each error reason
  means.
---

# AiAkiv links (partner-org memory)

A **link** is a read capability two orgs agreed to — not a project you switch
into. You pass `link_id` per call; your own scope is untouched. Everything is
read-only: you can never write into a partner org.

**Always start with `list_partner_links`.** It is local (works even when the
partner is down) and tells you which links are `usable` before you spend a
remote call. If the tools are absent from the tool list, the server has links
disabled — say so.

**Call link tools one at a time.** Your org may have only one link call in
flight at once. Two link tools issued in the same batch — the habit that serves
you well everywhere else — means the second returns `error: busy` without
running, even on a completely idle server. Await each call before starting the
next; `list_partner_links` is local and does not count.

## Which tool for which question

| Question shape | Tool |
|---|---|
| "What does the partner know about X?" — search THEIR memory directly | `search_partner_memory(sentence, link_id)` — entry happens on their side; results are their events only |
| "From what WE know, what connects to their side?" — walk from your context across shared ground | `find_partner_memory_connections` — entry in YOUR org, expansion crosses through **aliased entities** (entities the link declared "same thing on both sides") |
| Explicit relations across both orgs (shared entities, vector distance, ranked bridges) | `query_partner_memory_graph(link_id, query, params)` — same language as `query_memory_graph`; see the `aiakiv-graph-query` skill |
| Read one partner event's body | `get_partner_memory_content(link_id, event_id)` — paged; `event_id` comes from the other tools' results |

## Reading results

- Partner events carry `side: "remote"` (or a `link_id`). **Keep them
  labelled as the partner's** when you reason or summarize — do not present
  partner knowledge as the user's own, and do not re-save partner content
  into the user's memory unless the user explicitly asks.
- `find_partner_memory_connections` never claims something is *absent* on the partner side —
  entry is home-only, so the partner is only reached through bridges. Absence
  of remote candidates means "no bridge found", not "they don't know".
- Aliased bridge entities carry `df_home` / `df_remote` (how common the
  entity is on each side). Asymmetry is a signal: "1 here, 109 there" means a
  passing mention for the user is a central topic for the partner — often the
  most interesting finding in the response.
- `found: false` from `get_partner_memory_content` means the link does not
  surface that event — deliberately the same answer whether it is absent or
  out of scope. Don't retry; don't speculate which.

## When a call returns `status: "error"`

The `reason` names what to do — an error here **never** means the user's own
memory failed:

- `no_links`, `link_not_found`, `link_not_active`, `link_expired`,
  `org_not_party`, `principal_not_active` — this link cannot be read (or this
  direction is closed). Show `list_partner_links` output; fixing it is an
  owner/console action, not a retry.
- `we_are_provider` — the user's org is the *providing* side; there is
  nothing to read in this direction. Normal state, not a failure.
- `contract_version_too_old` — both owners must re-consent;
  `list_partner_links` → `reconsent` shows who is missing.
- `budget_exceeded` — the remote row/time budget ran out mid-walk. Narrow the
  ask (smaller `top_n`/`k`/`LIMIT`, tighter relation params) instead of
  repeating it.
- `busy` — a concurrency limit on **your** server (never the partner's). The
  `detail` says which of two: the per-org limit, which is almost always
  self-inflicted (two link calls issued together) — re-issue the refused one
  once the other finishes, it cost nothing and did not run; or the server-wide
  limit, shared with other tenants, where a short wait is the right response.
  Either way it is not a partner problem.
- `remote_failure`, `internal` — infrastructure failed; try later.

Results can also be `partial` (budget hit mid-walk): what came back is valid,
just incomplete — say so instead of treating it as the full picture.
aiakiv-save-and-recall5.45 KB

View saved version →

---
name: aiakiv-save-and-recall
description: How to write a memory that will be found again, and how to read what AiAkiv memory returns. Covers summary and entity spelling rules, what to do when a save is rejected (summary or content too long, a truncation marker, arguments arriving merged or empty), and how to interpret the related-memory signal that search responses carry. Use when saving to AiAkiv memory, when a save call fails or its arguments collapse, or when deciding whether a search result needs a follow-up expansion call.
---

# Saving to and recalling from AiAkiv memory

The tool descriptions state the contract. This skill covers the parts that
decide whether a saved memory is findable a month later, and what to do when
a call is rejected.

## 1. The summary is the retrieval surface

Searches rank against the summary, not the content. Write it with the words a
future question would use.

- **Keep exact identifiers verbatim.** Code symbols, document numbers, config
  keys. Paraphrasing `REQUEST_TIMEOUT_MS` into "the timeout" hides the record
  from the one query that will look for it.
- **Name the actor in the first sentence** when you record someone's judgment,
  design, or claim. Conclusion-style summaries ("decided that…") drop who said
  it, and "what did X propose?" then misses the record.
- Write a complete sentence, not a keyword pile.

## 2. Entity spelling decides what connects

Recall weaves records together on shared entity nodes, so spelling drift
silently loses connections.

- **One canonical spelling per entity** — the full name *or* the acronym, not
  both. Pick one and use it in every save.
- **Keep proper nouns and acronyms as they are written**: `PostgreSQL`, `RLS`.
  Do not lowercase or expand them.
- **Strip transient tokens** — ids, hashes, dates. They never repeat, so they
  add a node that connects to nothing.
- A suggested entity returned by the server is advisory. A literal name match
  does not establish that it denotes the same thing in this record.

## 3. When a save is rejected

### The summary is over 500 characters

**Split the event; do not compress the summary.** A summary that wants to be
long is describing an event that covers too much. Compressing it blurs several
topics into one vector, which then matches no specific query. Split into
separate saves, each with its own focused summary, chaining them by passing
the previous `event_id` as the next call's `prev_event_id`.

Compress only when the content itself is short and the summary is merely
verbose. Do not fragment one small record.

### The content is over 50000 characters

Split the full text across calls the same way, chaining with `prev_event_id`,
giving each piece its own summary. Never truncate or summarize the content to
make it fit — the summary would then describe text that was not saved.

### `truncation_marker_detected`

The content carries a marker that some earlier tool left when it cut the text
(`…2183 tokens truncated…`, `<response clipped>`, `[truncated]`). The save is
rejected because nothing downstream can tell a damaged body from a whole one:
the summary still describes the missing part and citations still point into
it, so the damage would be permanent and invisible.

Re-read the source in full and save again, splitting if it is long. Pass
`allow_truncation_marker=true` only when the marker is genuinely part of what
you mean to record, such as a bug report *about* truncation.

### The arguments arrive merged, empty, or the call errors on serialization

Two causes, both about argument boundaries:

- A long or multi-line `summary` or `content`. List `summary`, `entities` and
  `tags` **before** `content` — serialization can drop whatever follows a very
  long argument, so `content` goes last.
- Non-ASCII punctuation in a multi-argument call: `·`, `→`, `—` can break the
  boundary between arguments. Use ASCII punctuation in multi-argument saves.

**Recovery: retry with `payload`.** One argument has no boundary to collapse.
Send the whole call as a JSON string or an object:

```json
{"summary": "...", "entities": [{"name": "X", "type": "Y"}], "tags": ["a/b"], "content": "..."}
```

Other arguments are then ignored. The multi-argument form stays the default;
this is the recovery path.

## 4. Reading the related-memory signal

A search response may carry `hint.related_memory_expansion`. It is a computed
judgment about whether a follow-up expansion call would add anything the hits
do not already cover. Its fields:

| Field | Meaning |
|---|---|
| `tool` | Which tool performs the expansion |
| `decision` | `must_be_done`, `should_be_done`, `can_skip`, `must_not_be_done` |
| `candidate_count` | How many candidates sit outside the returned hits |
| `candidate_dimension` | The relation the candidates share with the hits |
| `candidate_ids` | The candidate memory ids |
| `reason_codes` | Which gates the candidates passed |
| `reason_facts` | The measured values behind those codes |

`should_be_done` means candidates outside the hits passed the relevance and
novelty gates, so expanding is likely to add something before you conclude.
`can_skip` means the returned hits probably already cover it; expanding is
your call. The block is omitted entirely when there is nothing to say.

Results that feel sufficient are not evidence that they are. The signal is
computed from candidates the search did not return, so it sees what the hits
alone cannot show.
Technical details
First seen
Sep 30, 2026 · 22:02 UTC
Last seen
Oct 1, 2026 · 12:00 UTC
Collection status
Collected

plugin_asdk_app_6a5c3e5e745881918b228734236a933d

Download listing JSON