@yugabytedb/meko-mcp 2.0.0 → 2.1.0

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.
Files changed (48) hide show
  1. package/README.md +31 -6
  2. package/assets/hooks-handlers/codex-session-end.sh +78 -0
  3. package/assets/hooks-handlers/lib/capture.js +163 -14
  4. package/assets/hooks-handlers/lib/checkpoint-timer.js +44 -2
  5. package/assets/hooks-handlers/session-end.sh +10 -0
  6. package/assets/hooks-handlers/session-start.sh +8 -1
  7. package/assets/hooks-handlers/test_checkpoint_timer.sh +162 -0
  8. package/assets/scripts/package-desktop-skill.sh +32 -1
  9. package/assets/skills/meko-mcp-tools/SKILL.md +47 -41
  10. package/assets/skills/meko-mcp-tools/references/_sections.md +2 -4
  11. package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +29 -25
  12. package/assets/skills/meko-mcp-tools/references/tools-conversation-dedup.md +5 -7
  13. package/assets/skills/meko-mcp-tools/references/tools-conversation-reasoning.md +50 -0
  14. package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +63 -46
  15. package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +6 -6
  16. package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +4 -4
  17. package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +17 -22
  18. package/assets/skills/meko-mcp-tools/references/tools-overview.md +40 -39
  19. package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +1 -2
  20. package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +4 -18
  21. package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +9 -8
  22. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +49 -42
  23. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +6 -6
  24. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +2 -2
  25. package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +28 -24
  26. package/assets/skills/meko-mcp-tools-desktop/references/tools-conversation-reasoning.md +50 -0
  27. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +61 -36
  28. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +4 -4
  29. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +16 -19
  30. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +40 -39
  31. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +1 -2
  32. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +3 -17
  33. package/assets/skills/meko-select-datapack-desktop/SKILL.md +31 -21
  34. package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +8 -8
  35. package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +41 -0
  36. package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +1 -1
  37. package/bin/create-meko-setup.mjs +45 -6
  38. package/lib/clients/claude-code.mjs +8 -4
  39. package/lib/clients/claude-desktop.mjs +14 -0
  40. package/lib/clients/codex.mjs +23 -8
  41. package/lib/clients/common.mjs +2 -0
  42. package/lib/clients/cursor.mjs +7 -2
  43. package/lib/detect.mjs +7 -2
  44. package/lib/prompt.mjs +1 -1
  45. package/lib/url-selection.mjs +55 -0
  46. package/package.json +1 -1
  47. package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +0 -71
  48. package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +0 -71
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # test_checkpoint_timer.sh — Regression tests for issue #159:
4
+ # - Defect 1: the checkpoint timer must spawn even when the transcript file
5
+ # does not exist yet at SessionStart time (spawn race).
6
+ # - Defect 3: the timer must self-exit (max-age / idle backstop) and
7
+ # session-end.sh must kill it by transcript path when the pid file is gone,
8
+ # so orphaned daemons cannot tick forever.
9
+ #
10
+ # These tests do NOT require a live Meko MCP server: the timer's self-guard and
11
+ # self-exit paths run before any capture.js checkpoint would reach MCP, and we
12
+ # assert on process liveness / pid-file presence only.
13
+ #
14
+ # Usage: ./test_checkpoint_timer.sh
15
+
16
+ set -uo pipefail
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
19
+ TIMER_JS="$SCRIPT_DIR/lib/checkpoint-timer.js"
20
+ SESSION_START="$SCRIPT_DIR/session-start.sh"
21
+ SESSION_END="$SCRIPT_DIR/session-end.sh"
22
+
23
+ WM_DIR="$(mktemp -d -t meko-timer-test-XXXXXX)"
24
+ TX_ROOT="$(mktemp -d -t meko-timer-tx-XXXXXX)"
25
+ # Exercise session-end matching when the absolute transcript path contains
26
+ # characters that have special meaning in a regular expression.
27
+ TX_DIR="$TX_ROOT/path with [regex] chars"
28
+ mkdir -p "$TX_DIR"
29
+ SESSION_ID="timer-test-$$"
30
+ TRANSCRIPT_PATH="$TX_DIR/${SESSION_ID}.jsonl"
31
+ PID_FILE="$WM_DIR/${SESSION_ID}.timer.pid"
32
+
33
+ PASS=0
34
+ FAIL=0
35
+ SPAWNED_PIDS=()
36
+
37
+ cleanup() {
38
+ for p in "${SPAWNED_PIDS[@]:-}"; do kill "$p" 2>/dev/null || true; done
39
+ rm -rf "$WM_DIR" "$TX_ROOT"
40
+ }
41
+ trap cleanup EXIT
42
+
43
+ log() { printf " %-55s" "$1"; }
44
+ pass() { printf "\033[32mPASS\033[0m %s\n" "${1:-}"; PASS=$((PASS + 1)); }
45
+ fail() { printf "\033[31mFAIL\033[0m %s\n" "${1:-}"; FAIL=$((FAIL + 1)); }
46
+
47
+ # Poll up to N deciseconds for a process to die. Returns 0 if it died.
48
+ wait_gone() {
49
+ local pid="$1" tries="${2:-50}"
50
+ while [ "$tries" -gt 0 ]; do
51
+ kill -0 "$pid" 2>/dev/null || return 0
52
+ sleep 0.1
53
+ tries=$((tries - 1))
54
+ done
55
+ return 1
56
+ }
57
+
58
+ export MEKO_WATERMARK_DIR="$WM_DIR"
59
+
60
+ echo "============================================================"
61
+ echo "Test: checkpoint-timer regression (issue #159)"
62
+ echo "============================================================"
63
+ echo " Watermark dir: $WM_DIR"
64
+ echo " Transcript: $TRANSCRIPT_PATH"
65
+ echo ""
66
+
67
+ # ---------------------------------------------------------------------------
68
+ # Defect 1 — timer spawns even though transcript does not exist yet.
69
+ # session-start.sh backgrounds the timer; capture.js session-start will fail to
70
+ # reach MCP but must not block the spawn. The timer must remain alive after its
71
+ # first tick while transcript creation is still within the startup grace period.
72
+ # ---------------------------------------------------------------------------
73
+ echo "--- Defect 1: spawn race (transcript absent at SessionStart) ---"
74
+ log "Transcript absent before hook"
75
+ [ ! -f "$TRANSCRIPT_PATH" ] && pass || fail "transcript unexpectedly exists"
76
+
77
+ # Spawn the timer directly the way the fixed session-start.sh does. A one-second
78
+ # interval guarantees the missing-transcript branch runs before the assertion.
79
+ MEKO_CHECKPOINT_INTERVAL=1 MEKO_TIMER_MAX_AGE=999 MEKO_TIMER_IDLE_EXIT=999 \
80
+ MEKO_TIMER_STARTUP_GRACE=10 \
81
+ nohup node "$TIMER_JS" "$TRANSCRIPT_PATH" </dev/null >/dev/null 2>&1 &
82
+ TIMER_PID=$!
83
+ SPAWNED_PIDS+=("$TIMER_PID")
84
+ sleep 1.5
85
+
86
+ log "Timer survives first tick with missing transcript"
87
+ if kill -0 "$TIMER_PID" 2>/dev/null; then pass "pid $TIMER_PID"; else fail "timer did not stay up"; fi
88
+
89
+ log "Timer wrote its pid file"
90
+ [ -f "$PID_FILE" ] && pass || fail "no pid file at $PID_FILE"
91
+
92
+ kill "$TIMER_PID" 2>/dev/null || true
93
+ wait_gone "$TIMER_PID" || true
94
+ rm -f "$PID_FILE"
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # Defect 3a — max-age self-exit. Transcript exists and is fresh, but max-age is
98
+ # tiny, so the first tick must self-exit and remove its pid file.
99
+ # ---------------------------------------------------------------------------
100
+ echo ""
101
+ echo "--- Defect 3a: max-age self-exit backstop ---"
102
+ printf '{"type":"user","uuid":"u1","message":{"content":"hi"}}\n' > "$TRANSCRIPT_PATH"
103
+
104
+ MEKO_CHECKPOINT_INTERVAL=1 MEKO_TIMER_MAX_AGE=1 MEKO_TIMER_IDLE_EXIT=999 \
105
+ nohup node "$TIMER_JS" "$TRANSCRIPT_PATH" </dev/null >/dev/null 2>&1 &
106
+ TIMER_PID=$!
107
+ SPAWNED_PIDS+=("$TIMER_PID")
108
+
109
+ log "Timer self-exits after max-age"
110
+ if wait_gone "$TIMER_PID" 60; then pass; else fail "still running after max-age"; fi
111
+
112
+ log "Pid file removed on self-exit"
113
+ [ ! -f "$PID_FILE" ] && pass || fail "stale pid file remains"
114
+
115
+ # ---------------------------------------------------------------------------
116
+ # Defect 3b — idle self-exit. Make the transcript look old (mtime in the past)
117
+ # so the idle backstop trips on the first tick.
118
+ # ---------------------------------------------------------------------------
119
+ echo ""
120
+ echo "--- Defect 3b: idle self-exit backstop ---"
121
+ printf '{"type":"user","uuid":"u1","message":{"content":"hi"}}\n' > "$TRANSCRIPT_PATH"
122
+ touch -t 202001010000 "$TRANSCRIPT_PATH" # far in the past
123
+
124
+ MEKO_CHECKPOINT_INTERVAL=1 MEKO_TIMER_MAX_AGE=999 MEKO_TIMER_IDLE_EXIT=1 \
125
+ nohup node "$TIMER_JS" "$TRANSCRIPT_PATH" </dev/null >/dev/null 2>&1 &
126
+ TIMER_PID=$!
127
+ SPAWNED_PIDS+=("$TIMER_PID")
128
+
129
+ log "Timer self-exits on idle transcript"
130
+ if wait_gone "$TIMER_PID" 60; then pass; else fail "still running with idle transcript"; fi
131
+
132
+ # ---------------------------------------------------------------------------
133
+ # Defect 3c — session-end.sh kills by transcript path when the pid file is gone.
134
+ # ---------------------------------------------------------------------------
135
+ echo ""
136
+ echo "--- Defect 3c: session-end path-based kill (no pid file) ---"
137
+ printf '{"type":"user","uuid":"u1","message":{"content":"hi"}}\n' > "$TRANSCRIPT_PATH"
138
+
139
+ # Long-lived timer; then delete its pid file to simulate the orphan condition.
140
+ MEKO_CHECKPOINT_INTERVAL=999 MEKO_TIMER_MAX_AGE=999 MEKO_TIMER_IDLE_EXIT=999 \
141
+ nohup node "$TIMER_JS" "$TRANSCRIPT_PATH" </dev/null >/dev/null 2>&1 &
142
+ TIMER_PID=$!
143
+ SPAWNED_PIDS+=("$TIMER_PID")
144
+ sleep 0.5
145
+ rm -f "$PID_FILE" # pid file vanished out of band -> classic orphan setup
146
+
147
+ log "Orphan timer running with no pid file"
148
+ if kill -0 "$TIMER_PID" 2>/dev/null && [ ! -f "$PID_FILE" ]; then pass "pid $TIMER_PID"; else fail "precondition not met"; fi
149
+
150
+ # session-end.sh must find it by transcript path and kill it.
151
+ printf '{"transcript_path":"%s"}' "$TRANSCRIPT_PATH" | bash "$SESSION_END" >/dev/null 2>&1 || true
152
+
153
+ log "session-end.sh killed the orphan by transcript path"
154
+ if wait_gone "$TIMER_PID" 30; then pass; else fail "orphan survived session-end"; fi
155
+
156
+ # ---------------------------------------------------------------------------
157
+ echo ""
158
+ echo "============================================================"
159
+ TOTAL=$((PASS + FAIL))
160
+ echo " Total: $TOTAL | Passed: $PASS | Failed: $FAIL"
161
+ echo "============================================================"
162
+ [ "$FAIL" -gt 0 ] && { echo " RESULT: FAILED"; exit 1; } || { echo " RESULT: ALL PASSED"; exit 0; }
@@ -6,8 +6,9 @@
6
6
  set -euo pipefail
7
7
 
8
8
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
- SKILLS_ROOT="$SCRIPT_DIR/../skills"
9
+ SKILLS_ROOT="${MEKO_DESKTOP_SKILLS_ROOT:-$SCRIPT_DIR/../skills}"
10
10
  SKILL_NAMES=("meko-mcp-tools-desktop" "meko-select-datapack-desktop")
11
+ XML_TAG_PATTERN='</?[[:alpha:]_:][[:alnum:]_.:-]*([[:blank:]][^<>]*)?[[:blank:]]*/?>'
11
12
 
12
13
  for SKILL_NAME in "${SKILL_NAMES[@]}"; do
13
14
  if [ ! -d "$SKILLS_ROOT/$SKILL_NAME" ]; then
@@ -19,6 +20,36 @@ for SKILL_NAME in "${SKILL_NAMES[@]}"; do
19
20
  echo "Error: SKILL.md not found in $SKILLS_ROOT/$SKILL_NAME/" >&2
20
21
  exit 1
21
22
  fi
23
+
24
+ if ! grep -q '^description:[[:space:]]*' "$SKILLS_ROOT/$SKILL_NAME/SKILL.md"; then
25
+ echo "Error: SKILL.md description is missing: $SKILL_NAME" >&2
26
+ exit 1
27
+ fi
28
+
29
+ DESCRIPTION="$(awk '
30
+ /^description:[[:space:]]*/ {
31
+ in_description = 1
32
+ value = $0
33
+ sub(/^description:[[:space:]]*/, "", value)
34
+ if (value !~ /^[>|][+-]?$/) printf "%s ", value
35
+ next
36
+ }
37
+ in_description && /^[^[:space:]]/ { exit }
38
+ in_description {
39
+ sub(/^[[:space:]]+/, "")
40
+ printf "%s ", $0
41
+ }
42
+ ' "$SKILLS_ROOT/$SKILL_NAME/SKILL.md")"
43
+
44
+ if [[ -z "${DESCRIPTION//[[:space:]]/}" ]]; then
45
+ echo "Error: SKILL.md description is empty: $SKILL_NAME" >&2
46
+ exit 1
47
+ fi
48
+
49
+ if printf '%s\n' "$DESCRIPTION" | grep -Eq "$XML_TAG_PATTERN"; then
50
+ echo "Error: SKILL.md description cannot contain XML-like tags: $SKILL_NAME" >&2
51
+ exit 1
52
+ fi
22
53
  done
23
54
 
24
55
  for SKILL_NAME in "${SKILL_NAMES[@]}"; do
@@ -4,13 +4,14 @@ description: Behavioral guide for AI agents using Meko MCP tools. Triggers when
4
4
  license: MIT
5
5
  metadata:
6
6
  author: Meko
7
- version: "3.0.0"
7
+ version: "3.0.2"
8
8
  tags: mcp, tools, datapack, memory, conversation, rag, meko
9
9
  ---
10
10
 
11
11
  # Meko MCP Tools — Agent Behavioral Guide
12
12
 
13
- Meko is agent-native data infrastructure that enables continuous learning from context windows through collective memory and shared knowledge. This skill teaches you how to use Meko's 22 MCP tools.
13
+ Meko is agent-native data infrastructure that enables continuous learning from context windows through collective memory and shared knowledge. This skill teaches you how to use Meko's 23 MCP tools.
14
+
14
15
 
15
16
  **Read this first.** On Claude Code, conversation capture is automatic (SessionStart / PreCompact / SessionEnd hooks) and the Meko server extracts durable memories from the captured turns on its own. Facts the user states in conversation are saved for you — you do **not** proactively call `memory_add` for them. Your active jobs are: **recall** what's already known (`memory_search`, `knowledgebase_search`), and the **few** save cases automatic extraction cannot reach. Details below.
16
17
 
@@ -18,8 +19,8 @@ Meko is agent-native data infrastructure that enables continuous learning from c
18
19
 
19
20
  Meko turns your volatile context window into persistent, shareable knowledge:
20
21
 
21
- - **Personal memory** — Store who the user is, their preferences, role, and working style. Private to you (scoped per-user and per-agent). Survives across sessions.
22
- - **Team-shared knowledge** — When the user promotes important memories from the Cloud UI's Learnings tab (or uploads documents via **Add Knowledge**), the content becomes visible to every member of the datapack. Queryable via `knowledgebase_search`.
22
+ - **Personal memory** — Store who the user is, their preferences, role, and working style. Private to you (scoped per-user; readable across all of your agents). Survives across sessions.
23
+ - **Team-shared knowledge** — Important memories can be promoted with `memory_promote` (after explicit confirmation) or from the Cloud UI's Learnings tab. Uploaded documents arrive through **Add Knowledge**. The resulting content is visible to every datapack member and queryable via `knowledgebase_search`.
23
24
  - **Conversation history** — Preserve full dialog exchanges with reasoning traces for audit, replay, and learning transfer.
24
25
  - **Decision traces** — Capture how and why decisions were made, enabling debugging and continuous improvement.
25
26
 
@@ -29,7 +30,7 @@ Meko turns your volatile context window into persistent, shareable knowledge:
29
30
  - You need to decide what to store, where, and how (personal memory vs. team-shared knowledge)
30
31
  - You want to search the team's shared knowledge base (`knowledgebase_search`)
31
32
  - You are managing datapacks
32
- - You want to understand scope/permission requirements, `agent_id` conventions, or `datapack_id` routing
33
+ - You want to understand `agent_id` conventions, or `datapack_id` routing
33
34
 
34
35
  ## `agent_id` — multi-agent identity
35
36
 
@@ -46,10 +47,12 @@ agent_id = "claude_code:meko-mcp-server" # this session's project bucket
46
47
  agent_id = "meko_agent" # cross-project facts
47
48
  ```
48
49
 
49
- The SessionStart hook injects the project-bucket value into the first-turn `additionalContext`. Use that value verbatim for project-scoped Meko MCP calls in this session; do not re-derive it. Use `meko_agent` explicitly when you want a write or read to span projects.
50
+ The SessionStart hook injects the project-bucket value into the first-turn `additionalContext`. Use that value verbatim for project-scoped Meko MCP calls in this session; do not re-derive it. Use `meko_agent` explicitly when you want a write to land in the cross-project bucket.
50
51
 
51
52
  **Read scoping varies by tool:**
52
- - `memory_search` / `memory_get_all` / `conversation_list` / `conversation_get` — scoped to `(datapack_id, user_id, agent_id)`. Each agent_id is its own bucket; pass the exact value you want to read from. Empty/whitespace gets rewritten to `meko_agent` server-side, so it returns common-bucket rows only — **not** a cross-agent fan-out.
53
+ - `memory_search` / `memory_get_all` — scoped to `(datapack_id, user_id)`. **`agent_id` does not filter memory reads:** one call returns all of your memories for this user across every `agent_id` (the tool passes `meko_agent_id=None` to mem0, so `agent_id` scopes the Langfuse trace, not the result set). No need to fan out per agent_id or read the `meko_agent` bucket separately — a single search already includes it.
54
+ - `conversation_get` — agent-owned: pass the exact `agent_id` that created the conversation; any other value returns `agent_id_mismatch`.
55
+ - `conversation_list` — returns the datapack's conversations for your user regardless of `agent_id` (the argument is trace-attribution only, like memory reads).
53
56
  - `knowledgebase_search` — scoped to `(datapack_id)` only. `agent_id` on the request is **ignored**; you see the team's shared knowledge regardless of what you pass. Includes uploaded documents and memories that the user promoted via the UI's "Promote to Knowledge" flow.
54
57
 
55
58
  See `tools-agent-id-conventions.md` for the full model, including how promotion works and how to phrase broad-query responses to the user.
@@ -131,8 +134,9 @@ So your Meko reflexes are:
131
134
  | User corrects your behavior or negates a prior fact ("actually, we use Nomad now") | The new fact is captured automatically, but extraction is **additive** — it does not remove the old one. If a stale, contradicted memory must not survive, `memory_search` for it and `memory_update` / `memory_delete_by_id`. |
132
135
  | User explicitly says "remember this" / "save this to memory" | `memory_add` (verbatim). This is the clearest explicit-save case. |
133
136
  | A durable fact appears only in YOUR output or a tool result, never in the user's words | `memory_add`. Extraction reads only the **user** turn, so facts you derive or a tool surfaces are never captured otherwise. |
134
- | User asks "what do you know about X?" | Three-call sweep: `memory_search(agent_id=<yours>)` → `memory_search(agent_id="meko_agent")` → `knowledgebase_search`. |
137
+ | User asks "what do you know about X?" | Two surfaces: `memory_search` (one call — all your personal memories for this user, every agent) then `knowledgebase_search` (team-shared knowledge). |
135
138
  | User asks "what do we (as a team) know about X?" or about uploaded documents | `knowledgebase_search` (returns uploaded docs + promoted memories). |
139
+ | User asks to share specific private memories with the team | Follow the `memory_promote` confirmation workflow below. Never promote based on a fuzzy search result or implied consent. |
136
140
  | User provides structured/tabular data (CSV, data dictionary) | Not natively supported via MCP. Point the user at the UI's Add Knowledge upload; do not `memory_add` row-by-row. |
137
141
  | User wants to add documents to the team's knowledge base | Point them at the Cloud UI flow — Datapack → Actions → **Add Knowledge** (PDF/TXT/MD/JSON/MP4, 5MB each, 10/batch). MCP ingestion tools are not available on Cloud. |
138
142
 
@@ -161,7 +165,7 @@ All three are handled automatically. **Do NOT set up CronCreate for conversation
161
165
  The SessionStart hook has already done the setup for you:
162
166
 
163
167
  1. **Created a Meko conversation** — `conversation_id` is injected into `additionalContext` on the first turn. Use that value for all subsequent MCP tool calls that accept `conversation_id`.
164
- 2. **Echoed the session's `agent_id`** — also injected into `additionalContext`. Use the injected value verbatim for project-scoped Meko tool calls. It's a per-session value derived from the cwd (shape: `claude_code:<repo-basename>`), not a constant. For genuinely cross-project facts, write/read with `agent_id="meko_agent"` (the common bucket) instead.
168
+ 2. **Echoed the session's `agent_id`** — also injected into `additionalContext`. Use the injected value verbatim for project-scoped Meko tool calls. It's a per-session value derived from the cwd (shape: `claude_code:<repo-basename>`), not a constant. For genuinely cross-project facts, write with `agent_id="meko_agent"` (the common bucket) instead.
165
169
  3. **Preloaded recent memories** — `additionalContext` may include a `### Memories from prior sessions` block. Inspect it before acting so you don't re-ask questions the user has already answered.
166
170
 
167
171
  If `additionalContext` is absent or empty (hook failed, MCP unavailable), ask the user for their `agent_id` — do not invent one. See `tools-agent-id-conventions.md`.
@@ -171,11 +175,11 @@ If `additionalContext` is absent or empty (hook failed, MCP unavailable), ask th
171
175
  Before any action that deletes, overwrites, or restructures user content — `Write` overwriting an existing file, large `Edit` deletions, `rm` / `rm -rf`, `git reset --hard`, destructive SQL (`DROP`, `TRUNCATE`, `DELETE` without `WHERE`) — run a `memory_search` for relevant feedback:
172
176
 
173
177
  ```
174
- memory_search(scope="read", query="destructive <action type> preferences feedback",
178
+ memory_search(query="destructive <action type> preferences feedback",
175
179
  agent_id=<your agent_id>, conversation_id=<session_conversation_id>)
176
180
  ```
177
181
 
178
- Also consider a second call with `agent_id="meko_agent"` to surface common-bucket feedback — preferences the user (or any other agent) wrote under the cross-project bucket. And a `knowledgebase_search` for team-wide guidance if the action is one the team has promoted policy on.
182
+ A single `memory_search` already covers everything you and other agents wrote for this user — `agent_id` doesn't filter memory reads. Also consider a `knowledgebase_search` for team-wide guidance if the action is one the team has promoted policy on.
179
183
 
180
184
  If a relevant feedback memory exists (e.g., "always ask before deleting content"), confirm with the user before proceeding. If the search is empty, proceed, but treat the action as irreversible and prefer a dry-run / preview when one is available.
181
185
 
@@ -196,46 +200,53 @@ In the examples below, `<your-agent-id>` means the value the SessionStart hook i
196
200
 
197
201
  Only for the three narrow cases in "When to call memory_add" above — not for facts the user stated in conversation (those are captured automatically). The example below is an explicit "remember this" save:
198
202
  ```
199
- memory_add(scope="write",
203
+ memory_add(
200
204
  text="Remember: deploy scripts must be run from the repo root, never a subdir",
201
205
  agent_id="<your-agent-id>",
202
206
  conversation_id="<session_conversation_id>")
203
207
  ```
204
- Required parameters: `scope`, `text`, `conversation_id`. On **writes**, `conversation_id` must be a real UUID (from `conversation_create` or the SessionStart hook) — writes attached to a nil/empty conversation are orphaned in Langfuse traces. NEVER pass `"current"` or other non-UUID values. `agent_id` is technically optional — empty/missing routes the write into the `meko_agent` common bucket — but for project-scoped facts pass the SessionStart-injected value verbatim.
208
+ Required parameters: `text`, `conversation_id`. On **writes**, `conversation_id` must be a real UUID (from `conversation_create` or the SessionStart hook) — writes attached to a nil/empty conversation are orphaned in Langfuse traces. NEVER pass `"current"` or other non-UUID values. `agent_id` is technically optional — empty/missing routes the write into the `meko_agent` common bucket — but for project-scoped facts pass the SessionStart-injected value verbatim.
205
209
 
206
210
  ### memory_search — correct call pattern
207
211
  ```
208
- memory_search(scope="read",
212
+ memory_search(
209
213
  query="user role",
210
214
  agent_id="<your-agent-id>",
211
215
  conversation_id="<session_conversation_id>")
212
216
  ```
213
- Required parameters: `scope`, `query`, `agent_id`, `conversation_id`. On **`memory_search`** specifically, `conversation_id` is used only for Langfuse trace nesting — it does NOT filter results. Pass the session's UUID so the search span appears under the active conversation in Observe; passing `""` is accepted and means "don't nest under any trace." For conversation-scoped filtering use the separate `run_id` parameter.
214
-
215
- For cross-project common-bucket search (facts written under `agent_id="meko_agent"`, including everything you wrote with empty/missing agent_id), pass it explicitly:
216
- ```
217
- memory_search(scope="read", query="user role",
218
- agent_id="meko_agent", conversation_id="<session_conversation_id>")
219
- ```
217
+ Required parameters: `query`, `agent_id`, `conversation_id`. On **`memory_search`** specifically, `conversation_id` is used only for Langfuse trace nesting — it does NOT filter results. Pass the session's UUID so the search span appears under the active conversation in Observe; passing `""` is accepted and means "don't nest under any trace." For conversation-scoped filtering use the separate `run_id` parameter.
220
218
 
221
- Empty string also resolves to `meko_agent` server-side, but be explicit so it's obvious from the call you mean the common bucket. To search results across multiple specific projects (e.g. also see what was written from `claude_code:other-repo`), call `memory_search` once per agent_id — there's no single-call fan-out.
219
+ `agent_id` does not filter what `memory_search` returns — a single call already surfaces everything you and every other agent wrote for this user in the datapack, including the `meko_agent` common bucket and any other project's rows. There's no need to repeat the search per agent_id. Use `run_id` if you want to narrow to a single conversation/run.
222
220
 
223
221
  ### knowledgebase_search — correct call pattern
224
222
  ```
225
- knowledgebase_search(scope="read",
223
+ knowledgebase_search(
226
224
  query="...",
227
225
  agent_id="<anything — ignored>",
228
226
  conversation_id="<session_conversation_id>",
229
227
  datapack_id="<datapack UUID>",
230
228
  limit=10)
231
229
  ```
232
- Required parameters: `scope`, `query`, `agent_id`, `conversation_id`, `datapack_id`. `datapack_id` has no default here. `agent_id` is ignored for filtering — results are the team's shared knowledge on the datapack regardless of what you pass.
230
+ Required parameters: `query`, `agent_id`, `conversation_id`, `datapack_id`. `datapack_id` has no default here. `agent_id` is ignored for filtering — results are the team's shared knowledge on the datapack regardless of what you pass.
231
+
232
+ ### memory_promote — explicit confirmation required
233
+
234
+ `memory_promote` is destructive and non-idempotent: it makes selected content visible to the datapack, moves the memories and graph context into shared knowledge, and evicts the private mem0 records. There is no MCP rollback.
235
+
236
+ 1. Use `memory_search` or `memory_get_all` to obtain exact memory UUIDs. Never pass relation or graph-edge IDs.
237
+ 2. Present each exact candidate memory and its UUID to the user.
238
+ 3. State that promotion is one-way, team-visible, and removes the private records.
239
+ 4. Obtain explicit confirmation for those exact candidates before calling `memory_promote`.
240
+ 5. Call it with the active `conversation_id`, exact `memory_ids`, intended `agent_id`, and intended `datapack_id`. If a legacy deployment exposes `scope`, use the minimum `write` scope; do not escalate to `admin`.
241
+ 6. Only datapack owners and maintainers may promote. If a viewer/contributor receives 403, or any authentication/permission error occurs, report it and stop. Do not retry with a broader scope, another datapack, or altered identity.
242
+ 7. After success, optionally verify visibility with `knowledgebase_search`. Do not claim rollback is possible.
243
+
244
+ The Cloud UI's Learnings tab remains an alternative user-driven path.
233
245
 
234
246
  ### Critical parameter rules
235
247
 
236
248
  - **agent_id**: Per-session value, injected via `additionalContext` by the SessionStart hook. Shape: `<client>:<repo-basename>`. Pass verbatim on project-scoped writes and reads. Pass `"meko_agent"` for the cross-project common bucket (or empty/omit — server rewrites empty to `meko_agent`). Ignored on `knowledgebase_search`. See `tools-agent-id-conventions.md`.
237
249
  - **conversation_id**: Behavior varies by tool. On write tools (`memory_add`, `conversation_add_message`) pass a real UUID from `conversation_create` or the SessionStart hook — a nil/empty value orphans the Langfuse trace. On `memory_search` it's used only for trace nesting (not for filtering), and empty-string is accepted; on `memory_get_all` and most read tools, still pass the session UUID when you have one. Never pass `"current"` or other non-UUID junk — only a real UUID or an intentionally empty string where the tool allows it.
238
- - **scope**: Only `"read"`, `"write"`, or `"admin"`. Nothing else. Use the minimum the code actually requires (the per-tool cookbook shows it).
239
250
  - **When in doubt about optional parameters, omit them.** The server has sensible defaults.
240
251
 
241
252
  ### Verify after writing
@@ -254,7 +265,6 @@ On the first Meko tool call in a fresh session (no prior Meko tool call since pr
254
265
 
255
266
  **Canary memory shape.** This is the spec; keep the fields stable so support can grep for them.
256
267
 
257
- - `scope`: `"write"`
258
268
  - `agent_id`: the session's `agent_id` (from `additionalContext`); not a constant
259
269
  - `conversation_id`: the session's `conversation_id` (from `additionalContext`). If the hook failed and you truly have no conversation_id, call `conversation_create` first and use the returned id — do not use a nil UUID, it orphans the canary trace.
260
270
  - `text`: `"Meko connection test: <client> @ <installer-version> — <ISO timestamp>. Safe to delete."`
@@ -268,31 +278,27 @@ On the first Meko tool call in a fresh session (no prior Meko tool call since pr
268
278
 
269
279
  ## Key concepts
270
280
 
271
- 1. **22 tools in 5 groups**: Memory (8), Conversation (6), Knowledge Base (1 — `knowledgebase_search`), Datapack (5), Artifacts (2 — `artifact_put`, `artifact_get`). KB ingestion is UI-only (Datapack → Actions → Add Knowledge); raw SQL is not exposed. See `tools-overview.md`.
272
- 2. **Scope hierarchy**: `read < write < admin` — every tool validates scope at entry. Only three valid values: `"read"`, `"write"`, `"admin"`. Using anything else (e.g., `"all"`) will fail. Tool docstrings sometimes say `"Pass 'admin'"` but the code only requires `"read"` or `"write"` — use the minimum the code actually enforces.
273
- 3. **datapack_id routing**: DB, RAG, and memory tools accept optional `datapack_id` (default datapack if omitted). `knowledgebase_search` is the exception: `datapack_id` is **required** there.
274
- 4. **agent_id is multi-agent**: Each client+project uses its own `agent_id` (shape `<client>:<repo-basename>`). Writes are scoped to `(datapack_id, user_id, agent_id)`. Personal reads (`memory_search`, `memory_get_all`) filter strictly on this tuple. Empty/missing `agent_id` lands in the `meko_agent` common bucket — use it explicitly for cross-project facts. `knowledgebase_search` ignores `agent_id` entirely and returns the team-shared knowledge on the datapack. See `tools-agent-id-conventions.md`.
275
- 5. **Personal memory vs. team-shared knowledge**: Un-promoted memories (`mem0_collection`) are scoped per-user and per-agent — only you see them. The user can promote important memories to Shared Knowledge via the Cloud UI's Learnings tab, which makes them visible to every team member on the datapack (user_id filter stripped). `knowledgebase_search` is the MCP read path for both Shared Knowledge and uploaded documents. See `tools-memory-vs-conversation.md`.
276
- 6. **conversation_id IS the Langfuse trace ID**: every MCP tool call inside a conversation becomes a span under that trace. Observable in the Meko UI's Observe hub (`/observe-hub?project=<langfuse_pid>&session=<conversation_id>`) or directly from a datapack's Conversations tab via the **Open in Observe** button. See `tools-memory-vs-conversation.md`.
277
- 7. **Adding documents or growing a knowledge base is UI-only on Cloud today**: users upload files via Datapack → Actions → **Add Knowledge** (PDF/TXT/MD/JSON/MP4, 5MB each). The MCP ingestion tools are not available on Cloud. See `tools-rag-workflow.md`.
278
- 8. **Conversation dedup**: `conversation_add_message` supports seed-based deterministic trace IDs to prevent duplicates.
279
- 9. **Memory latencies are not cheap**: observed on prod 2026-05-07 — `memory_add` 12-21s/call (calls OpenAI), `memory_search` 2-6s/call. Budget accordingly.
280
- 10. **Known limitations**: see `tools-known-limitations.md` before attempting write operations on tools that appear in the catalog but aren't available on Cloud.
281
- 11. **Error recovery**: Connection errors are common with memory tools — see `tools-troubleshooting.md` for retry strategies and fallbacks.
281
+ 1. **23 tools in 6 groups**: Memory (8 — including `memory_promote`), Conversation (6), Knowledge Base (1 — `knowledgebase_search`), Datapack (5), Artifacts (2 — `artifact_put`, `artifact_get`), Observability (1 — `track_token_usage`). KB ingestion is UI-only (Datapack → Actions → Add Knowledge); raw SQL is not exposed. See `tools-overview.md`.
282
+ 2. **datapack_id routing**: DB, RAG, and memory tools accept optional `datapack_id` (default datapack if omitted). `knowledgebase_search` is the exception: `datapack_id` is **required** there.
283
+ 3. **agent_id is multi-agent**: Each client+project uses its own `agent_id` (shape `<client>:<repo-basename>`). Writes are attributed to `(datapack_id, user_id, agent_id)`. Personal **memory** reads (`memory_search`, `memory_get_all`) are NOT filtered by `agent_id` — one call returns all of your memories for this user across every agent, scoped to `(datapack_id, user_id)`. Empty/missing `agent_id` on a write lands in the `meko_agent` common bucket. `knowledgebase_search` ignores `agent_id` entirely and returns the team-shared knowledge on the datapack. See `tools-agent-id-conventions.md`.
284
+ 4. **Personal memory vs. team-shared knowledge**: Un-promoted memories (`mem0_collection`) are scoped per-user — readable across all of your agents, but no other user can see them. With explicit confirmation, `memory_promote` moves exact memories into Shared Knowledge; the Cloud UI's Learnings tab is an alternative. Promoted content is visible to every datapack member (user_id filter stripped). `knowledgebase_search` is the MCP read path for both Shared Knowledge and uploaded documents. See `tools-memory-vs-conversation.md`.
285
+ 5. **conversation_id IS the Langfuse trace ID**: every MCP tool call inside a conversation becomes a span under that trace. Observable in the Meko UI's Observe hub (`/observe-hub?project=<langfuse_pid>&session=<conversation_id>`) or directly from a datapack's Conversations tab via the **Open in Observe** button. See `tools-memory-vs-conversation.md`.
286
+ 6. **Adding documents or growing a knowledge base is UI-only on Cloud today**: users upload files via Datapack → Actions → **Add Knowledge** (PDF/TXT/MD/JSON/MP4, 5MB each). The MCP ingestion tools are not available on Cloud. See `tools-rag-workflow.md`.
287
+ 7. **Conversation dedup**: `conversation_add_message` supports seed-based deterministic trace IDs to prevent duplicates.
288
+ 8. **Memory latencies are not cheap**: observed on prod 2026-05-07 — `memory_add` 12-21s/call (calls OpenAI), `memory_search` 2-6s/call. Budget accordingly.
289
+ 9. **Known limitations**: see `tools-known-limitations.md` before attempting write operations on tools that appear in the catalog but aren't available on Cloud.
290
+ 10. **Error recovery**: Connection errors are common with memory tools — see `tools-troubleshooting.md` for retry strategies and fallbacks.
282
291
 
283
292
  ## Reference sections
284
293
 
285
294
  | File | What it covers |
286
295
  |------|---------------|
287
- | `tools-overview.md` | Complete catalog of all 22 tools with decision tree |
296
+ | `tools-overview.md` | Complete catalog of all 23 tools with decision tree |
288
297
  | `tools-cookbook.md` | Per-tool examples with correct parameters, responses, and error cases |
289
298
  | `tools-memory-vs-conversation.md` | When to use memory tools vs conversation tools |
290
299
  | `tools-datapack-workflow.md` | Datapack lifecycle and datapack_id routing |
291
300
  | `tools-rag-workflow.md` | End-to-end RAG pipeline flow |
292
- | `tools-write-guardrails.md` | Blocked SQL patterns and configurable limits |
293
- | `tools-scope-permissions.md` | read/write/admin permission hierarchy |
294
301
  | `tools-agent-id-conventions.md` | Naming rules and constraints for agent_id |
295
302
  | `tools-conversation-dedup.md` | Seed-based trace deduplication |
296
- | `tools-db-query-patterns.md` | Read-only vs write query execution patterns |
297
303
  | `tools-troubleshooting.md` | Error recovery, retry strategies, stuck pipeline diagnosis |
298
304
  | `tools-known-limitations.md` | Broken tools, missing capabilities, permission gaps |
@@ -6,17 +6,15 @@ description: Maps reference files to their topics for the meko-mcp-tools skill
6
6
  # Reference Sections
7
7
 
8
8
  ## Tool Selection & Catalog
9
- - [tools-overview.md](tools-overview.md) — Complete catalog of all 22 tools with a decision tree
9
+ - [tools-overview.md](tools-overview.md) — Complete catalog of all 23 tools with a decision tree
10
10
 
11
11
  ## Tool-Specific Guidance
12
12
  - [tools-memory-vs-conversation.md](tools-memory-vs-conversation.md) — Decision framework: memory_add vs conversation_create/conversation_add_message
13
+ - [tools-conversation-reasoning.md](tools-conversation-reasoning.md) — Authoring the `reasoning` and `plan` fields on `conversation_add_message`, with worked examples
13
14
  - [tools-datapack-workflow.md](tools-datapack-workflow.md) — Datapack lifecycle, datapack_id routing, and multi-database patterns
14
15
  - [tools-rag-workflow.md](tools-rag-workflow.md) — End-to-end RAG pipeline: source registration, index creation, status polling
15
- - [tools-db-query-patterns.md](tools-db-query-patterns.md) — Read-only vs write query execution patterns and SQL conventions
16
16
 
17
17
  ## Safety & Permissions
18
- - [tools-write-guardrails.md](tools-write-guardrails.md) — Blocked SQL operations, bulk insert limits, WHERE clause enforcement
19
- - [tools-scope-permissions.md](tools-scope-permissions.md) — read/write/admin permission hierarchy and per-tool requirements
20
18
 
21
19
  ## Conventions & Deduplication
22
20
  - [tools-agent-id-conventions.md](tools-agent-id-conventions.md) — agent_id naming rules, hyphen prohibition, and namespace isolation
@@ -1,12 +1,12 @@
1
1
  # agent_id — multi-agent identity on Cloud Meko
2
2
 
3
- `agent_id` identifies **which agent wrote a memory or created a conversation**. It is not a constant. Pick a value that represents the specific agent + project you're acting for, use it consistently for writes within this session, and use it to scope your own reads.
3
+ `agent_id` identifies **which agent wrote a memory or created a conversation**. It is not a constant. Pick a value that represents the specific agent + project you're acting for and use it consistently for writes and trace attribution within this session.
4
4
 
5
- The Cloud UI (`cloud.mekodata.ai`) renders `agent_id` as a badge on every memory and conversation row — so the value you pick is user-visible. Multiple different agents with different `agent_id` values can write into the same datapack; each one's personal view is scoped to itself, and the UI shows the full cross-agent picture.
5
+ The Cloud UI (`cloud.mekodata.ai`) renders `agent_id` as a badge on every memory and conversation row — so the value you pick is user-visible. Multiple different agents with different `agent_id` values can write into the same datapack; the badge records who wrote each row, and both the UI and a `memory_search` show the full cross-agent picture for the user.
6
6
 
7
7
  ## The three buckets
8
8
 
9
- Memories segregate strictly by `agent_id`. Pick the right bucket up front:
9
+ Each memory is written under an `agent_id` (stored on the row, shown as the UI badge). It labels *who wrote* the row — it does not restrict who can later read it (`memory_search` spans all your agents for the user). Still, pick the right value up front so attribution is meaningful:
10
10
 
11
11
  | Pattern | When to use | Example |
12
12
  |---|---|---|
@@ -16,13 +16,15 @@ Memories segregate strictly by `agent_id`. Pick the right bucket up front:
16
16
 
17
17
  **Discover the value at session start.** The SessionStart hook injects the chosen `agent_id` into the first-turn `additionalContext` based on the cwd's repo basename. Use that value verbatim — do not re-derive it. If the block is absent, fall back to `<client>:<basename(cwd)>` or ask the user.
18
18
 
19
- For genuinely cross-project facts ("the user's name is Amiram", "the user prefers dark mode") write with `agent_id="meko_agent"` so future agents in different projects can read them.
19
+ For genuinely cross-project facts ("the user's name is Amiram", "the user prefers dark mode") write with `agent_id="meko_agent"` so their attribution clearly identifies the common cross-project bucket.
20
+
21
+ **Pick a value that matches the running client — don't invent another client's shape.** A Claude Desktop session that writes with `agent_id="claude_code"` (or `claude_code:something`) creates rows the UI will attribute to Claude Code — a misleading badge for both clients. Use the pattern from the table that matches the client you're actually running under.
20
22
 
21
23
  ## Valid characters
22
24
 
23
25
  The server stores `agent_id` as a row-level column value — it never becomes a PostgreSQL identifier on Cloud. Any printable string works: colons, hyphens, dots, underscores, spaces. Stay within what's readable in the UI badge.
24
26
 
25
- Pre-existing data in real datapacks includes a mix of legacy shapes — `agent`, `claude-code`, `claude_code`, `cursor:<slug>`, `claude-code:-Users-...`. None of them cause errors. To query those rows, pass the literal legacy value as `agent_id` (e.g. `agent_id="agent"` for rows from before the rewrite). New writes should follow the table above.
27
+ Pre-existing data in real datapacks includes a mix of legacy shapes — `agent`, `claude-code`, `claude_code`, `cursor:<slug>`, `claude-code:-Users-...`. None of them cause errors or prevent memory recall. New writes should follow the table above.
26
28
 
27
29
  ## How agent_id filters reads and writes
28
30
 
@@ -31,15 +33,19 @@ Pre-existing data in real datapacks includes a mix of legacy shapes — `agent`,
31
33
  - The row stores `(meko_datapack_id, meko_user_id, meko_agent_id)` — the user_id comes from your cognito account; the datapack_id defaults or is explicit; the agent_id is what you pass.
32
34
  - If you pass an empty or whitespace-only `agent_id`, the server stores `meko_agent`. That's the common bucket — fine for cross-project facts; not what you want for project-scoped writes.
33
35
 
34
- ### Personal reads — `memory_search`, `memory_get_all`, `conversation_list`, `conversation_get`
36
+ ### Personal memory reads — `memory_search`, `memory_get_all`
37
+
38
+ Filter tuple: `(datapack_id, user_id)`. `agent_id` is **not** applied on memory reads.
35
39
 
36
- Filter tuple: `(datapack_id, user_id, agent_id)`.
40
+ - `user_id` is **always enforced**: you can only see memories you wrote (or that other team members shared with you via promotion — see below). One user cannot read another team member's un-promoted memories directly from MCP.
41
+ - `agent_id` does **not** filter results. A single `memory_search` returns your memories for this user across *every* `agent_id`: a memory written under `agent_id="cursor:foo"` IS returned by a search passing `agent_id="claude_code:meko-mcp-server"` — or empty, or any value. The tool passes `meko_agent_id=None` to mem0, so `agent_id` scopes only the Langfuse trace, not the result set. There is nothing to "fan out"; one call already sees all of your agents' memories (including the `meko_agent` common bucket).
37
42
 
38
- - `user_id` is **always enforced**: you can only see memories and conversations you wrote (or that other team members shared with you via promotion — see below). One user cannot read another team member's un-promoted memories directly from MCP.
39
- - `agent_id` is **strictly matched**: a search with `agent_id="claude_code:meko-mcp-server"` returns only rows whose stored value is exactly that. A memory written by `agent_id="cursor:foo"` is invisible to it.
40
- - An empty `agent_id` is rewritten to `meko_agent` server-side, so a "broad" read with empty string returns only common-bucket rows — **not** a cross-agent fan-out. To search across multiple agent_ids, call once per known value.
43
+ Empirically verified 2026-07-23 on Cloud prod: memories written under two distinct `agent_id`s were both returned by a `memory_search` passing a third, never-written `agent_id` (and by one passing empty string).
41
44
 
42
- Empirically verified 2026-05-19 on Cloud prod (free-tier multitenant mode, segregation via row-level filter).
45
+ ### Personal conversation reads — `conversation_get`, `conversation_list`
46
+
47
+ - `conversation_get` — agent-owned. Filter tuple `(datapack_id, user_id, agent_id)`: pass the exact `agent_id` that created the conversation; a wrong value returns `agent_id_mismatch`.
48
+ - `conversation_list` — **not** agent-filtered. It lists by `(datapack_id, user_id)` only; `agent_id` on the call is trace-attribution only (like memory reads), so one call returns the datapack's conversations for your user regardless of which agent created them.
43
49
 
44
50
  ### Team-shared reads — `knowledgebase_search`
45
51
 
@@ -57,9 +63,10 @@ The originating `agent_id` is preserved in each result's `metadata_filters.agent
57
63
  memory_add(agent_id=X, text=...)
58
64
  │
59
65
  ▼
60
- mem0_collection ── personal memories, scoped (datapack, user, agent)
66
+ mem0_collection ── personal memories, tagged (datapack, user, agent)
61
67
  │
62
- │ UI "Promote to Knowledge" (Learnings tab)
68
+ │ memory_promote (exact UUIDs + explicit confirmation)
69
+ │ or UI "Promote to Knowledge" (Learnings tab)
63
70
  │ ─────────────────────────────────────────
64
71
  │ Strips user_id, keeps agent_id as metadata,
65
72
  │ copies into team-shared table
@@ -72,23 +79,20 @@ knowledgebase_search(agent_id=anything, query=...)
72
79
 
73
80
  - **Personal memories** are what you get from `memory_search` / `memory_get_all`. Private to you.
74
81
  - **Shared Knowledge** is what you get from `knowledgebase_search`. Visible to every user and every agent on the datapack.
75
- - **Promotion is UI-only.** There is no MCP tool for promoting a memory to Shared Knowledge — the user triggers it from the datapack's Learnings tab in the Cloud UI.
82
+ - **Promotion has MCP and UI paths.** `memory_promote` moves exact, user-confirmed memories into Shared Knowledge and evicts the private records; the Learnings tab is the user-driven alternative. Only datapack owners and maintainers may use the MCP path.
76
83
 
77
84
  ## When to use what — broad-query guidance
78
85
 
79
- If the user asks "what do you know about X?" and you want to maximize recall:
80
-
81
- 1. **First call**: `memory_search(agent_id="<your agent_id>", query="X")` — returns your own project's memories. Most relevant if the context is "what have you and I done in this repo."
82
- 2. **Second call**: `memory_search(agent_id="meko_agent", query="X")` — returns the common cross-project bucket (user identity, global preferences). Anything you wrote with `agent_id="meko_agent"` from any agent or project, plus rows the server stored under that name when called with empty `agent_id`.
83
- 3. **Third call**: `knowledgebase_search(agent_id="<anything>", datapack_id="<datapack>", query="X")` — returns team-shared knowledge. Useful when the answer may have been promoted by the user or a teammate.
86
+ If the user asks "what do you know about X?":
84
87
 
85
- For a true cross-agent fan-out (e.g. you also want to see what was written from `claude_code:other-repo` or `cursor:foo`), call `memory_search` once per known agent_id — the empty-string shortcut does not bypass the filter; the server rewrites empty to `meko_agent`.
88
+ 1. **`memory_search(agent_id="<your agent_id>", query="X")`** — returns all of your personal memories for this user, across every `agent_id` (your project bucket, the `meko_agent` common bucket, and any other project's rows). One call is the whole personal surface — `agent_id` doesn't filter memory reads, so there's nothing to fan out.
89
+ 2. **`knowledgebase_search(agent_id="<anything>", datapack_id="<datapack>", query="X")`** — returns team-shared knowledge. Useful when the answer may have been promoted by the user or a teammate.
86
90
 
87
- Always tell the user what scope you searched, so they understand why the answer is or isn't there. Example wording: "I found this in your project memories" vs. "I found this in your common cross-project bucket" vs. "I found this in your team's shared knowledge."
91
+ Always tell the user what scope you searched, so they understand why the answer is or isn't there. Example wording: "I found this in your personal memories" vs. "I found this in your team's shared knowledge."
88
92
 
89
- ## Sub-scoping within an agent
93
+ ## Optional sub-scoping
90
94
 
91
- Optional parameters narrow further within the same `agent_id`:
95
+ Optional parameters can narrow a call further:
92
96
  - `user_id` — explicitly scope writes/reads to a specific end-user if your agent serves multiple (rare in Claude Code; more common in API products)
93
97
  - `run_id` — per-execution run
94
98
 
@@ -96,6 +100,6 @@ Available on `memory_add`, `memory_search`, `memory_get_all`, `memory_delete_all
96
100
 
97
101
  ## Things I checked and found in-data on real Cloud datapacks
98
102
 
99
- - `agent_id="agent"` appears on many legacy rows — it was the previous canonical constant. New writes should use the schema in the table above; query legacy rows with `agent_id="agent"` explicitly.
100
- - Memory Summary UI (`/datapacks/<name>/memory-summary`) is agent-id-agnostic — shows every row in the datapack, renders the stored `agent_id` as a badge. The filtering is MCP-side only.
103
+ - `agent_id="agent"` appears on many legacy rows — it was the previous canonical constant. New writes should use the schema in the table above; memory reads still return the legacy rows.
104
+ - Memory Summary UI (`/datapacks/<name>/memory-summary`) is agent-id-agnostic — shows every row in the datapack, renders the stored `agent_id` as a badge. `memory_search` and `conversation_list` are likewise agent-agnostic (they return all your agents' rows for the user); only `conversation_get` enforces `agent_id` (agent ownership).
101
105
  - Subagents spawned via the `Agent` tool in Claude Code do not automatically inherit the parent's `agent_id`. The parent must inject it into the spawn prompt. See the SKILL.md "When spawning subagents" section.