mcp-agents-memory 0.9.19 → 0.9.21

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.
package/README.ko.md CHANGED
@@ -106,6 +106,7 @@
106
106
  mcp-agents-memory coldpath # MCP 서버 없이 Cold Path 워커만 도는 데몬 (systemd 권장)
107
107
  ```
108
108
  데몬은 PostgreSQL advisory lock으로 **싱글톤** 보장 — 인스턴스가 몇 개든 락을 잡은 1개만 처리한다(중복 방지·자동 failover).
109
+ - **버즈 (선택)**: `mcp-agents-memory buzz-ingest`는 지정한 신원이 멤버인 버즈 방의 채팅을 공식 `buzz` CLI로 한 건씩 메모리에 옮긴다. 인용된 글이 전부 행으로 들어오면 캡처된 버즈 턴의 이전 대화 인용도 걷어낸다(원문은 `raw_message`, 끄려면 `--no-reclean`). 타이머로 돌리고, 설정은 CHANGELOG 0.9.20–0.9.21 참고.
109
110
 
110
111
  ### Cold Path LLM 백엔드 (config로 교체)
111
112
 
package/README.md CHANGED
@@ -107,6 +107,7 @@ When several machines share **one** database, the Cold Path (tagging / profiling
107
107
  mcp-agents-memory coldpath # Cold Path worker only, no MCP server (systemd recommended)
108
108
  ```
109
109
  The daemon is a **singleton** via a PostgreSQL advisory lock — no matter how many instances exist, only the one holding the lock processes (dedup + automatic failover).
110
+ - **Buzz (optional)**: `mcp-agents-memory buzz-ingest` copies chat messages from the Buzz rooms a given identity is a member of into memory, one row per message, through the official `buzz` CLI. It also strips the quoted history from captured Buzz turns once every quoted message exists as a row (the original stays in `raw_message`; `--no-reclean` turns this off). Run it from a timer; see CHANGELOG 0.9.20–0.9.21 for the settings.
110
111
 
111
112
  ### Cold Path LLM backend
112
113
 
package/build/index.js CHANGED
@@ -12924,7 +12924,7 @@ var require_agent = __commonJS({
12924
12924
  var { Duplex } = __require("stream");
12925
12925
  var { resolve } = __require("path");
12926
12926
  var { readFile } = __require("fs");
12927
- var { execFile, spawn: spawn2 } = __require("child_process");
12927
+ var { execFile: execFile2, spawn: spawn2 } = __require("child_process");
12928
12928
  var { isParsedKey, parseKey } = require_keyParser();
12929
12929
  var {
12930
12930
  makeBufferParser,
@@ -13145,7 +13145,7 @@ var require_agent = __commonJS({
13145
13145
  if (err3) {
13146
13146
  if (triedCygpath)
13147
13147
  return cb(new Error("Invalid cygwin unix socket path"));
13148
- execFile("cygpath", ["-w", socketPath], (err4, stdout, stderr) => {
13148
+ execFile2("cygpath", ["-w", socketPath], (err4, stdout, stderr) => {
13149
13149
  if (err4 || stdout.length === 0)
13150
13150
  return cb(new Error("Invalid cygwin unix socket path"));
13151
13151
  triedCygpath = true;
@@ -32739,6 +32739,389 @@ var require_dist2 = __commonJS({
32739
32739
  }
32740
32740
  });
32741
32741
 
32742
+ // src/auto_save/buzz_envelope.ts
32743
+ function isNostrMetaLine(line) {
32744
+ return /^Tags: \[\[".*\]\]\s*$/.test(line) && /[0-9a-f]{64}/.test(line) || /^Parsed: (?:root=[0-9a-f]{64}|mentions=\[.*\([0-9a-f]{64}\))/.test(line);
32745
+ }
32746
+ function skipWhitespace(s, i) {
32747
+ while (i < s.length && /\s/.test(s[i])) i++;
32748
+ return i;
32749
+ }
32750
+ function locateBuzzContext(text) {
32751
+ if (!text.startsWith("<base>")) return CONTEXT_OPEN.test(text.slice(0, 12)) ? { rest: text, pos: 0 } : null;
32752
+ const baseEnd = text.indexOf("</base>");
32753
+ if (baseEnd < 0) return null;
32754
+ const rest = text.slice(baseEnd + "</base>".length);
32755
+ let pos = skipWhitespace(rest, 0);
32756
+ for (; ; ) {
32757
+ const name = PREAMBLE_BLOCKS.find((b) => rest.startsWith(`<${b}>`, pos));
32758
+ if (!name) break;
32759
+ const close = rest.indexOf(`</${name}>`, pos);
32760
+ if (close < 0) return null;
32761
+ pos = skipWhitespace(rest, close + name.length + 3);
32762
+ }
32763
+ return CONTEXT_OPEN.test(rest.slice(pos, pos + 12)) ? { rest, pos } : null;
32764
+ }
32765
+ function cleanBuzzEnvelope(message, opts = {}) {
32766
+ try {
32767
+ const text = message.trimStart();
32768
+ const hasBase = text.startsWith("<base>");
32769
+ if (!hasBase && !opts.baseless) return null;
32770
+ const loc = locateBuzzContext(text);
32771
+ if (!loc) return null;
32772
+ const { rest, pos } = loc;
32773
+ const bodyStart = rest.indexOf("\n", pos) + 1;
32774
+ const contextClose = rest.indexOf(CONTEXT_CLOSE, bodyStart - 1);
32775
+ if (contextClose < 0) return null;
32776
+ const contextBody = rest.slice(bodyStart, contextClose).replace(/\r+$/, "");
32777
+ if (!hasBase && !(BUZZ_SCOPE.test(contextBody) && BUZZ_CHANNEL.test(contextBody))) return null;
32778
+ const tail = rest.slice(contextClose + CONTEXT_CLOSE.length);
32779
+ let turnStart = 0;
32780
+ const history = HISTORY_OPEN.exec(tail);
32781
+ if (history) {
32782
+ const closeTag = `</${history[1]}>`;
32783
+ const close = tail.indexOf(closeTag, history[0].length);
32784
+ if (close < 0) return null;
32785
+ turnStart = close + closeTag.length;
32786
+ }
32787
+ if (TURN_IN_DROPPED.test(rest.slice(0, pos)) || TURN_IN_DROPPED.test(tail.slice(0, turnStart))) return null;
32788
+ const turn = tail.slice(turnStart);
32789
+ if (!TURN_OPEN.test(turn)) return null;
32790
+ const turnText = turn.split("\n").filter((line) => !isNostrMetaLine(line)).join("\n").trim();
32791
+ if (!turnText) return null;
32792
+ const cleaned = `<context>
32793
+ ${contextBody}
32794
+ </context>
32795
+
32796
+ ${turnText}`;
32797
+ if (cleaned.length >= message.length) return null;
32798
+ return cleaned;
32799
+ } catch {
32800
+ return null;
32801
+ }
32802
+ }
32803
+ function buzzQuotedMessages(message) {
32804
+ try {
32805
+ const loc = locateBuzzContext(message.trimStart());
32806
+ if (!loc) return null;
32807
+ const { rest, pos } = loc;
32808
+ const contextClose = rest.indexOf(CONTEXT_CLOSE, pos);
32809
+ if (contextClose < 0) return null;
32810
+ const tail = rest.slice(contextClose + CONTEXT_CLOSE.length);
32811
+ const history = HISTORY_OPEN.exec(tail);
32812
+ if (!history) return [];
32813
+ const closeTag = `</${history[1]}>`;
32814
+ const close = tail.indexOf(closeTag, history[0].length);
32815
+ if (close < 0) return null;
32816
+ const body = tail.slice(history[0].length, close).replace(/^\r?\n/, "").replace(/\r?\n$/, "");
32817
+ if (!body.trim()) return [];
32818
+ const out = [];
32819
+ for (const line of body.split(/\r?\n/)) {
32820
+ const e = QUOTE_ENTRY.exec(line);
32821
+ if (e && Number(e[1]) === out.length + 1) {
32822
+ out.push({ pubkey: e[2], time: e[3], content: line.slice(e[0].length) });
32823
+ } else if (out.length > 0) {
32824
+ out[out.length - 1].content += "\n" + line;
32825
+ } else {
32826
+ return null;
32827
+ }
32828
+ }
32829
+ return out;
32830
+ } catch {
32831
+ return null;
32832
+ }
32833
+ }
32834
+ function buzzEmbeddingText(message) {
32835
+ try {
32836
+ if (!message.startsWith("<context>\n")) return message;
32837
+ const close = message.indexOf(CONTEXT_CLOSE);
32838
+ if (close < 0) return message;
32839
+ const turn = message.slice(close + CONTEXT_CLOSE.length);
32840
+ if (!TURN_OPEN.test(turn)) return message;
32841
+ const text = turn.split("\n").filter((line) => !EVENT_HEADER.test(line) && !isNostrMetaLine(line)).map(
32842
+ (line) => (
32843
+ // `$` 없이: 끝까지 되짚는 역추적을 막는다 (긴 줄에서 제곱 시간)
32844
+ line.replace(/^From: (.*?) \(npub:.*/, "From: $1").replace(/^Channel: (\S+) \(#[0-9a-f-]+\)/, "Channel: $1")
32845
+ )
32846
+ ).join("\n").trim();
32847
+ return text || message;
32848
+ } catch {
32849
+ return message;
32850
+ }
32851
+ }
32852
+ function venueFromBuzzContext(message) {
32853
+ if (!CONTEXT_OPEN.test(message.slice(0, 12))) return null;
32854
+ const end = message.indexOf(CONTEXT_CLOSE);
32855
+ if (end < 0) return null;
32856
+ const lines = message.slice(0, end).split("\n").map((l) => l.replace(/\r$/, ""));
32857
+ const scope = lines.find((l) => l.startsWith("Scope: "))?.slice("Scope: ".length).trim();
32858
+ if (scope !== "thread" && scope !== "channel" && scope !== "dm") return null;
32859
+ if (scope === "dm") return "buzz:dm";
32860
+ const channel = lines.find((l) => l.startsWith("Channel: "))?.trimEnd();
32861
+ const m = channel ? /^Channel: (.+?) \(#[0-9a-f-]{36}\)$/.exec(channel) : null;
32862
+ return m ? `buzz:${m[1]}` : "buzz";
32863
+ }
32864
+ function looksLikeBuzzTurn(message) {
32865
+ const text = message.trimStart();
32866
+ if (text.startsWith("<base>")) return true;
32867
+ if (!CONTEXT_OPEN.test(text.slice(0, 12))) return false;
32868
+ const close = text.indexOf(CONTEXT_CLOSE);
32869
+ return close >= 0 && TURN_OPEN.test(text.slice(close + CONTEXT_CLOSE.length));
32870
+ }
32871
+ function venueFromBuzzMessage(message) {
32872
+ try {
32873
+ const loc = locateBuzzContext(message.trimStart());
32874
+ return loc ? venueFromBuzzContext(loc.rest.slice(loc.pos)) : null;
32875
+ } catch {
32876
+ return null;
32877
+ }
32878
+ }
32879
+ var PREAMBLE_BLOCKS, HISTORY_OPEN, TURN_OPEN, TURN_IN_DROPPED, CONTEXT_OPEN, BUZZ_SCOPE, BUZZ_CHANNEL, CONTEXT_CLOSE, QUOTE_ENTRY, EVENT_HEADER;
32880
+ var init_buzz_envelope = __esm({
32881
+ "src/auto_save/buzz_envelope.ts"() {
32882
+ "use strict";
32883
+ PREAMBLE_BLOCKS = ["agent-instructions", "core-memory"];
32884
+ HISTORY_OPEN = /^\s*<((?:thread|conversation)-context)(?:\s[^>]*)?>/;
32885
+ TURN_OPEN = /^\s*<(?:what-you-were-working-on|buzz-events?|new-message-arrived-while-you-were-working)[\s>]/;
32886
+ TURN_IN_DROPPED = /(?:^|>)[ \t]*<(?:what-you-were-working-on|buzz-events?|new-message-arrived-while-you-were-working)[\s>]/m;
32887
+ CONTEXT_OPEN = /^<context>\r?\n/;
32888
+ BUZZ_SCOPE = /^Scope: (?:thread|channel|dm)\r?\n/;
32889
+ BUZZ_CHANNEL = /(?:^|\n)Channel: .+ \(#[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\)\r?(?=\n|$)/;
32890
+ CONTEXT_CLOSE = "\n</context>";
32891
+ QUOTE_ENTRY = /^\[(\d+)\] .*? \(([0-9a-f]{64})\) \((\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:\d\d))\): ?/;
32892
+ EVENT_HEADER = /^(?:Event ID: [0-9a-f]{64}|Kind: \d+|Time: \d{4}-\d{2}-\d{2}T\S+|--- Event \d+ \(.*\) ---|Note: A new message arrived while you were working\..*)\s*$/;
32893
+ }
32894
+ });
32895
+
32896
+ // src/auto_save/venue.ts
32897
+ import fs2 from "fs";
32898
+ function classifySessionVenue(firstUserText, entrypoint) {
32899
+ if (buzzTurnVenue(firstUserText)) return "buzz";
32900
+ if (entrypoint && INTERACTIVE_ENTRYPOINTS.has(entrypoint)) return "terminal";
32901
+ if (/\bslack\b/i.test(firstUserText.slice(0, 500))) return "slack";
32902
+ if (entrypoint) return "auto";
32903
+ return null;
32904
+ }
32905
+ function buzzTurnVenue(message) {
32906
+ return venueFromBuzzMessage(message) ?? (looksLikeBuzzTurn(message) ? "buzz" : null);
32907
+ }
32908
+ function lastBuzzVenueInFile(jsonlPath, uptoBytes, userText) {
32909
+ const fd = fs2.openSync(jsonlPath, "r");
32910
+ try {
32911
+ let end = uptoBytes;
32912
+ let carry = Buffer.alloc(0);
32913
+ while (end > 0 && uptoBytes - end < TAIL_CAP) {
32914
+ const start = Math.max(0, end - TAIL_CHUNK);
32915
+ const chunk = Buffer.allocUnsafe(end - start);
32916
+ fs2.readSync(fd, chunk, 0, chunk.length, start);
32917
+ const buf = carry.length ? Buffer.concat([chunk, carry]) : chunk;
32918
+ const firstNl = start > 0 ? buf.indexOf(NEWLINE) : -1;
32919
+ carry = firstNl >= 0 ? buf.subarray(0, firstNl) : start > 0 ? buf : Buffer.alloc(0);
32920
+ const whole = firstNl >= 0 ? buf.subarray(firstNl + 1) : start > 0 ? Buffer.alloc(0) : buf;
32921
+ const lines = whole.toString("utf-8").split("\n");
32922
+ for (let i = lines.length - 1; i >= 0; i--) {
32923
+ const text = lines[i] ? userText(lines[i]) : null;
32924
+ const venue = text ? buzzTurnVenue(text) : null;
32925
+ if (venue) return venue;
32926
+ }
32927
+ end = start;
32928
+ }
32929
+ return null;
32930
+ } finally {
32931
+ fs2.closeSync(fd);
32932
+ }
32933
+ }
32934
+ var INTERACTIVE_ENTRYPOINTS, TAIL_CHUNK, TAIL_CAP, NEWLINE;
32935
+ var init_venue = __esm({
32936
+ "src/auto_save/venue.ts"() {
32937
+ "use strict";
32938
+ init_buzz_envelope();
32939
+ INTERACTIVE_ENTRYPOINTS = /* @__PURE__ */ new Set(["cli", "claude-vscode"]);
32940
+ TAIL_CHUNK = 4 * 1024 * 1024;
32941
+ TAIL_CAP = 64 * 1024 * 1024;
32942
+ NEWLINE = 10;
32943
+ }
32944
+ });
32945
+
32946
+ // src/hot_path.ts
32947
+ function missingOptionalColumn(err3, sql, cols) {
32948
+ const e = err3;
32949
+ if (e.code !== "42703") return void 0;
32950
+ const at = Number(e.position);
32951
+ const named = at > 0 ? /^[a-z_]+/.exec(sql.slice(at - 1))?.[0] : void 0;
32952
+ return cols.find(([c]) => named ? c === named : !!e.message?.includes(c))?.[0];
32953
+ }
32954
+ async function insertRawMemory(params) {
32955
+ const {
32956
+ user_id,
32957
+ agent_platform,
32958
+ agent_model,
32959
+ subagent = false,
32960
+ subagent_model = null,
32961
+ subagent_role = null,
32962
+ role,
32963
+ message,
32964
+ is_pinned = false,
32965
+ p_tag_id = null,
32966
+ d_tag = [],
32967
+ embedding = null,
32968
+ external_uuid = null,
32969
+ device_name = null
32970
+ } = params;
32971
+ const embeddingSql = embedding && embedding.length > 0 ? `[${embedding.join(",")}]` : null;
32972
+ const tagProcessed = p_tag_id !== null;
32973
+ const cleaned = role === "user" && !is_pinned && p_tag_id === null && embeddingSql === null ? cleanBuzzEnvelope(message) : null;
32974
+ const venue = params.venue ?? (role === "user" ? buzzTurnVenue(cleaned ?? message) : null);
32975
+ const raw = cleaned !== null ? message : params.raw_message ?? null;
32976
+ const optional2 = [];
32977
+ if (raw !== null) optional2.push(["raw_message", raw]);
32978
+ if (venue) optional2.push(["venue", venue]);
32979
+ const buildInsert = (cols2) => {
32980
+ const text = cleaned !== null && cols2.some(([c]) => c === "raw_message") ? cleaned : message;
32981
+ const extra = [
32982
+ ...cols2,
32983
+ ...params.created_at ? [["created_at", params.created_at]] : []
32984
+ ];
32985
+ const sql = `INSERT INTO memory (
32986
+ user_id, agent_platform, agent_model,
32987
+ subagent, subagent_model, subagent_role,
32988
+ role, message,
32989
+ p_tag_id, d_tag, embedding,
32990
+ is_pinned, tag_processed, external_uuid,
32991
+ device_name${extra.map(([c]) => `, ${c}`).join("")}
32992
+ ) VALUES (
32993
+ $1, $2, $3,
32994
+ $4, $5, $6,
32995
+ $7, $8,
32996
+ $9, $10::text[], $11::halfvec,
32997
+ $12, $13, $14,
32998
+ $15${extra.map((_, i) => `, $${16 + i}`).join("")}
32999
+ )
33000
+ ON CONFLICT (external_uuid) WHERE external_uuid IS NOT NULL
33001
+ DO NOTHING
33002
+ RETURNING id, created_at`;
33003
+ const values = [
33004
+ user_id,
33005
+ agent_platform,
33006
+ agent_model,
33007
+ subagent,
33008
+ subagent_model,
33009
+ subagent_role,
33010
+ role,
33011
+ text,
33012
+ p_tag_id,
33013
+ d_tag,
33014
+ embeddingSql,
33015
+ is_pinned,
33016
+ tagProcessed,
33017
+ external_uuid,
33018
+ device_name,
33019
+ ...extra.map(([, v]) => v)
33020
+ ];
33021
+ return { sql, values };
33022
+ };
33023
+ let cols = optional2.filter(([c]) => !missingColumns.has(c));
33024
+ let result;
33025
+ for (; ; ) {
33026
+ const { sql, values } = buildInsert(cols);
33027
+ try {
33028
+ result = await db.query(sql, values);
33029
+ break;
33030
+ } catch (err3) {
33031
+ const missing = missingOptionalColumn(err3, sql, cols);
33032
+ if (!missing) throw err3;
33033
+ missingColumns.add(missing);
33034
+ console.error(
33035
+ `\u26A0\uFE0F [HotPath] memory.${missing} \uCE78\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \`mcp-agents-memory migrate\` \uC2E4\uD589 \uD6C4 \uC774 \uD504\uB85C\uC138\uC2A4\uB97C \uC7AC\uC2DC\uC791\uD574\uC57C ${MISSING_COLUMN_EFFECT[missing]}`
33036
+ );
33037
+ cols = cols.filter(([c]) => c !== missing);
33038
+ }
33039
+ }
33040
+ if (result.rows.length === 0) {
33041
+ const existing = await db.query(
33042
+ `SELECT id, created_at FROM memory WHERE external_uuid = $1 LIMIT 1`,
33043
+ [external_uuid]
33044
+ );
33045
+ if (existing.rows.length > 0) {
33046
+ const row2 = existing.rows[0];
33047
+ return { id: Number(row2.id), created_at: row2.created_at, inserted: false };
33048
+ }
33049
+ throw new Error(`insertRawMemory: ON CONFLICT triggered but no existing row found (external_uuid=${external_uuid})`);
33050
+ }
33051
+ const row = result.rows[0];
33052
+ return { id: Number(row.id), created_at: row.created_at, inserted: true };
33053
+ }
33054
+ var missingColumns, MISSING_COLUMN_EFFECT;
33055
+ var init_hot_path = __esm({
33056
+ "src/hot_path.ts"() {
33057
+ "use strict";
33058
+ init_db();
33059
+ init_buzz_envelope();
33060
+ init_venue();
33061
+ missingColumns = /* @__PURE__ */ new Set();
33062
+ MISSING_COLUMN_EFFECT = {
33063
+ raw_message: "Buzz \uBD09\uD22C \uC815\uB9AC\uAC00 \uC2DC\uC791\uB429\uB2C8\uB2E4. \uADF8 \uC804\uAE4C\uC9C0\uB294 \uC6D0\uBB38 \uADF8\uB300\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4.",
33064
+ venue: "\uB300\uD654 \uC790\uB9AC(venue) \uAE30\uB85D\uC774 \uC2DC\uC791\uB429\uB2C8\uB2E4. \uADF8 \uC804\uAE4C\uC9C0\uB294 venue \uC5C6\uC774 \uC800\uC7A5\uD569\uB2C8\uB2E4."
33065
+ };
33066
+ }
33067
+ });
33068
+
33069
+ // src/users.ts
33070
+ async function getOrCreateUser(userName) {
33071
+ const cached2 = cache.get(userName);
33072
+ if (cached2 !== void 0) return cached2;
33073
+ const existing = await db.query(
33074
+ `SELECT user_id FROM users WHERE user_name = $1 LIMIT 1`,
33075
+ [userName]
33076
+ );
33077
+ if (existing.rows.length > 0) {
33078
+ const id2 = Number(existing.rows[0].user_id);
33079
+ cache.set(userName, id2);
33080
+ return id2;
33081
+ }
33082
+ const inserted = await db.query(
33083
+ `INSERT INTO users (user_name) VALUES ($1)
33084
+ ON CONFLICT (user_name) DO UPDATE SET updated_at = users.updated_at
33085
+ RETURNING user_id`,
33086
+ [userName]
33087
+ );
33088
+ const id = Number(inserted.rows[0].user_id);
33089
+ cache.set(userName, id);
33090
+ return id;
33091
+ }
33092
+ async function getDefaultUserId() {
33093
+ const name = process.env.USER_NAME ?? "hoon";
33094
+ return getOrCreateUser(name);
33095
+ }
33096
+ async function updateUserProfile(userId, field, value) {
33097
+ if (field === "core_profile") {
33098
+ await db.query(
33099
+ `UPDATE users SET core_profile = $1 WHERE user_id = $2`,
33100
+ [value, userId]
33101
+ );
33102
+ } else {
33103
+ await db.query(
33104
+ `UPDATE users SET sub_profile = $1 WHERE user_id = $2`,
33105
+ [value, userId]
33106
+ );
33107
+ }
33108
+ }
33109
+ async function getUserProfile(userId) {
33110
+ const r = await db.query(
33111
+ `SELECT core_profile, sub_profile FROM users WHERE user_id = $1`,
33112
+ [userId]
33113
+ );
33114
+ return r.rows[0] ?? { core_profile: null, sub_profile: null };
33115
+ }
33116
+ var cache;
33117
+ var init_users = __esm({
33118
+ "src/users.ts"() {
33119
+ "use strict";
33120
+ init_db();
33121
+ cache = /* @__PURE__ */ new Map();
33122
+ }
33123
+ });
33124
+
32742
33125
  // src/setup.ts
32743
33126
  var setup_exports = {};
32744
33127
  __export(setup_exports, {
@@ -32763,6 +33146,401 @@ var init_setup = __esm({
32763
33146
  }
32764
33147
  });
32765
33148
 
33149
+ // src/auto_save/buzz_ingest.ts
33150
+ var buzz_ingest_exports = {};
33151
+ __export(buzz_ingest_exports, {
33152
+ authorLabel: () => authorLabel,
33153
+ channelVenue: () => channelVenue,
33154
+ chatMessage: () => chatMessage,
33155
+ latestEdits: () => latestEdits,
33156
+ listChannels: () => listChannels,
33157
+ newChatEvents: () => newChatEvents,
33158
+ ownerPubkeys: () => ownerPubkeys,
33159
+ parseIngestArgs: () => parseIngestArgs,
33160
+ runBuzzIngest: () => runBuzzIngest
33161
+ });
33162
+ import { execFile } from "node:child_process";
33163
+ function cliPath() {
33164
+ return process.env.BUZZ_CLI || "buzz";
33165
+ }
33166
+ function childEnv() {
33167
+ return Object.fromEntries(Object.entries(process.env).filter(([k]) => CHILD_ENV.test(k)));
33168
+ }
33169
+ function runCli(args) {
33170
+ return new Promise((resolve, reject) => {
33171
+ execFile(
33172
+ cliPath(),
33173
+ args,
33174
+ { maxBuffer: 256 * 1024 * 1024, timeout: 12e4, env: childEnv() },
33175
+ (err3, stdout, stderr) => {
33176
+ if (err3) {
33177
+ reject(new Error(`buzz ${args.slice(0, 2).join(" ")} \uC2E4\uD328: ${String(stderr || err3.message).trim().slice(0, 500)}`));
33178
+ return;
33179
+ }
33180
+ try {
33181
+ resolve(JSON.parse(stdout));
33182
+ } catch {
33183
+ reject(new BuzzShapeError(`buzz ${args.slice(0, 2).join(" ")}: JSON\uC774 \uC544\uB2CC \uCD9C\uB825`));
33184
+ }
33185
+ }
33186
+ );
33187
+ });
33188
+ }
33189
+ function asEvent(v) {
33190
+ const e = v;
33191
+ const ok4 = e && typeof e === "object" && typeof e.id === "string" && HEX64.test(e.id) && typeof e.pubkey === "string" && HEX64.test(e.pubkey) && Number.isInteger(e.created_at) && Number.isInteger(e.kind) && typeof e.content === "string" && Array.isArray(e.tags) && e.tags.every((t) => Array.isArray(t) && t.every((x) => typeof x === "string"));
33192
+ if (!ok4) throw new BuzzShapeError(`\uC608\uC0C1\uACFC \uB2E4\uB978 \uC774\uBCA4\uD2B8 \uBAA8\uC591: ${JSON.stringify(v).slice(0, 200)}`);
33193
+ return e;
33194
+ }
33195
+ function asArray(v, what) {
33196
+ if (!Array.isArray(v)) throw new BuzzShapeError(`${what}: \uBC30\uC5F4\uC774 \uC544\uB2CC \uCD9C\uB825`);
33197
+ return v;
33198
+ }
33199
+ async function listChannels() {
33200
+ const parse3 = (v) => asArray(v, "channels list").map((c) => {
33201
+ const ch = c;
33202
+ if (typeof ch?.channel_id !== "string" || !UUID.test(ch.channel_id) || typeof ch.name !== "string") {
33203
+ throw new BuzzShapeError(`\uC608\uC0C1\uACFC \uB2E4\uB978 \uCC44\uB110 \uBAA8\uC591: ${JSON.stringify(c).slice(0, 200)}`);
33204
+ }
33205
+ return { id: ch.channel_id, name: ch.name };
33206
+ });
33207
+ const all = parse3(await runCli(["channels", "list", "--member"]));
33208
+ if (all.length === 0) throw new BuzzShapeError("channels list --member: CLI \uC2E0\uC6D0\uC774 \uBA64\uBC84\uC778 \uBC29\uC774 \uD558\uB098\uB3C4 \uC5C6\uC74C");
33209
+ const priv = new Set(parse3(await runCli(["channels", "list", "--member", "--visibility", "private"])).map((c) => c.id));
33210
+ return all.map((c) => ({ ...c, private: priv.has(c.id) }));
33211
+ }
33212
+ function channelVenue(ch) {
33213
+ const name = ch.name.trim();
33214
+ if (!name) return "buzz";
33215
+ return ch.private && name === "DM" ? "buzz:dm" : `buzz:${name}`;
33216
+ }
33217
+ async function getPage(channelId, kinds, before) {
33218
+ const args = ["messages", "get", "--channel", channelId, "--limit", String(PAGE), "--kinds", kinds.join(",")];
33219
+ if (before !== void 0) args.push("--before", String(before));
33220
+ return asArray(await runCli(args), "messages get").map(asEvent);
33221
+ }
33222
+ async function knownIds(eventIds) {
33223
+ if (eventIds.length === 0) return /* @__PURE__ */ new Set();
33224
+ const r = await db.query(
33225
+ `SELECT external_uuid FROM memory WHERE external_uuid = ANY($1::text[])`,
33226
+ [eventIds.map((id) => `buzz:${id}`)]
33227
+ );
33228
+ return new Set(r.rows.map((row) => String(row.external_uuid).slice("buzz:".length)));
33229
+ }
33230
+ async function pageBack(channelId, kinds, visit) {
33231
+ const seen = /* @__PURE__ */ new Set();
33232
+ let before;
33233
+ for (let pages = 0; ; pages++) {
33234
+ if (pages >= MAX_PAGES_PER_CHANNEL) {
33235
+ throw new BuzzShapeError(`${channelId}: ${MAX_PAGES_PER_CHANNEL}\uCABD\uC744 \uB118\uACA8\uB3C4 \uB05D\uB098\uC9C0 \uC54A\uC74C`);
33236
+ }
33237
+ const page = await getPage(channelId, kinds, before);
33238
+ if (page.length === 0) {
33239
+ if (before === void 0) return;
33240
+ throw new BuzzShapeError(`${channelId}: \uC774\uC5B4\uC9C0\uB294 \uCABD\uC774 \uBE44\uC5B4 \uC788\uC74C`);
33241
+ }
33242
+ const unseen = page.filter((e) => !seen.has(e.id));
33243
+ if (unseen.length === 0) {
33244
+ if (page.length >= PAGE) throw new BuzzShapeError(`${channelId}: \uCABD \uB118\uAE40\uC774 \uC55E\uC73C\uB85C \uAC00\uC9C0 \uC54A\uC74C`);
33245
+ return;
33246
+ }
33247
+ for (const e of unseen) seen.add(e.id);
33248
+ if (!await visit(page, unseen)) return;
33249
+ before = Math.min(...page.map((e) => e.created_at)) + 1;
33250
+ }
33251
+ }
33252
+ async function newChatEvents(channelId) {
33253
+ const fresh = /* @__PURE__ */ new Map();
33254
+ const nowSec = Math.floor(Date.now() / 1e3);
33255
+ let stopBelow;
33256
+ await pageBack(channelId, [KIND_CHAT], async (page, unseen) => {
33257
+ const chats = unseen.filter((e) => e.kind === KIND_CHAT);
33258
+ const known = await knownIds(chats.map((e) => e.id));
33259
+ for (const e of chats) if (!known.has(e.id)) fresh.set(e.id, e);
33260
+ if (known.size > 0 && stopBelow === void 0) {
33261
+ const newestKnown = Math.max(...chats.filter((e) => known.has(e.id)).map((e) => e.created_at));
33262
+ stopBelow = Math.min(newestKnown, nowSec) - LOOKBACK_SEC;
33263
+ }
33264
+ return !(stopBelow !== void 0 && Math.min(...page.map((e) => e.created_at)) < stopBelow);
33265
+ });
33266
+ return [...fresh.values()];
33267
+ }
33268
+ async function recentEvents(channelId, kinds, floor) {
33269
+ const out = [];
33270
+ await pageBack(channelId, kinds, async (page, unseen) => {
33271
+ out.push(...unseen.filter((e) => kinds.includes(e.kind)));
33272
+ return floor !== void 0 && Math.min(...page.map((e) => e.created_at)) >= floor;
33273
+ });
33274
+ return out;
33275
+ }
33276
+ async function displayNames(pubkeys) {
33277
+ const self2 = asArray(await runCli(["users", "get"]), "users get").map((u) => u?.pubkey).find((pk) => typeof pk === "string" && HEX64.test(pk));
33278
+ if (!self2) {
33279
+ throw new BuzzShapeError("users get: CLI \uC2E0\uC6D0\uC758 \uD504\uB85C\uD544\uC774 \uC548 \uBCF4\uC784 \u2014 \uC774\uB984 \uC751\uB2F5\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC74C (\uC2E0\uC6D0\uC5D0 \uD504\uB85C\uD544\uC744 \uB123\uC5B4 \uC8FC\uC138\uC694)");
33280
+ }
33281
+ const names = /* @__PURE__ */ new Map();
33282
+ for (let i = 0; i < pubkeys.length; i += 20) {
33283
+ const chunk = pubkeys.slice(i, i + 20);
33284
+ const ask = chunk.includes(self2) ? chunk : [...chunk, self2];
33285
+ const out = asArray(await runCli(["users", "get", ...ask.flatMap((pk) => ["--pubkey", pk])]), "users get");
33286
+ const profiles = out.map((u) => u);
33287
+ if (!profiles.some((p) => p?.pubkey === self2)) {
33288
+ throw new BuzzShapeError("users get: \uC790\uAE30 \uD504\uB85C\uD544\uC774 \uBE60\uC9C4 \uC751\uB2F5 \u2014 \uC774\uB984\uC744 \uBBFF\uC744 \uC218 \uC5C6\uC74C");
33289
+ }
33290
+ for (const p of profiles) {
33291
+ const label = [p?.display_name, p?.name].find((v) => typeof v === "string" && v.trim() !== "");
33292
+ if (typeof p?.pubkey === "string" && label) names.set(p.pubkey, label.trim());
33293
+ }
33294
+ }
33295
+ return names;
33296
+ }
33297
+ function ownerPubkeys() {
33298
+ const raw = process.env.BUZZ_INGEST_OWNER ?? "";
33299
+ const keys = raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
33300
+ if (keys.length === 0 || !keys.every((k) => HEX64.test(k))) {
33301
+ throw new Error(
33302
+ "BUZZ_INGEST_OWNER: \uC774 \uBA54\uBAA8\uB9AC \uC8FC\uC778\uC758 \uBC84\uC988 pubkey(64\uC790\uB9AC hex)\uB97C \uC27C\uD45C\uB85C \uC801\uC5B4\uC57C \uD569\uB2C8\uB2E4 \u2014 \uADF8 \uBC16\uC758 \uC791\uC131\uC790\uB294 assistant\uB85C \uC800\uC7A5\uB429\uB2C8\uB2E4"
33303
+ );
33304
+ }
33305
+ return new Set(keys);
33306
+ }
33307
+ function authorLabel(pubkey, names) {
33308
+ return names.get(pubkey) ?? pubkey.slice(0, 8);
33309
+ }
33310
+ function chatMessage(e, names) {
33311
+ return `[${authorLabel(e.pubkey, names)}] ${e.content}`;
33312
+ }
33313
+ function targetOf(e) {
33314
+ const t = e.tags.find((tag) => tag[0] === "e" && HEX64.test(tag[1] ?? ""));
33315
+ return t ? t[1] : null;
33316
+ }
33317
+ function latestEdits(events) {
33318
+ const latest = /* @__PURE__ */ new Map();
33319
+ for (const e of events) {
33320
+ if (e.kind !== KIND_EDIT) continue;
33321
+ const target = targetOf(e);
33322
+ if (!target) continue;
33323
+ const cur = latest.get(target);
33324
+ if (!cur || e.created_at > cur.created_at || e.created_at === cur.created_at && e.id > cur.id) {
33325
+ latest.set(target, e);
33326
+ }
33327
+ }
33328
+ return latest;
33329
+ }
33330
+ async function applyEdits(edits, dryRun) {
33331
+ if (edits.size === 0) return 0;
33332
+ const r = await db.query(
33333
+ `SELECT external_uuid, message, raw_message FROM memory
33334
+ WHERE external_uuid = ANY($1::text[]) AND agent_platform = 'buzz' AND raw_message IS NOT NULL`,
33335
+ [[...edits.keys()].map((id) => `buzz:${id}`)]
33336
+ );
33337
+ let n = 0;
33338
+ for (const row of r.rows) {
33339
+ const edit = edits.get(String(row.external_uuid).slice("buzz:".length));
33340
+ const raw = row.raw_message;
33341
+ const msg = row.message;
33342
+ if (raw === edit.content || !msg.endsWith(raw)) continue;
33343
+ n++;
33344
+ if (dryRun) continue;
33345
+ await db.query(
33346
+ `UPDATE memory
33347
+ SET message = $2, raw_message = $3,
33348
+ tag_processed = FALSE, embedding = NULL, cold_error = NULL
33349
+ WHERE external_uuid = $1`,
33350
+ [row.external_uuid, msg.slice(0, msg.length - raw.length) + edit.content, edit.content]
33351
+ );
33352
+ }
33353
+ return n;
33354
+ }
33355
+ async function hideDeleted(targets, dryRun) {
33356
+ if (targets.length === 0) return 0;
33357
+ const uuids = targets.map((id) => `buzz:${id}`);
33358
+ const sql = dryRun ? `SELECT count(*)::int AS n FROM memory
33359
+ WHERE external_uuid = ANY($1::text[]) AND agent_platform = 'buzz' AND is_active AND NOT is_pinned` : `WITH h AS (
33360
+ UPDATE memory SET is_active = FALSE, archived_at = NOW()
33361
+ WHERE external_uuid = ANY($1::text[]) AND agent_platform = 'buzz' AND is_active AND NOT is_pinned
33362
+ RETURNING 1)
33363
+ SELECT count(*)::int AS n FROM h`;
33364
+ const r = await db.query(sql, [uuids]);
33365
+ return Number(r.rows[0]?.n ?? 0);
33366
+ }
33367
+ async function recleanEnvelopes(userId, budget, dryRun) {
33368
+ const rows = (await db.query(
33369
+ `SELECT id, message, venue FROM memory
33370
+ WHERE user_id = $1 AND role = 'user' AND raw_message IS NULL AND message LIKE '<context>%'
33371
+ AND agent_platform <> 'buzz' AND is_active AND NOT is_pinned
33372
+ ORDER BY created_at, id`,
33373
+ [userId]
33374
+ )).rows;
33375
+ const cands = [];
33376
+ let stuck = 0;
33377
+ for (const r of rows) {
33378
+ const cleaned = cleanBuzzEnvelope(r.message, { baseless: true });
33379
+ const quotes = buzzQuotedMessages(r.message);
33380
+ if (cleaned === null && quotes !== null && quotes.length === 0) continue;
33381
+ const venue = [r.venue, venueFromBuzzMessage(r.message)].find((v) => typeof v === "string" && v.startsWith("buzz")) ?? null;
33382
+ const q = (quotes ?? []).map((x) => ({ ms: Date.parse(x.time), text: norm(x.content) }));
33383
+ if (cleaned === null || quotes === null || !venue || q.some((x) => !Number.isFinite(x.ms))) {
33384
+ if (venue || looksLikeBuzzTurn(r.message)) stuck++;
33385
+ continue;
33386
+ }
33387
+ cands.push({ id: Number(r.id), message: r.message, cleaned, venue, quotes: q });
33388
+ }
33389
+ const times = [...new Set(cands.flatMap((c) => c.quotes.map((x) => x.ms)))];
33390
+ const have = /* @__PURE__ */ new Set();
33391
+ if (times.length) {
33392
+ const r = await db.query(
33393
+ `SELECT venue, created_at, raw_message FROM memory
33394
+ WHERE user_id = $1 AND agent_platform = 'buzz' AND external_uuid LIKE 'buzz:%'
33395
+ AND raw_message IS NOT NULL AND created_at = ANY($2::timestamptz[])`,
33396
+ [userId, times.map((ms) => new Date(ms).toISOString())]
33397
+ );
33398
+ for (const row of r.rows) have.add(`${row.venue}\0${new Date(row.created_at).getTime()}\0${norm(row.raw_message)}`);
33399
+ }
33400
+ const ready = cands.filter((c) => c.quotes.every((x) => have.has(`${c.venue}\0${x.ms}\0${x.text}`)));
33401
+ stuck += cands.length - ready.length;
33402
+ let n = 0;
33403
+ for (const c of ready.slice(0, Math.max(0, budget))) {
33404
+ if (dryRun) {
33405
+ n++;
33406
+ continue;
33407
+ }
33408
+ const client2 = await db.getClient();
33409
+ try {
33410
+ await client2.query("BEGIN");
33411
+ await client2.query("SET LOCAL lock_timeout = '2s'");
33412
+ const r = await client2.query(
33413
+ `UPDATE memory
33414
+ SET message = $2, raw_message = $3, tag_processed = FALSE, embedding = NULL, cold_error = NULL
33415
+ WHERE id = $1 AND raw_message IS NULL AND message = $3`,
33416
+ [c.id, c.cleaned, c.message]
33417
+ );
33418
+ await client2.query("COMMIT");
33419
+ n += r.rowCount ?? 0;
33420
+ } catch (err3) {
33421
+ await client2.query("ROLLBACK").catch(() => {
33422
+ });
33423
+ if (err3.code !== "55P03") throw err3;
33424
+ } finally {
33425
+ client2.release();
33426
+ }
33427
+ }
33428
+ return { recleaned: n, ready: ready.length - n, stuck };
33429
+ }
33430
+ async function runBuzzIngest(opts = {}) {
33431
+ const dryRun = opts.dryRun ?? false;
33432
+ const max = opts.max ?? 20;
33433
+ const owner = ownerPubkeys();
33434
+ const channels = await listChannels();
33435
+ const pending = [];
33436
+ for (const ch of channels) {
33437
+ const venue = channelVenue(ch);
33438
+ for (const e of await newChatEvents(ch.id)) pending.push({ e, ch, venue });
33439
+ }
33440
+ pending.sort((a, b) => a.e.created_at - b.e.created_at || (a.e.id < b.e.id ? -1 : a.e.id > b.e.id ? 1 : 0));
33441
+ const deleteTargets = /* @__PURE__ */ new Set();
33442
+ const edits = [];
33443
+ let unsupported = 0;
33444
+ for (const ch of channels) {
33445
+ for (const d of await getPage(ch.id, DELETE_KINDS)) {
33446
+ const t = DELETE_KINDS.includes(d.kind) ? targetOf(d) : null;
33447
+ if (t) deleteTargets.add(t);
33448
+ }
33449
+ }
33450
+ const batch = pending.filter(({ e }) => !deleteTargets.has(e.id)).slice(0, Math.max(0, max));
33451
+ for (const ch of channels) {
33452
+ const mine = batch.filter((b) => b.ch.id === ch.id).map((b) => b.e.created_at);
33453
+ edits.push(...await recentEvents(ch.id, [KIND_EDIT], mine.length ? Math.min(...mine) - LOOKBACK_SEC : void 0));
33454
+ if (dryRun) unsupported += (await getPage(ch.id, UNSUPPORTED_KINDS)).filter((e) => UNSUPPORTED_KINDS.includes(e.kind)).length;
33455
+ }
33456
+ const pubkeys = [...new Set(batch.map(({ e }) => e.pubkey))];
33457
+ const names = pubkeys.length ? await displayNames(pubkeys) : /* @__PURE__ */ new Map();
33458
+ const report = {
33459
+ channels: channels.length,
33460
+ pending: pending.length,
33461
+ inserted: 0,
33462
+ edited: 0,
33463
+ hidden: 0,
33464
+ recleaned: 0,
33465
+ envelopesReady: 0,
33466
+ envelopesStuck: 0,
33467
+ byVenue: {},
33468
+ ...dryRun ? { unsupported } : {}
33469
+ };
33470
+ const userId = dryRun ? Number((await db.query(`SELECT user_id FROM users WHERE user_name = $1`, [process.env.USER_NAME ?? "hoon"])).rows[0]?.user_id ?? 0) : await getDefaultUserId();
33471
+ for (const { e, venue } of batch) {
33472
+ report.byVenue[venue] = (report.byVenue[venue] ?? 0) + 1;
33473
+ if (dryRun) {
33474
+ report.inserted++;
33475
+ continue;
33476
+ }
33477
+ let res;
33478
+ try {
33479
+ res = await insertRawMemory({
33480
+ user_id: userId,
33481
+ agent_platform: "buzz",
33482
+ agent_model: null,
33483
+ role: owner.has(e.pubkey) ? "user" : "assistant",
33484
+ message: chatMessage(e, names),
33485
+ raw_message: e.content,
33486
+ external_uuid: `buzz:${e.id}`,
33487
+ device_name: null,
33488
+ venue,
33489
+ created_at: new Date(e.created_at * 1e3)
33490
+ });
33491
+ } catch (err3) {
33492
+ throw new Error(`buzz:${e.id} \uC800\uC7A5 \uC2E4\uD328: ${err3 instanceof Error ? err3.message : String(err3)}`);
33493
+ }
33494
+ if (res.inserted) report.inserted++;
33495
+ }
33496
+ report.edited = await applyEdits(latestEdits(edits), dryRun);
33497
+ report.hidden = await hideDeleted([...deleteTargets], dryRun);
33498
+ if (!opts.noReclean && userId > 0) {
33499
+ const env = await recleanEnvelopes(userId, max - batch.length, dryRun);
33500
+ report.recleaned = env.recleaned;
33501
+ report.envelopesReady = env.ready;
33502
+ report.envelopesStuck = env.stuck;
33503
+ }
33504
+ return report;
33505
+ }
33506
+ function parseIngestArgs(argv) {
33507
+ const opts = {};
33508
+ for (let i = 0; i < argv.length; i++) {
33509
+ const a = argv[i];
33510
+ if (a === "--dry-run") opts.dryRun = true;
33511
+ else if (a === "--no-reclean") opts.noReclean = true;
33512
+ else if (a === "--max") {
33513
+ const v = argv[++i];
33514
+ if (v === void 0 || !/^\d+$/.test(v)) throw new Error("--max: 0 \uC774\uC0C1\uC758 \uC815\uC218");
33515
+ opts.max = Number(v);
33516
+ } else throw new Error(`buzz-ingest: \uBAA8\uB974\uB294 \uC635\uC158 ${a}`);
33517
+ }
33518
+ return opts;
33519
+ }
33520
+ var KIND_CHAT, KIND_EDIT, DELETE_KINDS, UNSUPPORTED_KINDS, PAGE, MAX_PAGES_PER_CHANNEL, LOOKBACK_SEC, HEX64, UUID, CHILD_ENV, BuzzShapeError, norm;
33521
+ var init_buzz_ingest = __esm({
33522
+ "src/auto_save/buzz_ingest.ts"() {
33523
+ "use strict";
33524
+ init_db();
33525
+ init_hot_path();
33526
+ init_buzz_envelope();
33527
+ init_users();
33528
+ KIND_CHAT = 9;
33529
+ KIND_EDIT = 40003;
33530
+ DELETE_KINDS = [5, 9005];
33531
+ UNSUPPORTED_KINDS = [40002, 40008, 45001, 45003];
33532
+ PAGE = 200;
33533
+ MAX_PAGES_PER_CHANNEL = 500;
33534
+ LOOKBACK_SEC = 48 * 3600;
33535
+ HEX64 = /^[0-9a-f]{64}$/;
33536
+ UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
33537
+ CHILD_ENV = /^(PATH|PATHEXT|HOME|USER|LANG|LC_[A-Z]+|TZ|TMPDIR|TEMP|TMP|SSL_CERT_(FILE|DIR)|NODE_EXTRA_CA_CERTS|(HTTPS?|ALL|NO)_PROXY|SYSTEMROOT|WINDIR|COMSPEC|APPDATA|LOCALAPPDATA|USERPROFILE|BUZZ_[A-Z0-9_]+)$/i;
33538
+ BuzzShapeError = class extends Error {
33539
+ };
33540
+ norm = (s) => s.replace(/\s+/g, " ").trim();
33541
+ }
33542
+ });
33543
+
32766
33544
  // src/migrations/runner.ts
32767
33545
  var runner_exports = {};
32768
33546
  __export(runner_exports, {
@@ -39873,13 +40651,13 @@ function _array(Class2, element, params) {
39873
40651
  });
39874
40652
  }
39875
40653
  function _custom(Class2, fn, _params) {
39876
- const norm = normalizeParams(_params);
39877
- norm.abort ?? (norm.abort = true);
40654
+ const norm2 = normalizeParams(_params);
40655
+ norm2.abort ?? (norm2.abort = true);
39878
40656
  const schema = new Class2({
39879
40657
  type: "custom",
39880
40658
  check: "custom",
39881
40659
  fn,
39882
- ...norm
40660
+ ...norm2
39883
40661
  });
39884
40662
  return schema;
39885
40663
  }
@@ -47030,325 +47808,10 @@ var StdioServerTransport = class {
47030
47808
 
47031
47809
  // src/tools/manage_knowledge.ts
47032
47810
  init_db();
47811
+ init_hot_path();
47812
+ init_users();
47033
47813
  import * as os3 from "node:os";
47034
47814
 
47035
- // src/hot_path.ts
47036
- init_db();
47037
-
47038
- // src/auto_save/buzz_envelope.ts
47039
- var PREAMBLE_BLOCKS = ["agent-instructions", "core-memory"];
47040
- var HISTORY_OPEN = /^\s*<((?:thread|conversation)-context)(?:\s[^>]*)?>/;
47041
- var TURN_OPEN = /^\s*<(?:what-you-were-working-on|buzz-events?|new-message-arrived-while-you-were-working)[\s>]/;
47042
- var TURN_IN_DROPPED = /(?:^|>)[ \t]*<(?:what-you-were-working-on|buzz-events?|new-message-arrived-while-you-were-working)[\s>]/m;
47043
- var CONTEXT_OPEN = /^<context>\r?\n/;
47044
- var CONTEXT_CLOSE = "\n</context>";
47045
- function isNostrMetaLine(line) {
47046
- return /^Tags: \[\[".*\]\]\s*$/.test(line) && /[0-9a-f]{64}/.test(line) || /^Parsed: (?:root=[0-9a-f]{64}|mentions=\[.*\([0-9a-f]{64}\))/.test(line);
47047
- }
47048
- function skipWhitespace(s, i) {
47049
- while (i < s.length && /\s/.test(s[i])) i++;
47050
- return i;
47051
- }
47052
- function locateBuzzContext(text) {
47053
- if (!text.startsWith("<base>")) return CONTEXT_OPEN.test(text.slice(0, 12)) ? { rest: text, pos: 0 } : null;
47054
- const baseEnd = text.indexOf("</base>");
47055
- if (baseEnd < 0) return null;
47056
- const rest = text.slice(baseEnd + "</base>".length);
47057
- let pos = skipWhitespace(rest, 0);
47058
- for (; ; ) {
47059
- const name = PREAMBLE_BLOCKS.find((b) => rest.startsWith(`<${b}>`, pos));
47060
- if (!name) break;
47061
- const close = rest.indexOf(`</${name}>`, pos);
47062
- if (close < 0) return null;
47063
- pos = skipWhitespace(rest, close + name.length + 3);
47064
- }
47065
- return CONTEXT_OPEN.test(rest.slice(pos, pos + 12)) ? { rest, pos } : null;
47066
- }
47067
- function cleanBuzzEnvelope(message) {
47068
- try {
47069
- const text = message.trimStart();
47070
- if (!text.startsWith("<base>")) return null;
47071
- const loc = locateBuzzContext(text);
47072
- if (!loc) return null;
47073
- const { rest, pos } = loc;
47074
- const bodyStart = rest.indexOf("\n", pos) + 1;
47075
- const contextClose = rest.indexOf(CONTEXT_CLOSE, bodyStart - 1);
47076
- if (contextClose < 0) return null;
47077
- const contextBody = rest.slice(bodyStart, contextClose).replace(/\r$/, "");
47078
- const tail = rest.slice(contextClose + CONTEXT_CLOSE.length);
47079
- let turnStart = 0;
47080
- const history = HISTORY_OPEN.exec(tail);
47081
- if (history) {
47082
- const closeTag = `</${history[1]}>`;
47083
- const close = tail.indexOf(closeTag, history[0].length);
47084
- if (close < 0) return null;
47085
- turnStart = close + closeTag.length;
47086
- }
47087
- if (TURN_IN_DROPPED.test(rest.slice(0, pos)) || TURN_IN_DROPPED.test(tail.slice(0, turnStart))) return null;
47088
- const turn = tail.slice(turnStart);
47089
- if (!TURN_OPEN.test(turn)) return null;
47090
- const turnText = turn.split("\n").filter((line) => !isNostrMetaLine(line)).join("\n").trim();
47091
- if (!turnText) return null;
47092
- const cleaned = `<context>
47093
- ${contextBody}
47094
- </context>
47095
-
47096
- ${turnText}`;
47097
- if (cleaned.length >= message.length) return null;
47098
- return cleaned;
47099
- } catch {
47100
- return null;
47101
- }
47102
- }
47103
- var EVENT_HEADER = /^(?:Event ID: [0-9a-f]{64}|Kind: \d+|Time: \d{4}-\d{2}-\d{2}T\S+|--- Event \d+ \(.*\) ---|Note: A new message arrived while you were working\..*)\s*$/;
47104
- function buzzEmbeddingText(message) {
47105
- try {
47106
- if (!message.startsWith("<context>\n")) return message;
47107
- const close = message.indexOf(CONTEXT_CLOSE);
47108
- if (close < 0) return message;
47109
- const turn = message.slice(close + CONTEXT_CLOSE.length);
47110
- if (!TURN_OPEN.test(turn)) return message;
47111
- const text = turn.split("\n").filter((line) => !EVENT_HEADER.test(line) && !isNostrMetaLine(line)).map(
47112
- (line) => (
47113
- // `$` 없이: 끝까지 되짚는 역추적을 막는다 (긴 줄에서 제곱 시간)
47114
- line.replace(/^From: (.*?) \(npub:.*/, "From: $1").replace(/^Channel: (\S+) \(#[0-9a-f-]+\)/, "Channel: $1")
47115
- )
47116
- ).join("\n").trim();
47117
- return text || message;
47118
- } catch {
47119
- return message;
47120
- }
47121
- }
47122
- function venueFromBuzzContext(message) {
47123
- if (!CONTEXT_OPEN.test(message.slice(0, 12))) return null;
47124
- const end = message.indexOf(CONTEXT_CLOSE);
47125
- if (end < 0) return null;
47126
- const lines = message.slice(0, end).split("\n").map((l) => l.replace(/\r$/, ""));
47127
- const scope = lines.find((l) => l.startsWith("Scope: "))?.slice("Scope: ".length).trim();
47128
- if (scope !== "thread" && scope !== "channel" && scope !== "dm") return null;
47129
- if (scope === "dm") return "buzz:dm";
47130
- const channel = lines.find((l) => l.startsWith("Channel: "))?.trimEnd();
47131
- const m = channel ? /^Channel: (.+?) \(#[0-9a-f-]{36}\)$/.exec(channel) : null;
47132
- return m ? `buzz:${m[1]}` : "buzz";
47133
- }
47134
- function looksLikeBuzzTurn(message) {
47135
- const text = message.trimStart();
47136
- if (text.startsWith("<base>")) return true;
47137
- if (!CONTEXT_OPEN.test(text.slice(0, 12))) return false;
47138
- const close = text.indexOf(CONTEXT_CLOSE);
47139
- return close >= 0 && TURN_OPEN.test(text.slice(close + CONTEXT_CLOSE.length));
47140
- }
47141
- function venueFromBuzzMessage(message) {
47142
- try {
47143
- const loc = locateBuzzContext(message.trimStart());
47144
- return loc ? venueFromBuzzContext(loc.rest.slice(loc.pos)) : null;
47145
- } catch {
47146
- return null;
47147
- }
47148
- }
47149
-
47150
- // src/auto_save/venue.ts
47151
- import fs2 from "fs";
47152
- var INTERACTIVE_ENTRYPOINTS = /* @__PURE__ */ new Set(["cli", "claude-vscode"]);
47153
- function classifySessionVenue(firstUserText, entrypoint) {
47154
- if (buzzTurnVenue(firstUserText)) return "buzz";
47155
- if (entrypoint && INTERACTIVE_ENTRYPOINTS.has(entrypoint)) return "terminal";
47156
- if (/\bslack\b/i.test(firstUserText.slice(0, 500))) return "slack";
47157
- if (entrypoint) return "auto";
47158
- return null;
47159
- }
47160
- function buzzTurnVenue(message) {
47161
- return venueFromBuzzMessage(message) ?? (looksLikeBuzzTurn(message) ? "buzz" : null);
47162
- }
47163
- var TAIL_CHUNK = 4 * 1024 * 1024;
47164
- var TAIL_CAP = 64 * 1024 * 1024;
47165
- var NEWLINE = 10;
47166
- function lastBuzzVenueInFile(jsonlPath, uptoBytes, userText) {
47167
- const fd = fs2.openSync(jsonlPath, "r");
47168
- try {
47169
- let end = uptoBytes;
47170
- let carry = Buffer.alloc(0);
47171
- while (end > 0 && uptoBytes - end < TAIL_CAP) {
47172
- const start = Math.max(0, end - TAIL_CHUNK);
47173
- const chunk = Buffer.allocUnsafe(end - start);
47174
- fs2.readSync(fd, chunk, 0, chunk.length, start);
47175
- const buf = carry.length ? Buffer.concat([chunk, carry]) : chunk;
47176
- const firstNl = start > 0 ? buf.indexOf(NEWLINE) : -1;
47177
- carry = firstNl >= 0 ? buf.subarray(0, firstNl) : start > 0 ? buf : Buffer.alloc(0);
47178
- const whole = firstNl >= 0 ? buf.subarray(firstNl + 1) : start > 0 ? Buffer.alloc(0) : buf;
47179
- const lines = whole.toString("utf-8").split("\n");
47180
- for (let i = lines.length - 1; i >= 0; i--) {
47181
- const text = lines[i] ? userText(lines[i]) : null;
47182
- const venue = text ? buzzTurnVenue(text) : null;
47183
- if (venue) return venue;
47184
- }
47185
- end = start;
47186
- }
47187
- return null;
47188
- } finally {
47189
- fs2.closeSync(fd);
47190
- }
47191
- }
47192
-
47193
- // src/hot_path.ts
47194
- var missingColumns = /* @__PURE__ */ new Set();
47195
- var MISSING_COLUMN_EFFECT = {
47196
- raw_message: "Buzz \uBD09\uD22C \uC815\uB9AC\uAC00 \uC2DC\uC791\uB429\uB2C8\uB2E4. \uADF8 \uC804\uAE4C\uC9C0\uB294 \uC6D0\uBB38 \uADF8\uB300\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4.",
47197
- venue: "\uB300\uD654 \uC790\uB9AC(venue) \uAE30\uB85D\uC774 \uC2DC\uC791\uB429\uB2C8\uB2E4. \uADF8 \uC804\uAE4C\uC9C0\uB294 venue \uC5C6\uC774 \uC800\uC7A5\uD569\uB2C8\uB2E4."
47198
- };
47199
- function missingOptionalColumn(err3, sql, cols) {
47200
- const e = err3;
47201
- if (e.code !== "42703") return void 0;
47202
- const at = Number(e.position);
47203
- const named = at > 0 ? /^[a-z_]+/.exec(sql.slice(at - 1))?.[0] : void 0;
47204
- return cols.find(([c]) => named ? c === named : !!e.message?.includes(c))?.[0];
47205
- }
47206
- async function insertRawMemory(params) {
47207
- const {
47208
- user_id,
47209
- agent_platform,
47210
- agent_model,
47211
- subagent = false,
47212
- subagent_model = null,
47213
- subagent_role = null,
47214
- role,
47215
- message,
47216
- is_pinned = false,
47217
- p_tag_id = null,
47218
- d_tag = [],
47219
- embedding = null,
47220
- external_uuid = null,
47221
- device_name = null
47222
- } = params;
47223
- const embeddingSql = embedding && embedding.length > 0 ? `[${embedding.join(",")}]` : null;
47224
- const tagProcessed = p_tag_id !== null;
47225
- const cleaned = role === "user" && !is_pinned && p_tag_id === null && embeddingSql === null ? cleanBuzzEnvelope(message) : null;
47226
- const venue = params.venue ?? (role === "user" ? buzzTurnVenue(cleaned ?? message) : null);
47227
- const optional2 = [];
47228
- if (cleaned !== null) optional2.push(["raw_message", message]);
47229
- if (venue) optional2.push(["venue", venue]);
47230
- const buildInsert = (cols2) => {
47231
- const text = cols2.some(([c]) => c === "raw_message") ? cleaned : message;
47232
- const sql = `INSERT INTO memory (
47233
- user_id, agent_platform, agent_model,
47234
- subagent, subagent_model, subagent_role,
47235
- role, message,
47236
- p_tag_id, d_tag, embedding,
47237
- is_pinned, tag_processed, external_uuid,
47238
- device_name${cols2.map(([c]) => `, ${c}`).join("")}
47239
- ) VALUES (
47240
- $1, $2, $3,
47241
- $4, $5, $6,
47242
- $7, $8,
47243
- $9, $10::text[], $11::halfvec,
47244
- $12, $13, $14,
47245
- $15${cols2.map((_, i) => `, $${16 + i}`).join("")}
47246
- )
47247
- ON CONFLICT (external_uuid) WHERE external_uuid IS NOT NULL
47248
- DO NOTHING
47249
- RETURNING id, created_at`;
47250
- const values = [
47251
- user_id,
47252
- agent_platform,
47253
- agent_model,
47254
- subagent,
47255
- subagent_model,
47256
- subagent_role,
47257
- role,
47258
- text,
47259
- p_tag_id,
47260
- d_tag,
47261
- embeddingSql,
47262
- is_pinned,
47263
- tagProcessed,
47264
- external_uuid,
47265
- device_name,
47266
- ...cols2.map(([, v]) => v)
47267
- ];
47268
- return { sql, values };
47269
- };
47270
- let cols = optional2.filter(([c]) => !missingColumns.has(c));
47271
- let result;
47272
- for (; ; ) {
47273
- const { sql, values } = buildInsert(cols);
47274
- try {
47275
- result = await db.query(sql, values);
47276
- break;
47277
- } catch (err3) {
47278
- const missing = missingOptionalColumn(err3, sql, cols);
47279
- if (!missing) throw err3;
47280
- missingColumns.add(missing);
47281
- console.error(
47282
- `\u26A0\uFE0F [HotPath] memory.${missing} \uCE78\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \`mcp-agents-memory migrate\` \uC2E4\uD589 \uD6C4 \uC774 \uD504\uB85C\uC138\uC2A4\uB97C \uC7AC\uC2DC\uC791\uD574\uC57C ${MISSING_COLUMN_EFFECT[missing]}`
47283
- );
47284
- cols = cols.filter(([c]) => c !== missing);
47285
- }
47286
- }
47287
- if (result.rows.length === 0) {
47288
- const existing = await db.query(
47289
- `SELECT id, created_at FROM memory WHERE external_uuid = $1 LIMIT 1`,
47290
- [external_uuid]
47291
- );
47292
- if (existing.rows.length > 0) {
47293
- const row2 = existing.rows[0];
47294
- return { id: Number(row2.id), created_at: row2.created_at, inserted: false };
47295
- }
47296
- throw new Error(`insertRawMemory: ON CONFLICT triggered but no existing row found (external_uuid=${external_uuid})`);
47297
- }
47298
- const row = result.rows[0];
47299
- return { id: Number(row.id), created_at: row.created_at, inserted: true };
47300
- }
47301
-
47302
- // src/users.ts
47303
- init_db();
47304
- var cache = /* @__PURE__ */ new Map();
47305
- async function getOrCreateUser(userName) {
47306
- const cached2 = cache.get(userName);
47307
- if (cached2 !== void 0) return cached2;
47308
- const existing = await db.query(
47309
- `SELECT user_id FROM users WHERE user_name = $1 LIMIT 1`,
47310
- [userName]
47311
- );
47312
- if (existing.rows.length > 0) {
47313
- const id2 = Number(existing.rows[0].user_id);
47314
- cache.set(userName, id2);
47315
- return id2;
47316
- }
47317
- const inserted = await db.query(
47318
- `INSERT INTO users (user_name) VALUES ($1)
47319
- ON CONFLICT (user_name) DO UPDATE SET updated_at = users.updated_at
47320
- RETURNING user_id`,
47321
- [userName]
47322
- );
47323
- const id = Number(inserted.rows[0].user_id);
47324
- cache.set(userName, id);
47325
- return id;
47326
- }
47327
- async function getDefaultUserId() {
47328
- const name = process.env.USER_NAME ?? "hoon";
47329
- return getOrCreateUser(name);
47330
- }
47331
- async function updateUserProfile(userId, field, value) {
47332
- if (field === "core_profile") {
47333
- await db.query(
47334
- `UPDATE users SET core_profile = $1 WHERE user_id = $2`,
47335
- [value, userId]
47336
- );
47337
- } else {
47338
- await db.query(
47339
- `UPDATE users SET sub_profile = $1 WHERE user_id = $2`,
47340
- [value, userId]
47341
- );
47342
- }
47343
- }
47344
- async function getUserProfile(userId) {
47345
- const r = await db.query(
47346
- `SELECT core_profile, sub_profile FROM users WHERE user_id = $1`,
47347
- [userId]
47348
- );
47349
- return r.rows[0] ?? { core_profile: null, sub_profile: null };
47350
- }
47351
-
47352
47815
  // ../mcp-agents-memory/node_modules/openai/internal/tslib.mjs
47353
47816
  function __classPrivateFieldSet(receiver, state, value, kind, f) {
47354
47817
  if (kind === "m")
@@ -56089,6 +56552,8 @@ async function tagMessage(input) {
56089
56552
  }
56090
56553
 
56091
56554
  // src/auto_save/opencode_capture.ts
56555
+ init_hot_path();
56556
+ init_users();
56092
56557
  import * as fs4 from "node:fs";
56093
56558
  import * as path4 from "node:path";
56094
56559
  import * as os2 from "node:os";
@@ -56651,6 +57116,7 @@ ${args.content}` : args.content;
56651
57116
 
56652
57117
  // src/tools/search_memory.ts
56653
57118
  init_db();
57119
+ init_users();
56654
57120
 
56655
57121
  // src/cold_path/embedder.ts
56656
57122
  var MAX_RETRIES = 3;
@@ -56997,9 +57463,11 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56997
57463
 
56998
57464
  // src/tools/manage_project_tags.ts
56999
57465
  init_db();
57466
+ init_users();
57000
57467
 
57001
57468
  // src/cold_path/project_alias_promoter.ts
57002
57469
  init_db();
57470
+ init_users();
57003
57471
  var QUALIFYING_RELATIONS = /* @__PURE__ */ new Set([
57004
57472
  "rename",
57005
57473
  "alias",
@@ -58315,7 +58783,9 @@ Actions:
58315
58783
  }
58316
58784
 
58317
58785
  // src/briefing.ts
58786
+ init_buzz_envelope();
58318
58787
  init_db();
58788
+ init_users();
58319
58789
  import * as os5 from "node:os";
58320
58790
  var RECENT_CURRENT_LIMIT = 8;
58321
58791
  var RECENT_OTHERS_LIMIT = 4;
@@ -58633,9 +59103,13 @@ agent_platform_filter=false.`,
58633
59103
  }
58634
59104
 
58635
59105
  // src/auto_save/save_message_tool.ts
59106
+ init_hot_path();
59107
+ init_users();
58636
59108
  import * as os10 from "node:os";
58637
59109
 
58638
59110
  // src/auto_save/gemini_capture.ts
59111
+ init_hot_path();
59112
+ init_users();
58639
59113
  import * as fs5 from "node:fs";
58640
59114
  import * as path5 from "node:path";
58641
59115
  import * as os6 from "node:os";
@@ -59020,6 +59494,8 @@ async function captureSessionEnd2() {
59020
59494
  }
59021
59495
 
59022
59496
  // src/auto_save/grok_capture.ts
59497
+ init_hot_path();
59498
+ init_users();
59023
59499
  import * as fs6 from "node:fs";
59024
59500
  import * as path6 from "node:path";
59025
59501
  import * as os7 from "node:os";
@@ -59303,6 +59779,8 @@ function isCaptureArmed3() {
59303
59779
  }
59304
59780
 
59305
59781
  // src/auto_save/antigravity_capture.ts
59782
+ init_hot_path();
59783
+ init_users();
59306
59784
  import * as fs7 from "node:fs";
59307
59785
  import * as path7 from "node:path";
59308
59786
  import * as os8 from "node:os";
@@ -59605,6 +60083,9 @@ function isCaptureArmed4() {
59605
60083
  }
59606
60084
 
59607
60085
  // src/auto_save/hermes_capture.ts
60086
+ init_hot_path();
60087
+ init_venue();
60088
+ init_users();
59608
60089
  import * as fs8 from "node:fs";
59609
60090
  import * as path8 from "node:path";
59610
60091
  import * as os9 from "node:os";
@@ -59975,6 +60456,7 @@ subagent \uCEE8\uD14D\uC2A4\uD2B8\uB77C\uBA74 subagent=true + subagent_model + s
59975
60456
 
59976
60457
  // src/tools/task_pin.ts
59977
60458
  init_db();
60459
+ init_users();
59978
60460
  import * as os11 from "node:os";
59979
60461
  var DEVICE_NAME8 = os11.hostname();
59980
60462
  var STATUSES = ["pending", "in_progress", "done", "failed"];
@@ -60125,9 +60607,11 @@ function registerTools(server) {
60125
60607
 
60126
60608
  // src/cold_path/worker.ts
60127
60609
  init_db();
60610
+ init_buzz_envelope();
60128
60611
 
60129
60612
  // src/cold_path/dtag_promoter.ts
60130
60613
  init_db();
60614
+ init_users();
60131
60615
  function envInt2(name, fallback) {
60132
60616
  const raw = process.env[name];
60133
60617
  if (!raw) return fallback;
@@ -60276,6 +60760,7 @@ async function runDtagPromotion() {
60276
60760
 
60277
60761
  // src/librarian.ts
60278
60762
  init_db();
60763
+ init_users();
60279
60764
 
60280
60765
  // src/context_budget.ts
60281
60766
  var DEFAULT_CTX = 8192;
@@ -60893,6 +61378,9 @@ async function drainColdPath(maxTicks = 12) {
60893
61378
  }
60894
61379
 
60895
61380
  // src/auto_save/jsonl_capture.ts
61381
+ init_hot_path();
61382
+ init_venue();
61383
+ init_users();
60896
61384
  import * as fs9 from "node:fs";
60897
61385
  import * as path9 from "node:path";
60898
61386
  import * as os12 from "node:os";
@@ -61260,6 +61748,8 @@ async function captureSessionEnd6() {
61260
61748
  }
61261
61749
 
61262
61750
  // src/auto_save/codex_capture.ts
61751
+ init_hot_path();
61752
+ init_users();
61263
61753
  import * as fs10 from "node:fs";
61264
61754
  import * as path10 from "node:path";
61265
61755
  import * as os13 from "node:os";
@@ -61756,6 +62246,14 @@ Usage:
61756
62246
  mcp-agents-memory coldpath Run ONLY the cold-path worker as a standalone always-on daemon (no MCP server). For the processing/GPU machine via systemd.
61757
62247
  mcp-agents-memory setup Interactive setup \u2014 write config to ~/.config/mcp-agents-memory/.env and run migrations.
61758
62248
  mcp-agents-memory migrate Apply any pending DB migrations against the configured database.
62249
+ mcp-agents-memory buzz-ingest [--dry-run] [--max N] [--no-reclean]
62250
+ (Buzz only) Copy chat messages from the Buzz rooms the CLI identity is a member of
62251
+ into memory, one row per message, via the official \`buzz\` CLI. Oldest first, at most
62252
+ N per run (default 20). With the remaining budget it also strips the quoted history
62253
+ from captured Buzz turns once every quoted message exists as a row (original kept in
62254
+ raw_message; --no-reclean skips this). Needs BUZZ_RELAY_URL, BUZZ_PRIVATE_KEY and BUZZ_INGEST_OWNER
62255
+ (the memory owner's pubkeys, comma-separated) in the environment of this command only \u2014
62256
+ not in the shared .env; optional BUZZ_CLI.
61759
62257
  mcp-agents-memory help Show this message.
61760
62258
 
61761
62259
  Configuration is loaded from (first hit wins):
@@ -61848,6 +62346,15 @@ async function cli() {
61848
62346
  await runSetupWizard2();
61849
62347
  process.exit(0);
61850
62348
  }
62349
+ if (cmd === "buzz-ingest") {
62350
+ const { runBuzzIngest: runBuzzIngest2, parseIngestArgs: parseIngestArgs2 } = await Promise.resolve().then(() => (init_buzz_ingest(), buzz_ingest_exports));
62351
+ const opts = parseIngestArgs2(process.argv.slice(3));
62352
+ await db.connect();
62353
+ const report = await runBuzzIngest2(opts);
62354
+ console.log(JSON.stringify({ dryRun: opts.dryRun ?? false, ...report }));
62355
+ await db.close();
62356
+ process.exit(0);
62357
+ }
61851
62358
  if (cmd === "migrate") {
61852
62359
  const { runAllMigrations: runAllMigrations2 } = await Promise.resolve().then(() => (init_runner(), runner_exports));
61853
62360
  await runAllMigrations2();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-agents-memory",
3
- "version": "0.9.19",
3
+ "version": "0.9.21",
4
4
  "description": "Long-term memory for AI agents with provenance tracking",
5
5
  "main": "build/index.js",
6
6
  "type": "module",