mcp-agents-memory 0.9.18 → 0.9.20

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/build/index.js CHANGED
@@ -1675,15 +1675,15 @@ var require_pg_connection_string = __commonJS({
1675
1675
  if (config2.sslcert || config2.sslkey || config2.sslrootcert || config2.sslmode) {
1676
1676
  config2.ssl = {};
1677
1677
  }
1678
- const fs13 = config2.sslcert || config2.sslkey || config2.sslrootcert ? __require("fs") : null;
1678
+ const fs14 = config2.sslcert || config2.sslkey || config2.sslrootcert ? __require("fs") : null;
1679
1679
  if (config2.sslcert) {
1680
- config2.ssl.cert = fs13.readFileSync(config2.sslcert).toString();
1680
+ config2.ssl.cert = fs14.readFileSync(config2.sslcert).toString();
1681
1681
  }
1682
1682
  if (config2.sslkey) {
1683
- config2.ssl.key = fs13.readFileSync(config2.sslkey).toString();
1683
+ config2.ssl.key = fs14.readFileSync(config2.sslkey).toString();
1684
1684
  }
1685
1685
  if (config2.sslrootcert) {
1686
- config2.ssl.ca = fs13.readFileSync(config2.sslrootcert).toString();
1686
+ config2.ssl.ca = fs14.readFileSync(config2.sslrootcert).toString();
1687
1687
  }
1688
1688
  if (options.useLibpqCompat && config2.uselibpqcompat) {
1689
1689
  throw new Error("Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.");
@@ -3620,15 +3620,15 @@ var require_lib = __commonJS({
3620
3620
  "../mcp-agents-memory/node_modules/pgpass/lib/index.js"(exports, module) {
3621
3621
  "use strict";
3622
3622
  var path13 = __require("path");
3623
- var fs13 = __require("fs");
3623
+ var fs14 = __require("fs");
3624
3624
  var helper = require_helper();
3625
3625
  module.exports = function(connInfo, cb) {
3626
3626
  var file = helper.getFileName();
3627
- fs13.stat(file, function(err3, stat) {
3627
+ fs14.stat(file, function(err3, stat) {
3628
3628
  if (err3 || !helper.usePgPass(stat, file)) {
3629
3629
  return cb(void 0);
3630
3630
  }
3631
- var st = fs13.createReadStream(file);
3631
+ var st = fs14.createReadStream(file);
3632
3632
  helper.getPassword(connInfo, st, cb);
3633
3633
  });
3634
3634
  };
@@ -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;
@@ -18203,8 +18203,8 @@ var require_SFTP = __commonJS({
18203
18203
  "../mcp-agents-memory/node_modules/ssh2/lib/protocol/SFTP.js"(exports, module) {
18204
18204
  "use strict";
18205
18205
  var EventEmitter = __require("events");
18206
- var fs13 = __require("fs");
18207
- var { constants } = fs13;
18206
+ var fs14 = __require("fs");
18207
+ var { constants } = fs14;
18208
18208
  var {
18209
18209
  Readable: ReadableStream2,
18210
18210
  Writable: WritableStream
@@ -18633,12 +18633,12 @@ var require_SFTP = __commonJS({
18633
18633
  fastGet(remotePath, localPath, opts, cb) {
18634
18634
  if (this.server)
18635
18635
  throw new Error("Client-only method called in server mode");
18636
- fastXfer(this, fs13, remotePath, localPath, opts, cb);
18636
+ fastXfer(this, fs14, remotePath, localPath, opts, cb);
18637
18637
  }
18638
18638
  fastPut(localPath, remotePath, opts, cb) {
18639
18639
  if (this.server)
18640
18640
  throw new Error("Client-only method called in server mode");
18641
- fastXfer(fs13, this, localPath, remotePath, opts, cb);
18641
+ fastXfer(fs14, this, localPath, remotePath, opts, cb);
18642
18642
  }
18643
18643
  readFile(path13, options, callback_) {
18644
18644
  if (this.server)
@@ -19899,13 +19899,13 @@ var require_SFTP = __commonJS({
19899
19899
  if (--left === 0)
19900
19900
  cb(err3);
19901
19901
  };
19902
- if (srcHandle && (src === fs13 || src.outgoing.state === "open"))
19902
+ if (srcHandle && (src === fs14 || src.outgoing.state === "open"))
19903
19903
  ++left;
19904
- if (dstHandle && (dst === fs13 || dst.outgoing.state === "open"))
19904
+ if (dstHandle && (dst === fs14 || dst.outgoing.state === "open"))
19905
19905
  ++left;
19906
- if (srcHandle && (src === fs13 || src.outgoing.state === "open"))
19906
+ if (srcHandle && (src === fs14 || src.outgoing.state === "open"))
19907
19907
  src.close(srcHandle, cbfinal);
19908
- if (dstHandle && (dst === fs13 || dst.outgoing.state === "open"))
19908
+ if (dstHandle && (dst === fs14 || dst.outgoing.state === "open"))
19909
19909
  dst.close(dstHandle, cbfinal);
19910
19910
  } else {
19911
19911
  cb(err3);
@@ -19921,7 +19921,7 @@ var require_SFTP = __commonJS({
19921
19921
  tryStat(null, { size: fileSize });
19922
19922
  function tryStat(err4, attrs) {
19923
19923
  if (err4) {
19924
- if (src !== fs13) {
19924
+ if (src !== fs14) {
19925
19925
  src.stat(srcPath, (err_, attrs_) => {
19926
19926
  if (err_)
19927
19927
  return onerror(err4);
@@ -25485,7 +25485,7 @@ var require_package2 = __commonJS({
25485
25485
  // ../mcp-agents-memory/node_modules/dotenv/lib/main.js
25486
25486
  var require_main = __commonJS({
25487
25487
  "../mcp-agents-memory/node_modules/dotenv/lib/main.js"(exports, module) {
25488
- var fs13 = __require("fs");
25488
+ var fs14 = __require("fs");
25489
25489
  var path13 = __require("path");
25490
25490
  var os14 = __require("os");
25491
25491
  var crypto3 = __require("crypto");
@@ -25594,7 +25594,7 @@ var require_main = __commonJS({
25594
25594
  if (options && options.path && options.path.length > 0) {
25595
25595
  if (Array.isArray(options.path)) {
25596
25596
  for (const filepath of options.path) {
25597
- if (fs13.existsSync(filepath)) {
25597
+ if (fs14.existsSync(filepath)) {
25598
25598
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
25599
25599
  }
25600
25600
  }
@@ -25604,7 +25604,7 @@ var require_main = __commonJS({
25604
25604
  } else {
25605
25605
  possibleVaultPath = path13.resolve(process.cwd(), ".env.vault");
25606
25606
  }
25607
- if (fs13.existsSync(possibleVaultPath)) {
25607
+ if (fs14.existsSync(possibleVaultPath)) {
25608
25608
  return possibleVaultPath;
25609
25609
  }
25610
25610
  return null;
@@ -25653,7 +25653,7 @@ var require_main = __commonJS({
25653
25653
  const parsedAll = {};
25654
25654
  for (const path14 of optionPaths) {
25655
25655
  try {
25656
- const parsed = DotenvModule.parse(fs13.readFileSync(path14, { encoding }));
25656
+ const parsed = DotenvModule.parse(fs14.readFileSync(path14, { encoding }));
25657
25657
  DotenvModule.populate(parsedAll, parsed, options);
25658
25658
  } catch (e) {
25659
25659
  if (debug) {
@@ -32726,12 +32726,12 @@ var require_dist2 = __commonJS({
32726
32726
  throw new Error(`Unknown format "${name}"`);
32727
32727
  return f;
32728
32728
  };
32729
- function addFormats(ajv, list, fs13, exportName) {
32729
+ function addFormats(ajv, list, fs14, exportName) {
32730
32730
  var _a3;
32731
32731
  var _b;
32732
32732
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
32733
32733
  for (const f of list)
32734
- ajv.addFormat(f, fs13[f]);
32734
+ ajv.addFormat(f, fs14[f]);
32735
32735
  }
32736
32736
  module.exports = exports = formatsPlugin;
32737
32737
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -32739,6 +32739,353 @@ 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) {
32766
+ try {
32767
+ const text = message.trimStart();
32768
+ if (!text.startsWith("<base>")) return null;
32769
+ const loc = locateBuzzContext(text);
32770
+ if (!loc) return null;
32771
+ const { rest, pos } = loc;
32772
+ const bodyStart = rest.indexOf("\n", pos) + 1;
32773
+ const contextClose = rest.indexOf(CONTEXT_CLOSE, bodyStart - 1);
32774
+ if (contextClose < 0) return null;
32775
+ const contextBody = rest.slice(bodyStart, contextClose).replace(/\r$/, "");
32776
+ const tail = rest.slice(contextClose + CONTEXT_CLOSE.length);
32777
+ let turnStart = 0;
32778
+ const history = HISTORY_OPEN.exec(tail);
32779
+ if (history) {
32780
+ const closeTag = `</${history[1]}>`;
32781
+ const close = tail.indexOf(closeTag, history[0].length);
32782
+ if (close < 0) return null;
32783
+ turnStart = close + closeTag.length;
32784
+ }
32785
+ if (TURN_IN_DROPPED.test(rest.slice(0, pos)) || TURN_IN_DROPPED.test(tail.slice(0, turnStart))) return null;
32786
+ const turn = tail.slice(turnStart);
32787
+ if (!TURN_OPEN.test(turn)) return null;
32788
+ const turnText = turn.split("\n").filter((line) => !isNostrMetaLine(line)).join("\n").trim();
32789
+ if (!turnText) return null;
32790
+ const cleaned = `<context>
32791
+ ${contextBody}
32792
+ </context>
32793
+
32794
+ ${turnText}`;
32795
+ if (cleaned.length >= message.length) return null;
32796
+ return cleaned;
32797
+ } catch {
32798
+ return null;
32799
+ }
32800
+ }
32801
+ function buzzEmbeddingText(message) {
32802
+ try {
32803
+ if (!message.startsWith("<context>\n")) return message;
32804
+ const close = message.indexOf(CONTEXT_CLOSE);
32805
+ if (close < 0) return message;
32806
+ const turn = message.slice(close + CONTEXT_CLOSE.length);
32807
+ if (!TURN_OPEN.test(turn)) return message;
32808
+ const text = turn.split("\n").filter((line) => !EVENT_HEADER.test(line) && !isNostrMetaLine(line)).map(
32809
+ (line) => (
32810
+ // `$` 없이: 끝까지 되짚는 역추적을 막는다 (긴 줄에서 제곱 시간)
32811
+ line.replace(/^From: (.*?) \(npub:.*/, "From: $1").replace(/^Channel: (\S+) \(#[0-9a-f-]+\)/, "Channel: $1")
32812
+ )
32813
+ ).join("\n").trim();
32814
+ return text || message;
32815
+ } catch {
32816
+ return message;
32817
+ }
32818
+ }
32819
+ function venueFromBuzzContext(message) {
32820
+ if (!CONTEXT_OPEN.test(message.slice(0, 12))) return null;
32821
+ const end = message.indexOf(CONTEXT_CLOSE);
32822
+ if (end < 0) return null;
32823
+ const lines = message.slice(0, end).split("\n").map((l) => l.replace(/\r$/, ""));
32824
+ const scope = lines.find((l) => l.startsWith("Scope: "))?.slice("Scope: ".length).trim();
32825
+ if (scope !== "thread" && scope !== "channel" && scope !== "dm") return null;
32826
+ if (scope === "dm") return "buzz:dm";
32827
+ const channel = lines.find((l) => l.startsWith("Channel: "))?.trimEnd();
32828
+ const m = channel ? /^Channel: (.+?) \(#[0-9a-f-]{36}\)$/.exec(channel) : null;
32829
+ return m ? `buzz:${m[1]}` : "buzz";
32830
+ }
32831
+ function looksLikeBuzzTurn(message) {
32832
+ const text = message.trimStart();
32833
+ if (text.startsWith("<base>")) return true;
32834
+ if (!CONTEXT_OPEN.test(text.slice(0, 12))) return false;
32835
+ const close = text.indexOf(CONTEXT_CLOSE);
32836
+ return close >= 0 && TURN_OPEN.test(text.slice(close + CONTEXT_CLOSE.length));
32837
+ }
32838
+ function venueFromBuzzMessage(message) {
32839
+ try {
32840
+ const loc = locateBuzzContext(message.trimStart());
32841
+ return loc ? venueFromBuzzContext(loc.rest.slice(loc.pos)) : null;
32842
+ } catch {
32843
+ return null;
32844
+ }
32845
+ }
32846
+ var PREAMBLE_BLOCKS, HISTORY_OPEN, TURN_OPEN, TURN_IN_DROPPED, CONTEXT_OPEN, CONTEXT_CLOSE, EVENT_HEADER;
32847
+ var init_buzz_envelope = __esm({
32848
+ "src/auto_save/buzz_envelope.ts"() {
32849
+ "use strict";
32850
+ PREAMBLE_BLOCKS = ["agent-instructions", "core-memory"];
32851
+ HISTORY_OPEN = /^\s*<((?:thread|conversation)-context)(?:\s[^>]*)?>/;
32852
+ TURN_OPEN = /^\s*<(?:what-you-were-working-on|buzz-events?|new-message-arrived-while-you-were-working)[\s>]/;
32853
+ TURN_IN_DROPPED = /(?:^|>)[ \t]*<(?:what-you-were-working-on|buzz-events?|new-message-arrived-while-you-were-working)[\s>]/m;
32854
+ CONTEXT_OPEN = /^<context>\r?\n/;
32855
+ CONTEXT_CLOSE = "\n</context>";
32856
+ 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*$/;
32857
+ }
32858
+ });
32859
+
32860
+ // src/auto_save/venue.ts
32861
+ import fs2 from "fs";
32862
+ function classifySessionVenue(firstUserText, entrypoint) {
32863
+ if (buzzTurnVenue(firstUserText)) return "buzz";
32864
+ if (entrypoint && INTERACTIVE_ENTRYPOINTS.has(entrypoint)) return "terminal";
32865
+ if (/\bslack\b/i.test(firstUserText.slice(0, 500))) return "slack";
32866
+ if (entrypoint) return "auto";
32867
+ return null;
32868
+ }
32869
+ function buzzTurnVenue(message) {
32870
+ return venueFromBuzzMessage(message) ?? (looksLikeBuzzTurn(message) ? "buzz" : null);
32871
+ }
32872
+ function lastBuzzVenueInFile(jsonlPath, uptoBytes, userText) {
32873
+ const fd = fs2.openSync(jsonlPath, "r");
32874
+ try {
32875
+ let end = uptoBytes;
32876
+ let carry = Buffer.alloc(0);
32877
+ while (end > 0 && uptoBytes - end < TAIL_CAP) {
32878
+ const start = Math.max(0, end - TAIL_CHUNK);
32879
+ const chunk = Buffer.allocUnsafe(end - start);
32880
+ fs2.readSync(fd, chunk, 0, chunk.length, start);
32881
+ const buf = carry.length ? Buffer.concat([chunk, carry]) : chunk;
32882
+ const firstNl = start > 0 ? buf.indexOf(NEWLINE) : -1;
32883
+ carry = firstNl >= 0 ? buf.subarray(0, firstNl) : start > 0 ? buf : Buffer.alloc(0);
32884
+ const whole = firstNl >= 0 ? buf.subarray(firstNl + 1) : start > 0 ? Buffer.alloc(0) : buf;
32885
+ const lines = whole.toString("utf-8").split("\n");
32886
+ for (let i = lines.length - 1; i >= 0; i--) {
32887
+ const text = lines[i] ? userText(lines[i]) : null;
32888
+ const venue = text ? buzzTurnVenue(text) : null;
32889
+ if (venue) return venue;
32890
+ }
32891
+ end = start;
32892
+ }
32893
+ return null;
32894
+ } finally {
32895
+ fs2.closeSync(fd);
32896
+ }
32897
+ }
32898
+ var INTERACTIVE_ENTRYPOINTS, TAIL_CHUNK, TAIL_CAP, NEWLINE;
32899
+ var init_venue = __esm({
32900
+ "src/auto_save/venue.ts"() {
32901
+ "use strict";
32902
+ init_buzz_envelope();
32903
+ INTERACTIVE_ENTRYPOINTS = /* @__PURE__ */ new Set(["cli", "claude-vscode"]);
32904
+ TAIL_CHUNK = 4 * 1024 * 1024;
32905
+ TAIL_CAP = 64 * 1024 * 1024;
32906
+ NEWLINE = 10;
32907
+ }
32908
+ });
32909
+
32910
+ // src/hot_path.ts
32911
+ function missingOptionalColumn(err3, sql, cols) {
32912
+ const e = err3;
32913
+ if (e.code !== "42703") return void 0;
32914
+ const at = Number(e.position);
32915
+ const named = at > 0 ? /^[a-z_]+/.exec(sql.slice(at - 1))?.[0] : void 0;
32916
+ return cols.find(([c]) => named ? c === named : !!e.message?.includes(c))?.[0];
32917
+ }
32918
+ async function insertRawMemory(params) {
32919
+ const {
32920
+ user_id,
32921
+ agent_platform,
32922
+ agent_model,
32923
+ subagent = false,
32924
+ subagent_model = null,
32925
+ subagent_role = null,
32926
+ role,
32927
+ message,
32928
+ is_pinned = false,
32929
+ p_tag_id = null,
32930
+ d_tag = [],
32931
+ embedding = null,
32932
+ external_uuid = null,
32933
+ device_name = null
32934
+ } = params;
32935
+ const embeddingSql = embedding && embedding.length > 0 ? `[${embedding.join(",")}]` : null;
32936
+ const tagProcessed = p_tag_id !== null;
32937
+ const cleaned = role === "user" && !is_pinned && p_tag_id === null && embeddingSql === null ? cleanBuzzEnvelope(message) : null;
32938
+ const venue = params.venue ?? (role === "user" ? buzzTurnVenue(cleaned ?? message) : null);
32939
+ const raw = cleaned !== null ? message : params.raw_message ?? null;
32940
+ const optional2 = [];
32941
+ if (raw !== null) optional2.push(["raw_message", raw]);
32942
+ if (venue) optional2.push(["venue", venue]);
32943
+ const buildInsert = (cols2) => {
32944
+ const text = cleaned !== null && cols2.some(([c]) => c === "raw_message") ? cleaned : message;
32945
+ const extra = [
32946
+ ...cols2,
32947
+ ...params.created_at ? [["created_at", params.created_at]] : []
32948
+ ];
32949
+ const sql = `INSERT INTO memory (
32950
+ user_id, agent_platform, agent_model,
32951
+ subagent, subagent_model, subagent_role,
32952
+ role, message,
32953
+ p_tag_id, d_tag, embedding,
32954
+ is_pinned, tag_processed, external_uuid,
32955
+ device_name${extra.map(([c]) => `, ${c}`).join("")}
32956
+ ) VALUES (
32957
+ $1, $2, $3,
32958
+ $4, $5, $6,
32959
+ $7, $8,
32960
+ $9, $10::text[], $11::halfvec,
32961
+ $12, $13, $14,
32962
+ $15${extra.map((_, i) => `, $${16 + i}`).join("")}
32963
+ )
32964
+ ON CONFLICT (external_uuid) WHERE external_uuid IS NOT NULL
32965
+ DO NOTHING
32966
+ RETURNING id, created_at`;
32967
+ const values = [
32968
+ user_id,
32969
+ agent_platform,
32970
+ agent_model,
32971
+ subagent,
32972
+ subagent_model,
32973
+ subagent_role,
32974
+ role,
32975
+ text,
32976
+ p_tag_id,
32977
+ d_tag,
32978
+ embeddingSql,
32979
+ is_pinned,
32980
+ tagProcessed,
32981
+ external_uuid,
32982
+ device_name,
32983
+ ...extra.map(([, v]) => v)
32984
+ ];
32985
+ return { sql, values };
32986
+ };
32987
+ let cols = optional2.filter(([c]) => !missingColumns.has(c));
32988
+ let result;
32989
+ for (; ; ) {
32990
+ const { sql, values } = buildInsert(cols);
32991
+ try {
32992
+ result = await db.query(sql, values);
32993
+ break;
32994
+ } catch (err3) {
32995
+ const missing = missingOptionalColumn(err3, sql, cols);
32996
+ if (!missing) throw err3;
32997
+ missingColumns.add(missing);
32998
+ console.error(
32999
+ `\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]}`
33000
+ );
33001
+ cols = cols.filter(([c]) => c !== missing);
33002
+ }
33003
+ }
33004
+ if (result.rows.length === 0) {
33005
+ const existing = await db.query(
33006
+ `SELECT id, created_at FROM memory WHERE external_uuid = $1 LIMIT 1`,
33007
+ [external_uuid]
33008
+ );
33009
+ if (existing.rows.length > 0) {
33010
+ const row2 = existing.rows[0];
33011
+ return { id: Number(row2.id), created_at: row2.created_at, inserted: false };
33012
+ }
33013
+ throw new Error(`insertRawMemory: ON CONFLICT triggered but no existing row found (external_uuid=${external_uuid})`);
33014
+ }
33015
+ const row = result.rows[0];
33016
+ return { id: Number(row.id), created_at: row.created_at, inserted: true };
33017
+ }
33018
+ var missingColumns, MISSING_COLUMN_EFFECT;
33019
+ var init_hot_path = __esm({
33020
+ "src/hot_path.ts"() {
33021
+ "use strict";
33022
+ init_db();
33023
+ init_buzz_envelope();
33024
+ init_venue();
33025
+ missingColumns = /* @__PURE__ */ new Set();
33026
+ MISSING_COLUMN_EFFECT = {
33027
+ 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.",
33028
+ 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."
33029
+ };
33030
+ }
33031
+ });
33032
+
33033
+ // src/users.ts
33034
+ async function getOrCreateUser(userName) {
33035
+ const cached2 = cache.get(userName);
33036
+ if (cached2 !== void 0) return cached2;
33037
+ const existing = await db.query(
33038
+ `SELECT user_id FROM users WHERE user_name = $1 LIMIT 1`,
33039
+ [userName]
33040
+ );
33041
+ if (existing.rows.length > 0) {
33042
+ const id2 = Number(existing.rows[0].user_id);
33043
+ cache.set(userName, id2);
33044
+ return id2;
33045
+ }
33046
+ const inserted = await db.query(
33047
+ `INSERT INTO users (user_name) VALUES ($1)
33048
+ ON CONFLICT (user_name) DO UPDATE SET updated_at = users.updated_at
33049
+ RETURNING user_id`,
33050
+ [userName]
33051
+ );
33052
+ const id = Number(inserted.rows[0].user_id);
33053
+ cache.set(userName, id);
33054
+ return id;
33055
+ }
33056
+ async function getDefaultUserId() {
33057
+ const name = process.env.USER_NAME ?? "hoon";
33058
+ return getOrCreateUser(name);
33059
+ }
33060
+ async function updateUserProfile(userId, field, value) {
33061
+ if (field === "core_profile") {
33062
+ await db.query(
33063
+ `UPDATE users SET core_profile = $1 WHERE user_id = $2`,
33064
+ [value, userId]
33065
+ );
33066
+ } else {
33067
+ await db.query(
33068
+ `UPDATE users SET sub_profile = $1 WHERE user_id = $2`,
33069
+ [value, userId]
33070
+ );
33071
+ }
33072
+ }
33073
+ async function getUserProfile(userId) {
33074
+ const r = await db.query(
33075
+ `SELECT core_profile, sub_profile FROM users WHERE user_id = $1`,
33076
+ [userId]
33077
+ );
33078
+ return r.rows[0] ?? { core_profile: null, sub_profile: null };
33079
+ }
33080
+ var cache;
33081
+ var init_users = __esm({
33082
+ "src/users.ts"() {
33083
+ "use strict";
33084
+ init_db();
33085
+ cache = /* @__PURE__ */ new Map();
33086
+ }
33087
+ });
33088
+
32742
33089
  // src/setup.ts
32743
33090
  var setup_exports = {};
32744
33091
  __export(setup_exports, {
@@ -32763,6 +33110,326 @@ var init_setup = __esm({
32763
33110
  }
32764
33111
  });
32765
33112
 
33113
+ // src/auto_save/buzz_ingest.ts
33114
+ var buzz_ingest_exports = {};
33115
+ __export(buzz_ingest_exports, {
33116
+ authorLabel: () => authorLabel,
33117
+ channelVenue: () => channelVenue,
33118
+ chatMessage: () => chatMessage,
33119
+ latestEdits: () => latestEdits,
33120
+ listChannels: () => listChannels,
33121
+ newChatEvents: () => newChatEvents,
33122
+ ownerPubkeys: () => ownerPubkeys,
33123
+ parseIngestArgs: () => parseIngestArgs,
33124
+ runBuzzIngest: () => runBuzzIngest
33125
+ });
33126
+ import { execFile } from "node:child_process";
33127
+ function cliPath() {
33128
+ return process.env.BUZZ_CLI || "buzz";
33129
+ }
33130
+ function childEnv() {
33131
+ return Object.fromEntries(Object.entries(process.env).filter(([k]) => CHILD_ENV.test(k)));
33132
+ }
33133
+ function runCli(args) {
33134
+ return new Promise((resolve, reject) => {
33135
+ execFile(
33136
+ cliPath(),
33137
+ args,
33138
+ { maxBuffer: 256 * 1024 * 1024, timeout: 12e4, env: childEnv() },
33139
+ (err3, stdout, stderr) => {
33140
+ if (err3) {
33141
+ reject(new Error(`buzz ${args.slice(0, 2).join(" ")} \uC2E4\uD328: ${String(stderr || err3.message).trim().slice(0, 500)}`));
33142
+ return;
33143
+ }
33144
+ try {
33145
+ resolve(JSON.parse(stdout));
33146
+ } catch {
33147
+ reject(new BuzzShapeError(`buzz ${args.slice(0, 2).join(" ")}: JSON\uC774 \uC544\uB2CC \uCD9C\uB825`));
33148
+ }
33149
+ }
33150
+ );
33151
+ });
33152
+ }
33153
+ function asEvent(v) {
33154
+ const e = v;
33155
+ 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"));
33156
+ if (!ok4) throw new BuzzShapeError(`\uC608\uC0C1\uACFC \uB2E4\uB978 \uC774\uBCA4\uD2B8 \uBAA8\uC591: ${JSON.stringify(v).slice(0, 200)}`);
33157
+ return e;
33158
+ }
33159
+ function asArray(v, what) {
33160
+ if (!Array.isArray(v)) throw new BuzzShapeError(`${what}: \uBC30\uC5F4\uC774 \uC544\uB2CC \uCD9C\uB825`);
33161
+ return v;
33162
+ }
33163
+ async function listChannels() {
33164
+ const parse3 = (v) => asArray(v, "channels list").map((c) => {
33165
+ const ch = c;
33166
+ if (typeof ch?.channel_id !== "string" || !UUID.test(ch.channel_id) || typeof ch.name !== "string") {
33167
+ throw new BuzzShapeError(`\uC608\uC0C1\uACFC \uB2E4\uB978 \uCC44\uB110 \uBAA8\uC591: ${JSON.stringify(c).slice(0, 200)}`);
33168
+ }
33169
+ return { id: ch.channel_id, name: ch.name };
33170
+ });
33171
+ const all = parse3(await runCli(["channels", "list", "--member"]));
33172
+ if (all.length === 0) throw new BuzzShapeError("channels list --member: CLI \uC2E0\uC6D0\uC774 \uBA64\uBC84\uC778 \uBC29\uC774 \uD558\uB098\uB3C4 \uC5C6\uC74C");
33173
+ const priv = new Set(parse3(await runCli(["channels", "list", "--member", "--visibility", "private"])).map((c) => c.id));
33174
+ return all.map((c) => ({ ...c, private: priv.has(c.id) }));
33175
+ }
33176
+ function channelVenue(ch) {
33177
+ const name = ch.name.trim();
33178
+ if (!name) return "buzz";
33179
+ return ch.private && name === "DM" ? "buzz:dm" : `buzz:${name}`;
33180
+ }
33181
+ async function getPage(channelId, kinds, before) {
33182
+ const args = ["messages", "get", "--channel", channelId, "--limit", String(PAGE), "--kinds", kinds.join(",")];
33183
+ if (before !== void 0) args.push("--before", String(before));
33184
+ return asArray(await runCli(args), "messages get").map(asEvent);
33185
+ }
33186
+ async function knownIds(eventIds) {
33187
+ if (eventIds.length === 0) return /* @__PURE__ */ new Set();
33188
+ const r = await db.query(
33189
+ `SELECT external_uuid FROM memory WHERE external_uuid = ANY($1::text[])`,
33190
+ [eventIds.map((id) => `buzz:${id}`)]
33191
+ );
33192
+ return new Set(r.rows.map((row) => String(row.external_uuid).slice("buzz:".length)));
33193
+ }
33194
+ async function pageBack(channelId, kinds, visit) {
33195
+ const seen = /* @__PURE__ */ new Set();
33196
+ let before;
33197
+ for (let pages = 0; ; pages++) {
33198
+ if (pages >= MAX_PAGES_PER_CHANNEL) {
33199
+ throw new BuzzShapeError(`${channelId}: ${MAX_PAGES_PER_CHANNEL}\uCABD\uC744 \uB118\uACA8\uB3C4 \uB05D\uB098\uC9C0 \uC54A\uC74C`);
33200
+ }
33201
+ const page = await getPage(channelId, kinds, before);
33202
+ if (page.length === 0) {
33203
+ if (before === void 0) return;
33204
+ throw new BuzzShapeError(`${channelId}: \uC774\uC5B4\uC9C0\uB294 \uCABD\uC774 \uBE44\uC5B4 \uC788\uC74C`);
33205
+ }
33206
+ const unseen = page.filter((e) => !seen.has(e.id));
33207
+ if (unseen.length === 0) {
33208
+ if (page.length >= PAGE) throw new BuzzShapeError(`${channelId}: \uCABD \uB118\uAE40\uC774 \uC55E\uC73C\uB85C \uAC00\uC9C0 \uC54A\uC74C`);
33209
+ return;
33210
+ }
33211
+ for (const e of unseen) seen.add(e.id);
33212
+ if (!await visit(page, unseen)) return;
33213
+ before = Math.min(...page.map((e) => e.created_at)) + 1;
33214
+ }
33215
+ }
33216
+ async function newChatEvents(channelId) {
33217
+ const fresh = /* @__PURE__ */ new Map();
33218
+ const nowSec = Math.floor(Date.now() / 1e3);
33219
+ let stopBelow;
33220
+ await pageBack(channelId, [KIND_CHAT], async (page, unseen) => {
33221
+ const chats = unseen.filter((e) => e.kind === KIND_CHAT);
33222
+ const known = await knownIds(chats.map((e) => e.id));
33223
+ for (const e of chats) if (!known.has(e.id)) fresh.set(e.id, e);
33224
+ if (known.size > 0 && stopBelow === void 0) {
33225
+ const newestKnown = Math.max(...chats.filter((e) => known.has(e.id)).map((e) => e.created_at));
33226
+ stopBelow = Math.min(newestKnown, nowSec) - LOOKBACK_SEC;
33227
+ }
33228
+ return !(stopBelow !== void 0 && Math.min(...page.map((e) => e.created_at)) < stopBelow);
33229
+ });
33230
+ return [...fresh.values()];
33231
+ }
33232
+ async function recentEvents(channelId, kinds, floor) {
33233
+ const out = [];
33234
+ await pageBack(channelId, kinds, async (page, unseen) => {
33235
+ out.push(...unseen.filter((e) => kinds.includes(e.kind)));
33236
+ return floor !== void 0 && Math.min(...page.map((e) => e.created_at)) >= floor;
33237
+ });
33238
+ return out;
33239
+ }
33240
+ async function displayNames(pubkeys) {
33241
+ const self2 = asArray(await runCli(["users", "get"]), "users get").map((u) => u?.pubkey).find((pk) => typeof pk === "string" && HEX64.test(pk));
33242
+ if (!self2) {
33243
+ 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)");
33244
+ }
33245
+ const names = /* @__PURE__ */ new Map();
33246
+ for (let i = 0; i < pubkeys.length; i += 20) {
33247
+ const chunk = pubkeys.slice(i, i + 20);
33248
+ const ask = chunk.includes(self2) ? chunk : [...chunk, self2];
33249
+ const out = asArray(await runCli(["users", "get", ...ask.flatMap((pk) => ["--pubkey", pk])]), "users get");
33250
+ const profiles = out.map((u) => u);
33251
+ if (!profiles.some((p) => p?.pubkey === self2)) {
33252
+ throw new BuzzShapeError("users get: \uC790\uAE30 \uD504\uB85C\uD544\uC774 \uBE60\uC9C4 \uC751\uB2F5 \u2014 \uC774\uB984\uC744 \uBBFF\uC744 \uC218 \uC5C6\uC74C");
33253
+ }
33254
+ for (const p of profiles) {
33255
+ const label = [p?.display_name, p?.name].find((v) => typeof v === "string" && v.trim() !== "");
33256
+ if (typeof p?.pubkey === "string" && label) names.set(p.pubkey, label.trim());
33257
+ }
33258
+ }
33259
+ return names;
33260
+ }
33261
+ function ownerPubkeys() {
33262
+ const raw = process.env.BUZZ_INGEST_OWNER ?? "";
33263
+ const keys = raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
33264
+ if (keys.length === 0 || !keys.every((k) => HEX64.test(k))) {
33265
+ throw new Error(
33266
+ "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"
33267
+ );
33268
+ }
33269
+ return new Set(keys);
33270
+ }
33271
+ function authorLabel(pubkey, names) {
33272
+ return names.get(pubkey) ?? pubkey.slice(0, 8);
33273
+ }
33274
+ function chatMessage(e, names) {
33275
+ return `[${authorLabel(e.pubkey, names)}] ${e.content}`;
33276
+ }
33277
+ function targetOf(e) {
33278
+ const t = e.tags.find((tag) => tag[0] === "e" && HEX64.test(tag[1] ?? ""));
33279
+ return t ? t[1] : null;
33280
+ }
33281
+ function latestEdits(events) {
33282
+ const latest = /* @__PURE__ */ new Map();
33283
+ for (const e of events) {
33284
+ if (e.kind !== KIND_EDIT) continue;
33285
+ const target = targetOf(e);
33286
+ if (!target) continue;
33287
+ const cur = latest.get(target);
33288
+ if (!cur || e.created_at > cur.created_at || e.created_at === cur.created_at && e.id > cur.id) {
33289
+ latest.set(target, e);
33290
+ }
33291
+ }
33292
+ return latest;
33293
+ }
33294
+ async function applyEdits(edits, dryRun) {
33295
+ if (edits.size === 0) return 0;
33296
+ const r = await db.query(
33297
+ `SELECT external_uuid, message, raw_message FROM memory
33298
+ WHERE external_uuid = ANY($1::text[]) AND agent_platform = 'buzz' AND raw_message IS NOT NULL`,
33299
+ [[...edits.keys()].map((id) => `buzz:${id}`)]
33300
+ );
33301
+ let n = 0;
33302
+ for (const row of r.rows) {
33303
+ const edit = edits.get(String(row.external_uuid).slice("buzz:".length));
33304
+ const raw = row.raw_message;
33305
+ const msg = row.message;
33306
+ if (raw === edit.content || !msg.endsWith(raw)) continue;
33307
+ n++;
33308
+ if (dryRun) continue;
33309
+ await db.query(
33310
+ `UPDATE memory
33311
+ SET message = $2, raw_message = $3,
33312
+ tag_processed = FALSE, embedding = NULL, cold_error = NULL
33313
+ WHERE external_uuid = $1`,
33314
+ [row.external_uuid, msg.slice(0, msg.length - raw.length) + edit.content, edit.content]
33315
+ );
33316
+ }
33317
+ return n;
33318
+ }
33319
+ async function hideDeleted(targets, dryRun) {
33320
+ if (targets.length === 0) return 0;
33321
+ const uuids = targets.map((id) => `buzz:${id}`);
33322
+ const sql = dryRun ? `SELECT count(*)::int AS n FROM memory
33323
+ WHERE external_uuid = ANY($1::text[]) AND agent_platform = 'buzz' AND is_active AND NOT is_pinned` : `WITH h AS (
33324
+ UPDATE memory SET is_active = FALSE, archived_at = NOW()
33325
+ WHERE external_uuid = ANY($1::text[]) AND agent_platform = 'buzz' AND is_active AND NOT is_pinned
33326
+ RETURNING 1)
33327
+ SELECT count(*)::int AS n FROM h`;
33328
+ const r = await db.query(sql, [uuids]);
33329
+ return Number(r.rows[0]?.n ?? 0);
33330
+ }
33331
+ async function runBuzzIngest(opts = {}) {
33332
+ const dryRun = opts.dryRun ?? false;
33333
+ const max = opts.max ?? 20;
33334
+ const owner = ownerPubkeys();
33335
+ const channels = await listChannels();
33336
+ const pending = [];
33337
+ for (const ch of channels) {
33338
+ const venue = channelVenue(ch);
33339
+ for (const e of await newChatEvents(ch.id)) pending.push({ e, ch, venue });
33340
+ }
33341
+ 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));
33342
+ const deleteTargets = /* @__PURE__ */ new Set();
33343
+ const edits = [];
33344
+ let unsupported = 0;
33345
+ for (const ch of channels) {
33346
+ for (const d of await getPage(ch.id, DELETE_KINDS)) {
33347
+ const t = DELETE_KINDS.includes(d.kind) ? targetOf(d) : null;
33348
+ if (t) deleteTargets.add(t);
33349
+ }
33350
+ }
33351
+ const batch = pending.filter(({ e }) => !deleteTargets.has(e.id)).slice(0, Math.max(0, max));
33352
+ for (const ch of channels) {
33353
+ const mine = batch.filter((b) => b.ch.id === ch.id).map((b) => b.e.created_at);
33354
+ edits.push(...await recentEvents(ch.id, [KIND_EDIT], mine.length ? Math.min(...mine) - LOOKBACK_SEC : void 0));
33355
+ if (dryRun) unsupported += (await getPage(ch.id, UNSUPPORTED_KINDS)).filter((e) => UNSUPPORTED_KINDS.includes(e.kind)).length;
33356
+ }
33357
+ const pubkeys = [...new Set(batch.map(({ e }) => e.pubkey))];
33358
+ const names = pubkeys.length ? await displayNames(pubkeys) : /* @__PURE__ */ new Map();
33359
+ const report = {
33360
+ channels: channels.length,
33361
+ pending: pending.length,
33362
+ inserted: 0,
33363
+ edited: 0,
33364
+ hidden: 0,
33365
+ byVenue: {},
33366
+ ...dryRun ? { unsupported } : {}
33367
+ };
33368
+ const userId = dryRun ? 0 : await getDefaultUserId();
33369
+ for (const { e, venue } of batch) {
33370
+ report.byVenue[venue] = (report.byVenue[venue] ?? 0) + 1;
33371
+ if (dryRun) {
33372
+ report.inserted++;
33373
+ continue;
33374
+ }
33375
+ let res;
33376
+ try {
33377
+ res = await insertRawMemory({
33378
+ user_id: userId,
33379
+ agent_platform: "buzz",
33380
+ agent_model: null,
33381
+ role: owner.has(e.pubkey) ? "user" : "assistant",
33382
+ message: chatMessage(e, names),
33383
+ raw_message: e.content,
33384
+ external_uuid: `buzz:${e.id}`,
33385
+ device_name: null,
33386
+ venue,
33387
+ created_at: new Date(e.created_at * 1e3)
33388
+ });
33389
+ } catch (err3) {
33390
+ throw new Error(`buzz:${e.id} \uC800\uC7A5 \uC2E4\uD328: ${err3 instanceof Error ? err3.message : String(err3)}`);
33391
+ }
33392
+ if (res.inserted) report.inserted++;
33393
+ }
33394
+ report.edited = await applyEdits(latestEdits(edits), dryRun);
33395
+ report.hidden = await hideDeleted([...deleteTargets], dryRun);
33396
+ return report;
33397
+ }
33398
+ function parseIngestArgs(argv) {
33399
+ const opts = {};
33400
+ for (let i = 0; i < argv.length; i++) {
33401
+ const a = argv[i];
33402
+ if (a === "--dry-run") opts.dryRun = true;
33403
+ else if (a === "--max") {
33404
+ const v = argv[++i];
33405
+ if (v === void 0 || !/^\d+$/.test(v)) throw new Error("--max: 0 \uC774\uC0C1\uC758 \uC815\uC218");
33406
+ opts.max = Number(v);
33407
+ } else throw new Error(`buzz-ingest: \uBAA8\uB974\uB294 \uC635\uC158 ${a}`);
33408
+ }
33409
+ return opts;
33410
+ }
33411
+ var KIND_CHAT, KIND_EDIT, DELETE_KINDS, UNSUPPORTED_KINDS, PAGE, MAX_PAGES_PER_CHANNEL, LOOKBACK_SEC, HEX64, UUID, CHILD_ENV, BuzzShapeError;
33412
+ var init_buzz_ingest = __esm({
33413
+ "src/auto_save/buzz_ingest.ts"() {
33414
+ "use strict";
33415
+ init_db();
33416
+ init_hot_path();
33417
+ init_users();
33418
+ KIND_CHAT = 9;
33419
+ KIND_EDIT = 40003;
33420
+ DELETE_KINDS = [5, 9005];
33421
+ UNSUPPORTED_KINDS = [40002, 40008, 45001, 45003];
33422
+ PAGE = 200;
33423
+ MAX_PAGES_PER_CHANNEL = 500;
33424
+ LOOKBACK_SEC = 48 * 3600;
33425
+ HEX64 = /^[0-9a-f]{64}$/;
33426
+ UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
33427
+ 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;
33428
+ BuzzShapeError = class extends Error {
33429
+ };
33430
+ }
33431
+ });
33432
+
32766
33433
  // src/migrations/runner.ts
32767
33434
  var runner_exports = {};
32768
33435
  __export(runner_exports, {
@@ -32770,12 +33437,12 @@ __export(runner_exports, {
32770
33437
  runAllMigrations: () => runAllMigrations
32771
33438
  });
32772
33439
  import { spawn } from "child_process";
32773
- import fs11 from "fs";
33440
+ import fs12 from "fs";
32774
33441
  import path12 from "path";
32775
33442
  import { fileURLToPath as fileURLToPath4 } from "url";
32776
33443
  function listMigrationFiles() {
32777
- if (!fs11.existsSync(migrationsDir)) return [];
32778
- return fs11.readdirSync(migrationsDir).filter((f) => MIGRATION_FILE_RE.test(f)).sort();
33444
+ if (!fs12.existsSync(migrationsDir)) return [];
33445
+ return fs12.readdirSync(migrationsDir).filter((f) => MIGRATION_FILE_RE.test(f)).sort();
32779
33446
  }
32780
33447
  function runOne(file) {
32781
33448
  return new Promise((resolve, reject) => {
@@ -32808,7 +33475,7 @@ var init_runner = __esm({
32808
33475
  "src/migrations/runner.ts"() {
32809
33476
  "use strict";
32810
33477
  __dirname4 = path12.dirname(fileURLToPath4(import.meta.url));
32811
- migrationsDir = fs11.existsSync(path12.join(__dirname4, "migrations")) ? path12.join(__dirname4, "migrations") : __dirname4;
33478
+ migrationsDir = fs12.existsSync(path12.join(__dirname4, "migrations")) ? path12.join(__dirname4, "migrations") : __dirname4;
32812
33479
  MIGRATION_FILE_RE = /^\d{3}_.+\.js$/;
32813
33480
  }
32814
33481
  });
@@ -47030,231 +47697,10 @@ var StdioServerTransport = class {
47030
47697
 
47031
47698
  // src/tools/manage_knowledge.ts
47032
47699
  init_db();
47700
+ init_hot_path();
47701
+ init_users();
47033
47702
  import * as os3 from "node:os";
47034
47703
 
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 cleanBuzzEnvelope(message) {
47053
- try {
47054
- const text = message.trimStart();
47055
- if (!text.startsWith("<base>")) return null;
47056
- const baseEnd = text.indexOf("</base>");
47057
- if (baseEnd < 0) return null;
47058
- const rest = text.slice(baseEnd + "</base>".length);
47059
- let pos = skipWhitespace(rest, 0);
47060
- for (; ; ) {
47061
- const name = PREAMBLE_BLOCKS.find((b) => rest.startsWith(`<${b}>`, pos));
47062
- if (!name) break;
47063
- const close = rest.indexOf(`</${name}>`, pos);
47064
- if (close < 0) return null;
47065
- pos = skipWhitespace(rest, close + name.length + 3);
47066
- }
47067
- if (!CONTEXT_OPEN.test(rest.slice(pos, pos + 12))) return null;
47068
- const bodyStart = rest.indexOf("\n", pos) + 1;
47069
- const contextClose = rest.indexOf(CONTEXT_CLOSE, bodyStart - 1);
47070
- if (contextClose < 0) return null;
47071
- const contextBody = rest.slice(bodyStart, contextClose).replace(/\r$/, "");
47072
- const tail = rest.slice(contextClose + CONTEXT_CLOSE.length);
47073
- let turnStart = 0;
47074
- const history = HISTORY_OPEN.exec(tail);
47075
- if (history) {
47076
- const closeTag = `</${history[1]}>`;
47077
- const close = tail.indexOf(closeTag, history[0].length);
47078
- if (close < 0) return null;
47079
- turnStart = close + closeTag.length;
47080
- }
47081
- if (TURN_IN_DROPPED.test(rest.slice(0, pos)) || TURN_IN_DROPPED.test(tail.slice(0, turnStart))) return null;
47082
- const turn = tail.slice(turnStart);
47083
- if (!TURN_OPEN.test(turn)) return null;
47084
- const turnText = turn.split("\n").filter((line) => !isNostrMetaLine(line)).join("\n").trim();
47085
- if (!turnText) return null;
47086
- const cleaned = `<context>
47087
- ${contextBody}
47088
- </context>
47089
-
47090
- ${turnText}`;
47091
- if (cleaned.length >= message.length) return null;
47092
- return cleaned;
47093
- } catch {
47094
- return null;
47095
- }
47096
- }
47097
- 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*$/;
47098
- function buzzEmbeddingText(message) {
47099
- try {
47100
- if (!message.startsWith("<context>\n")) return message;
47101
- const close = message.indexOf(CONTEXT_CLOSE);
47102
- if (close < 0) return message;
47103
- const turn = message.slice(close + CONTEXT_CLOSE.length);
47104
- if (!TURN_OPEN.test(turn)) return message;
47105
- const text = turn.split("\n").filter((line) => !EVENT_HEADER.test(line) && !isNostrMetaLine(line)).map(
47106
- (line) => (
47107
- // `$` 없이: 끝까지 되짚는 역추적을 막는다 (긴 줄에서 제곱 시간)
47108
- line.replace(/^From: (.*?) \(npub:.*/, "From: $1").replace(/^Channel: (\S+) \(#[0-9a-f-]+\)/, "Channel: $1")
47109
- )
47110
- ).join("\n").trim();
47111
- return text || message;
47112
- } catch {
47113
- return message;
47114
- }
47115
- }
47116
-
47117
- // src/hot_path.ts
47118
- var rawMessageColumnMissing = false;
47119
- async function insertRawMemory(params) {
47120
- const {
47121
- user_id,
47122
- agent_platform,
47123
- agent_model,
47124
- subagent = false,
47125
- subagent_model = null,
47126
- subagent_role = null,
47127
- role,
47128
- message,
47129
- is_pinned = false,
47130
- p_tag_id = null,
47131
- d_tag = [],
47132
- embedding = null,
47133
- external_uuid = null,
47134
- device_name = null
47135
- } = params;
47136
- const embeddingSql = embedding && embedding.length > 0 ? `[${embedding.join(",")}]` : null;
47137
- const tagProcessed = p_tag_id !== null;
47138
- const cleaned = role === "user" && !is_pinned && p_tag_id === null && embeddingSql === null ? cleanBuzzEnvelope(message) : null;
47139
- const insert = (text, raw) => db.query(
47140
- `INSERT INTO memory (
47141
- user_id, agent_platform, agent_model,
47142
- subagent, subagent_model, subagent_role,
47143
- role, message,
47144
- p_tag_id, d_tag, embedding,
47145
- is_pinned, tag_processed, external_uuid,
47146
- device_name${raw !== null ? ", raw_message" : ""}
47147
- ) VALUES (
47148
- $1, $2, $3,
47149
- $4, $5, $6,
47150
- $7, $8,
47151
- $9, $10::text[], $11::halfvec,
47152
- $12, $13, $14,
47153
- $15${raw !== null ? ", $16" : ""}
47154
- )
47155
- ON CONFLICT (external_uuid) WHERE external_uuid IS NOT NULL
47156
- DO NOTHING
47157
- RETURNING id, created_at`,
47158
- [
47159
- user_id,
47160
- agent_platform,
47161
- agent_model,
47162
- subagent,
47163
- subagent_model,
47164
- subagent_role,
47165
- role,
47166
- text,
47167
- p_tag_id,
47168
- d_tag,
47169
- embeddingSql,
47170
- is_pinned,
47171
- tagProcessed,
47172
- external_uuid,
47173
- device_name,
47174
- ...raw !== null ? [raw] : []
47175
- ]
47176
- );
47177
- let result;
47178
- if (cleaned !== null && !rawMessageColumnMissing) {
47179
- try {
47180
- result = await insert(cleaned, message);
47181
- } catch (err3) {
47182
- const e = err3;
47183
- if (e.code !== "42703" || !e.message?.includes("raw_message")) throw err3;
47184
- rawMessageColumnMissing = true;
47185
- console.error(
47186
- "\u26A0\uFE0F [HotPath] memory.raw_message \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 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."
47187
- );
47188
- result = await insert(message, null);
47189
- }
47190
- } else {
47191
- result = await insert(message, null);
47192
- }
47193
- if (result.rows.length === 0) {
47194
- const existing = await db.query(
47195
- `SELECT id, created_at FROM memory WHERE external_uuid = $1 LIMIT 1`,
47196
- [external_uuid]
47197
- );
47198
- if (existing.rows.length > 0) {
47199
- const row2 = existing.rows[0];
47200
- return { id: Number(row2.id), created_at: row2.created_at, inserted: false };
47201
- }
47202
- throw new Error(`insertRawMemory: ON CONFLICT triggered but no existing row found (external_uuid=${external_uuid})`);
47203
- }
47204
- const row = result.rows[0];
47205
- return { id: Number(row.id), created_at: row.created_at, inserted: true };
47206
- }
47207
-
47208
- // src/users.ts
47209
- init_db();
47210
- var cache = /* @__PURE__ */ new Map();
47211
- async function getOrCreateUser(userName) {
47212
- const cached2 = cache.get(userName);
47213
- if (cached2 !== void 0) return cached2;
47214
- const existing = await db.query(
47215
- `SELECT user_id FROM users WHERE user_name = $1 LIMIT 1`,
47216
- [userName]
47217
- );
47218
- if (existing.rows.length > 0) {
47219
- const id2 = Number(existing.rows[0].user_id);
47220
- cache.set(userName, id2);
47221
- return id2;
47222
- }
47223
- const inserted = await db.query(
47224
- `INSERT INTO users (user_name) VALUES ($1)
47225
- ON CONFLICT (user_name) DO UPDATE SET updated_at = users.updated_at
47226
- RETURNING user_id`,
47227
- [userName]
47228
- );
47229
- const id = Number(inserted.rows[0].user_id);
47230
- cache.set(userName, id);
47231
- return id;
47232
- }
47233
- async function getDefaultUserId() {
47234
- const name = process.env.USER_NAME ?? "hoon";
47235
- return getOrCreateUser(name);
47236
- }
47237
- async function updateUserProfile(userId, field, value) {
47238
- if (field === "core_profile") {
47239
- await db.query(
47240
- `UPDATE users SET core_profile = $1 WHERE user_id = $2`,
47241
- [value, userId]
47242
- );
47243
- } else {
47244
- await db.query(
47245
- `UPDATE users SET sub_profile = $1 WHERE user_id = $2`,
47246
- [value, userId]
47247
- );
47248
- }
47249
- }
47250
- async function getUserProfile(userId) {
47251
- const r = await db.query(
47252
- `SELECT core_profile, sub_profile FROM users WHERE user_id = $1`,
47253
- [userId]
47254
- );
47255
- return r.rows[0] ?? { core_profile: null, sub_profile: null };
47256
- }
47257
-
47258
47704
  // ../mcp-agents-memory/node_modules/openai/internal/tslib.mjs
47259
47705
  function __classPrivateFieldSet(receiver, state, value, kind, f) {
47260
47706
  if (kind === "m")
@@ -55635,7 +56081,7 @@ async function generateEmbedding(text) {
55635
56081
  init_db();
55636
56082
 
55637
56083
  // src/cold_path/jev_judge.ts
55638
- import fs2 from "fs";
56084
+ import fs3 from "fs";
55639
56085
  var API_URL = "https://api.typesafe.ai/v1/systemone";
55640
56086
  var MODEL = "jev-latest";
55641
56087
  var TYPESAFE_KEY_PATH = "/home/admin_3alab/.config/typesafe/api_key";
@@ -55661,7 +56107,7 @@ function loadJevApiKey() {
55661
56107
  const fromEnv = process.env.TYPESAFE_API_KEY?.trim();
55662
56108
  if (fromEnv) return fromEnv;
55663
56109
  try {
55664
- return fs2.readFileSync(TYPESAFE_KEY_PATH, "utf8").trim() || null;
56110
+ return fs3.readFileSync(TYPESAFE_KEY_PATH, "utf8").trim() || null;
55665
56111
  } catch {
55666
56112
  return null;
55667
56113
  }
@@ -55995,7 +56441,9 @@ async function tagMessage(input) {
55995
56441
  }
55996
56442
 
55997
56443
  // src/auto_save/opencode_capture.ts
55998
- import * as fs3 from "node:fs";
56444
+ init_hot_path();
56445
+ init_users();
56446
+ import * as fs4 from "node:fs";
55999
56447
  import * as path4 from "node:path";
56000
56448
  import * as os2 from "node:os";
56001
56449
  var DEVICE_NAME = os2.hostname();
@@ -56191,7 +56639,7 @@ async function captureSessionStart(_cwd) {
56191
56639
  _state = null;
56192
56640
  _consecutiveFailures = 0;
56193
56641
  const dbPath = resolveDbPath();
56194
- if (!dbPath || !fs3.existsSync(dbPath)) return;
56642
+ if (!dbPath || !fs4.existsSync(dbPath)) return;
56195
56643
  const DatabaseSync = await loadSqlite();
56196
56644
  if (!DatabaseSync) {
56197
56645
  console.error(
@@ -56557,6 +57005,7 @@ ${args.content}` : args.content;
56557
57005
 
56558
57006
  // src/tools/search_memory.ts
56559
57007
  init_db();
57008
+ init_users();
56560
57009
 
56561
57010
  // src/cold_path/embedder.ts
56562
57011
  var MAX_RETRIES = 3;
@@ -56590,6 +57039,30 @@ function vectorToHalfvecSql(embedding) {
56590
57039
 
56591
57040
  // src/tools/search_memory.ts
56592
57041
  import os4 from "node:os";
57042
+
57043
+ // src/memory_columns.ts
57044
+ init_db();
57045
+ var cache2 = /* @__PURE__ */ new Map();
57046
+ async function hasMemoryColumn(column) {
57047
+ const c = cache2.get(column);
57048
+ if (c && (c.exists || Date.now() - c.checkedAt < 5 * 60 * 1e3)) return c.exists;
57049
+ let exists = false;
57050
+ try {
57051
+ const r = await db.query(
57052
+ // 쿼리가 search_path로 찾는 바로 그 memory 테이블 기준
57053
+ `SELECT 1 FROM pg_attribute
57054
+ WHERE attrelid = to_regclass('memory') AND attname = $1 AND NOT attisdropped LIMIT 1`,
57055
+ [column]
57056
+ );
57057
+ exists = r.rows.length > 0;
57058
+ } catch {
57059
+ exists = false;
57060
+ }
57061
+ cache2.set(column, { exists, checkedAt: Date.now() });
57062
+ return exists;
57063
+ }
57064
+
57065
+ // src/tools/search_memory.ts
56593
57066
  var DEFAULT_LIMIT = 10;
56594
57067
  var DEFAULT_FALLBACK_THRESHOLD = 0.3;
56595
57068
  function parseDateRange(s) {
@@ -56664,7 +57137,10 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56664
57137
  "\uAE30\uAE30 \uD55C\uC815. 'global'(\uAE30\uBCF8): \uC804 \uAE30\uAE30 \uAC80\uC0C9(\uB113\uAC8C). 'local': \uD604\uC7AC \uAE30\uAE30(hostname)\uB85C \uD55C\uC815(\uC774\uC5B4\uBC1B\uAE30\uC6A9, \uC881\uAC8C). zero-config \u2014 \uC11C\uBC84\uAC00 hostname \uC790\uB3D9 \uD574\uC11D."
56665
57138
  ),
56666
57139
  limit: external_exports.number().int().min(1).max(50).optional().describe(`\uCD5C\uB300 \uACB0\uACFC \uC218 (default ${DEFAULT_LIMIT})`),
56667
- include_archived: external_exports.boolean().optional().describe("archived \uBA54\uBAA8\uB9AC\uB3C4 \uD3EC\uD568 (default false)")
57140
+ include_archived: external_exports.boolean().optional().describe("archived \uBA54\uBAA8\uB9AC\uB3C4 \uD3EC\uD568 (default false)"),
57141
+ venue: external_exports.string().optional().describe(
57142
+ "\uB300\uD654\uAC00 \uC624\uAC04 \uC790\uB9AC\uB85C \uD55C\uC815. 'buzz'(\uBC84\uC988 \uC804\uCCB4) / 'buzz:<\uCC44\uB110>'(\uADF8 \uCC44\uB110, \uC608: buzz:DevRoom) / 'buzz:dm' / 'terminal'(\uC0AC\uB78C\uACFC 1:1) / 'slack' / 'auto'. \uACB0\uACFC\uB9C8\uB2E4 venue\uAC00 \uBD99\uC5B4 \uB098\uC628\uB2E4."
57143
+ )
56668
57144
  }
56669
57145
  },
56670
57146
  async (args) => {
@@ -56717,6 +57193,13 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56717
57193
  filters.push(`(m.device_name = $${p++} OR m.is_pinned = TRUE)`);
56718
57194
  params.push(localDevice);
56719
57195
  }
57196
+ const withVenue = await hasMemoryColumn("venue");
57197
+ const venueSel = withVenue ? ", m.venue" : "";
57198
+ const venueFilter = (n) => args.venue.includes(":") ? `lower(m.venue) = lower($${n})` : `(lower(m.venue) = lower($${n}) OR lower(m.venue) LIKE lower($${n}) || ':%')`;
57199
+ if (args.venue && withVenue) {
57200
+ filters.push(venueFilter(p++));
57201
+ params.push(args.venue);
57202
+ }
56720
57203
  const whereSql = filters.join(" AND ");
56721
57204
  let used = "recency";
56722
57205
  let topSimilarity = void 0;
@@ -56731,7 +57214,7 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56731
57214
  params.push(limit2);
56732
57215
  const r = await db.query(
56733
57216
  `SELECT m.id, m.role, m.message, m.agent_platform, m.agent_model, m.device_name,
56734
- pt.name AS p_tag_name, m.d_tag, m.is_pinned, m.created_at,
57217
+ pt.name AS p_tag_name, m.d_tag, m.is_pinned, m.created_at${venueSel},
56735
57218
  1 - (m.embedding <=> $${vecParam}::halfvec) AS similarity
56736
57219
  FROM memory m
56737
57220
  LEFT JOIN project_tags pt ON pt.id = canonical_project_tag_id(m.p_tag_id)
@@ -56752,6 +57235,7 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56752
57235
  d_tag: row.d_tag ?? [],
56753
57236
  is_pinned: row.is_pinned,
56754
57237
  created_at: row.created_at,
57238
+ venue: row.venue ?? null,
56755
57239
  similarity: Number(row.similarity)
56756
57240
  }));
56757
57241
  if (rows.length > 0) {
@@ -56787,12 +57271,16 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56787
57271
  ilikeFilters.push(`(m.device_name = $${q++} OR m.is_pinned = TRUE)`);
56788
57272
  ilikeParams.push(localDevice);
56789
57273
  }
57274
+ if (args.venue && withVenue) {
57275
+ ilikeFilters.push(venueFilter(q++));
57276
+ ilikeParams.push(args.venue);
57277
+ }
56790
57278
  ilikeFilters.push(`m.message ILIKE $${q++}`);
56791
57279
  ilikeParams.push(`%${args.query}%`);
56792
57280
  ilikeParams.push(limit2);
56793
57281
  const r = await db.query(
56794
57282
  `SELECT m.id, m.role, m.message, m.agent_platform, m.agent_model, m.device_name,
56795
- pt.name AS p_tag_name, m.d_tag, m.is_pinned, m.created_at
57283
+ pt.name AS p_tag_name, m.d_tag, m.is_pinned, m.created_at${venueSel}
56796
57284
  FROM memory m
56797
57285
  LEFT JOIN project_tags pt ON pt.id = canonical_project_tag_id(m.p_tag_id)
56798
57286
  WHERE ${ilikeFilters.join(" AND ")}
@@ -56811,7 +57299,8 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56811
57299
  p_tag_name: row.p_tag_name,
56812
57300
  d_tag: row.d_tag ?? [],
56813
57301
  is_pinned: row.is_pinned,
56814
- created_at: row.created_at
57302
+ created_at: row.created_at,
57303
+ venue: row.venue ?? null
56815
57304
  }));
56816
57305
  used = "ilike";
56817
57306
  topSimilarity = void 0;
@@ -56821,7 +57310,7 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56821
57310
  params.push(limit2);
56822
57311
  const r = await db.query(
56823
57312
  `SELECT m.id, m.role, m.message, m.agent_platform, m.agent_model, m.device_name,
56824
- pt.name AS p_tag_name, m.d_tag, m.is_pinned, m.created_at
57313
+ pt.name AS p_tag_name, m.d_tag, m.is_pinned, m.created_at${venueSel}
56825
57314
  FROM memory m
56826
57315
  LEFT JOIN project_tags pt ON pt.id = canonical_project_tag_id(m.p_tag_id)
56827
57316
  WHERE ${whereSql}
@@ -56839,7 +57328,8 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56839
57328
  p_tag_name: row.p_tag_name,
56840
57329
  d_tag: row.d_tag ?? [],
56841
57330
  is_pinned: row.is_pinned,
56842
- created_at: row.created_at
57331
+ created_at: row.created_at,
57332
+ venue: row.venue ?? null
56843
57333
  }));
56844
57334
  used = "recency";
56845
57335
  }
@@ -56851,6 +57341,7 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56851
57341
  threshold: used === "ilike" ? threshold : void 0,
56852
57342
  top_similarity: topSimilarity,
56853
57343
  count: rows.length,
57344
+ ...args.venue && !withVenue ? { note: "\uC774 DB\uC5D4 \uC544\uC9C1 venue \uCE78\uC774 \uC5C6\uC5B4(migration 029 \uC804) venue \uD544\uD130\uB97C \uC801\uC6A9\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4." } : {},
56854
57345
  results: rows
56855
57346
  }, null, 2)
56856
57347
  }]
@@ -56861,9 +57352,11 @@ date_range \uC778\uC2DD \uD615\uC2DD:
56861
57352
 
56862
57353
  // src/tools/manage_project_tags.ts
56863
57354
  init_db();
57355
+ init_users();
56864
57356
 
56865
57357
  // src/cold_path/project_alias_promoter.ts
56866
57358
  init_db();
57359
+ init_users();
56867
57360
  var QUALIFYING_RELATIONS = /* @__PURE__ */ new Set([
56868
57361
  "rename",
56869
57362
  "alias",
@@ -58179,7 +58672,9 @@ Actions:
58179
58672
  }
58180
58673
 
58181
58674
  // src/briefing.ts
58675
+ init_buzz_envelope();
58182
58676
  init_db();
58677
+ init_users();
58183
58678
  import * as os5 from "node:os";
58184
58679
  var RECENT_CURRENT_LIMIT = 8;
58185
58680
  var RECENT_OTHERS_LIMIT = 4;
@@ -58192,6 +58687,7 @@ var PINNED_LIMIT = 10;
58192
58687
  var INJECT_PINNED_LIMIT = 5;
58193
58688
  var BRIEF_MAX_CHARS = Number(process.env.BRIEF_MAX_CHARS ?? 8e3);
58194
58689
  async function collectBrief(opts = {}) {
58690
+ const venueSel = await hasMemoryColumn("venue") ? ", venue" : "";
58195
58691
  const userId = opts.userId ?? await getDefaultUserId();
58196
58692
  const shortTermDays = opts.shortTermDays ?? Number(process.env.SHORT_TERM_DAYS ?? 3);
58197
58693
  const currentPlatform = opts.currentPlatform ?? null;
@@ -58202,7 +58698,7 @@ async function collectBrief(opts = {}) {
58202
58698
  );
58203
58699
  const user = u.rows[0] ?? { user_name: "unknown", core_profile: null, sub_profile: null };
58204
58700
  const pinnedMsgs = await db.query(
58205
- `SELECT role, agent_platform, device_name, message, created_at
58701
+ `SELECT role, agent_platform, device_name, message, created_at${venueSel}
58206
58702
  FROM memory
58207
58703
  WHERE user_id = $1
58208
58704
  AND is_active = TRUE
@@ -58230,7 +58726,7 @@ async function collectBrief(opts = {}) {
58230
58726
  let recentOthers = [];
58231
58727
  if (currentPlatform) {
58232
58728
  const currentMsgs = await db.query(
58233
- `SELECT role, agent_platform, device_name, message, created_at
58729
+ `SELECT role, agent_platform, device_name, message, created_at${venueSel}
58234
58730
  FROM memory
58235
58731
  WHERE user_id = $1
58236
58732
  AND is_active = TRUE
@@ -58245,7 +58741,7 @@ async function collectBrief(opts = {}) {
58245
58741
  );
58246
58742
  recentCurrent = currentMsgs.rows.reverse().map(rowToMsg);
58247
58743
  const othersMsgs = await db.query(
58248
- `SELECT role, agent_platform, device_name, message, created_at
58744
+ `SELECT role, agent_platform, device_name, message, created_at${venueSel}
58249
58745
  FROM memory
58250
58746
  WHERE user_id = $1
58251
58747
  AND is_active = TRUE
@@ -58260,7 +58756,7 @@ async function collectBrief(opts = {}) {
58260
58756
  recentOthers = othersMsgs.rows.reverse().map(rowToMsg);
58261
58757
  } else {
58262
58758
  const msgs = await db.query(
58263
- `SELECT role, agent_platform, device_name, message, created_at
58759
+ `SELECT role, agent_platform, device_name, message, created_at${venueSel}
58264
58760
  FROM memory
58265
58761
  WHERE user_id = $1
58266
58762
  AND is_active = TRUE
@@ -58307,11 +58803,15 @@ async function collectBrief(opts = {}) {
58307
58803
  };
58308
58804
  }
58309
58805
  function rowToMsg(r) {
58806
+ const raw = String(r.message ?? "");
58807
+ const buzz = buzzEmbeddingText(raw);
58808
+ const text = buzz === raw ? raw : buzz.split("\n").filter((l) => !/^<\/?[a-z-]+(\s[^>]*)?>\s*$/.test(l)).join(" ").replace(/\s+/g, " ").trim();
58310
58809
  return {
58311
58810
  role: r.role,
58312
58811
  agent_platform: r.agent_platform,
58313
58812
  device_name: r.device_name ?? null,
58314
- preview: String(r.message ?? "").slice(0, MAX_PREVIEW_STORE),
58813
+ venue: r.venue ?? null,
58814
+ preview: text.slice(0, MAX_PREVIEW_STORE),
58315
58815
  created_at: r.created_at
58316
58816
  };
58317
58817
  }
@@ -58441,7 +58941,8 @@ function fitSection(heading, lines, budget) {
58441
58941
  function formatMsgLine(m, showPlatform, maxPreview = PREVIEW_COMPACT) {
58442
58942
  const dt = m.created_at?.toISOString?.().slice(11, 16) ?? "";
58443
58943
  const device = m.device_name ? `@${m.device_name} ` : "";
58444
- const platformTag = showPlatform ? `${m.agent_platform} ${device}` : device;
58944
+ const venue = m.venue ? `${m.venue} ` : "";
58945
+ const platformTag = (showPlatform ? `${m.agent_platform} ${device}` : device) + venue;
58445
58946
  const truncated = m.preview.length > maxPreview;
58446
58947
  const text = truncated ? m.preview.slice(0, maxPreview) : m.preview;
58447
58948
  return `- [${dt} ${platformTag}${m.role}] ${text}${truncated ? "\u2026" : ""}`;
@@ -58491,10 +58992,14 @@ agent_platform_filter=false.`,
58491
58992
  }
58492
58993
 
58493
58994
  // src/auto_save/save_message_tool.ts
58995
+ init_hot_path();
58996
+ init_users();
58494
58997
  import * as os10 from "node:os";
58495
58998
 
58496
58999
  // src/auto_save/gemini_capture.ts
58497
- import * as fs4 from "node:fs";
59000
+ init_hot_path();
59001
+ init_users();
59002
+ import * as fs5 from "node:fs";
58498
59003
  import * as path5 from "node:path";
58499
59004
  import * as os6 from "node:os";
58500
59005
  import * as crypto2 from "node:crypto";
@@ -58520,12 +59025,12 @@ function deriveChatsDirCandidates(cwd) {
58520
59025
  path5.join(GEMINI_TMP_ROOT, cwdHash, "chats"),
58521
59026
  path5.join(GEMINI_TMP_ROOT, path5.basename(cwd), "chats")
58522
59027
  ];
58523
- return candidates.filter((d) => fs4.existsSync(d));
59028
+ return candidates.filter((d) => fs5.existsSync(d));
58524
59029
  }
58525
59030
  async function readSessionFile(filePath) {
58526
59031
  for (let attempt = 0; attempt < 2; attempt++) {
58527
59032
  try {
58528
- const raw = fs4.readFileSync(filePath, "utf-8");
59033
+ const raw = fs5.readFileSync(filePath, "utf-8");
58529
59034
  if (!raw.trim()) return null;
58530
59035
  return JSON.parse(raw);
58531
59036
  } catch {
@@ -58615,23 +59120,23 @@ async function flushJsonFile(filePath, fileState, contentSeen) {
58615
59120
  return { inserted, skipped, dedup };
58616
59121
  }
58617
59122
  async function flushJsonlFile(filePath, fileState, contentSeen) {
58618
- if (!fs4.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
59123
+ if (!fs5.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
58619
59124
  let stat;
58620
59125
  try {
58621
- stat = fs4.statSync(filePath);
59126
+ stat = fs5.statSync(filePath);
58622
59127
  } catch {
58623
59128
  return { inserted: 0, skipped: 0, dedup: 0 };
58624
59129
  }
58625
59130
  if (stat.size <= fileState.cursor) return { inserted: 0, skipped: 0, dedup: 0 };
58626
59131
  const length = stat.size - fileState.cursor;
58627
- const fd = fs4.openSync(filePath, "r");
59132
+ const fd = fs5.openSync(filePath, "r");
58628
59133
  let raw;
58629
59134
  try {
58630
59135
  const buf = Buffer.alloc(length);
58631
- fs4.readSync(fd, buf, 0, length, fileState.cursor);
59136
+ fs5.readSync(fd, buf, 0, length, fileState.cursor);
58632
59137
  raw = buf.toString("utf-8");
58633
59138
  } finally {
58634
- fs4.closeSync(fd);
59139
+ fs5.closeSync(fd);
58635
59140
  }
58636
59141
  const lastNl = raw.lastIndexOf("\n");
58637
59142
  if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
@@ -58702,10 +59207,10 @@ async function flushAllFiles() {
58702
59207
  let totalI = 0, totalS = 0, totalD = 0;
58703
59208
  const contentSeen = /* @__PURE__ */ new Set();
58704
59209
  for (const dir of _state2.chatsDirs) {
58705
- if (!fs4.existsSync(dir)) continue;
59210
+ if (!fs5.existsSync(dir)) continue;
58706
59211
  let entries;
58707
59212
  try {
58708
- entries = fs4.readdirSync(dir, { withFileTypes: true });
59213
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
58709
59214
  } catch {
58710
59215
  continue;
58711
59216
  }
@@ -58770,7 +59275,7 @@ function captureSessionStart2(cwd) {
58770
59275
  for (const dir of chatsDirs) {
58771
59276
  let entries;
58772
59277
  try {
58773
- entries = fs4.readdirSync(dir, { withFileTypes: true });
59278
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
58774
59279
  } catch {
58775
59280
  continue;
58776
59281
  }
@@ -58786,21 +59291,21 @@ function captureSessionStart2(cwd) {
58786
59291
  if (format === "json") {
58787
59292
  let session = null;
58788
59293
  try {
58789
- session = JSON.parse(fs4.readFileSync(filePath, "utf-8"));
59294
+ session = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
58790
59295
  } catch {
58791
59296
  }
58792
59297
  cursor = session && Array.isArray(session.messages) ? session.messages.length : 0;
58793
59298
  if (session?.sessionId) realSessionId = session.sessionId;
58794
59299
  } else {
58795
59300
  try {
58796
- cursor = fs4.statSync(filePath).size;
59301
+ cursor = fs5.statSync(filePath).size;
58797
59302
  } catch {
58798
59303
  }
58799
59304
  try {
58800
- const fd = fs4.openSync(filePath, "r");
59305
+ const fd = fs5.openSync(filePath, "r");
58801
59306
  const buf = Buffer.alloc(512);
58802
- const n = fs4.readSync(fd, buf, 0, 512, 0);
58803
- fs4.closeSync(fd);
59307
+ const n = fs5.readSync(fd, buf, 0, 512, 0);
59308
+ fs5.closeSync(fd);
58804
59309
  const firstLine = buf.slice(0, n).toString("utf-8").split("\n")[0].trim();
58805
59310
  const header = JSON.parse(firstLine);
58806
59311
  if (typeof header.sessionId === "string" && header.sessionId) {
@@ -58823,7 +59328,7 @@ function armDirWatchers() {
58823
59328
  if (_watchers.length > 0) return;
58824
59329
  for (const dir of _state2.chatsDirs) {
58825
59330
  try {
58826
- const w = fs4.watch(dir, (_evt, filename) => {
59331
+ const w = fs5.watch(dir, (_evt, filename) => {
58827
59332
  if (filename && !filename.endsWith(".json") && !filename.endsWith(".jsonl")) return;
58828
59333
  scheduleFlush();
58829
59334
  });
@@ -58878,7 +59383,9 @@ async function captureSessionEnd2() {
58878
59383
  }
58879
59384
 
58880
59385
  // src/auto_save/grok_capture.ts
58881
- import * as fs5 from "node:fs";
59386
+ init_hot_path();
59387
+ init_users();
59388
+ import * as fs6 from "node:fs";
58882
59389
  import * as path6 from "node:path";
58883
59390
  import * as os7 from "node:os";
58884
59391
  var DEVICE_NAME4 = os7.hostname();
@@ -58899,7 +59406,7 @@ function extractSessionId(filePath) {
58899
59406
  }
58900
59407
  function countCompleteLines(filePath) {
58901
59408
  try {
58902
- const content = fs5.readFileSync(filePath, "utf-8");
59409
+ const content = fs6.readFileSync(filePath, "utf-8");
58903
59410
  let n = 0;
58904
59411
  for (let i = 0; i < content.length; i++) if (content[i] === "\n") n++;
58905
59412
  return n;
@@ -58908,13 +59415,13 @@ function countCompleteLines(filePath) {
58908
59415
  }
58909
59416
  }
58910
59417
  function* walkChatHistories(root) {
58911
- if (!fs5.existsSync(root)) return;
59418
+ if (!fs6.existsSync(root)) return;
58912
59419
  const stack = [root];
58913
59420
  while (stack.length) {
58914
59421
  const dir = stack.pop();
58915
59422
  let entries;
58916
59423
  try {
58917
- entries = fs5.readdirSync(dir, { withFileTypes: true });
59424
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
58918
59425
  } catch {
58919
59426
  continue;
58920
59427
  }
@@ -58929,7 +59436,7 @@ function* walkChatHistories(root) {
58929
59436
  }
58930
59437
  }
58931
59438
  function captureSessionStart3(_cwd) {
58932
- const rootExists = fs5.existsSync(SESSIONS_ROOT);
59439
+ const rootExists = fs6.existsSync(SESSIONS_ROOT);
58933
59440
  _state3 = {
58934
59441
  rootExists,
58935
59442
  files: /* @__PURE__ */ new Map()
@@ -58941,7 +59448,7 @@ function captureSessionStart3(_cwd) {
58941
59448
  for (const filePath of walkChatHistories(SESSIONS_ROOT)) {
58942
59449
  let stat;
58943
59450
  try {
58944
- stat = fs5.statSync(filePath);
59451
+ stat = fs6.statSync(filePath);
58945
59452
  } catch {
58946
59453
  continue;
58947
59454
  }
@@ -58992,14 +59499,14 @@ function parseEntry(line, lineIndex) {
58992
59499
  async function flushDeltaForFile2(filePath, fileState) {
58993
59500
  let stat;
58994
59501
  try {
58995
- stat = fs5.statSync(filePath);
59502
+ stat = fs6.statSync(filePath);
58996
59503
  } catch {
58997
59504
  return { inserted: 0, skipped: 0, dedup: 0 };
58998
59505
  }
58999
59506
  if (stat.size === fileState.lastSize) return { inserted: 0, skipped: 0, dedup: 0 };
59000
59507
  let content;
59001
59508
  try {
59002
- content = fs5.readFileSync(filePath, "utf-8");
59509
+ content = fs6.readFileSync(filePath, "utf-8");
59003
59510
  } catch {
59004
59511
  return { inserted: 0, skipped: 0, dedup: 0 };
59005
59512
  }
@@ -59046,7 +59553,7 @@ async function flushAllFiles2() {
59046
59553
  if (!fileState) {
59047
59554
  let stat = null;
59048
59555
  try {
59049
- stat = fs5.statSync(filePath);
59556
+ stat = fs6.statSync(filePath);
59050
59557
  } catch {
59051
59558
  }
59052
59559
  const fileBirthMs = stat ? stat.birthtimeMs || stat.ctimeMs : SERVER_START_MS;
@@ -59106,7 +59613,7 @@ function armDirWatcher() {
59106
59613
  if (!_state3 || !_state3.rootExists) return;
59107
59614
  if (_watcher) return;
59108
59615
  try {
59109
- _watcher = fs5.watch(SESSIONS_ROOT, { recursive: true }, (_evt, filename) => {
59616
+ _watcher = fs6.watch(SESSIONS_ROOT, { recursive: true }, (_evt, filename) => {
59110
59617
  if (!filename) return;
59111
59618
  if (path6.basename(filename) !== CHAT_FILE) return;
59112
59619
  scheduleFlush2();
@@ -59161,7 +59668,9 @@ function isCaptureArmed3() {
59161
59668
  }
59162
59669
 
59163
59670
  // src/auto_save/antigravity_capture.ts
59164
- import * as fs6 from "node:fs";
59671
+ init_hot_path();
59672
+ init_users();
59673
+ import * as fs7 from "node:fs";
59165
59674
  import * as path7 from "node:path";
59166
59675
  import * as os8 from "node:os";
59167
59676
  var DEVICE_NAME5 = os8.hostname();
@@ -59186,7 +59695,7 @@ function extractSid(filePath) {
59186
59695
  }
59187
59696
  function countCompleteLines2(filePath) {
59188
59697
  try {
59189
- const content = fs6.readFileSync(filePath, "utf-8");
59698
+ const content = fs7.readFileSync(filePath, "utf-8");
59190
59699
  let n = 0;
59191
59700
  for (let i = 0; i < content.length; i++) if (content[i] === "\n") n++;
59192
59701
  return n;
@@ -59195,13 +59704,13 @@ function countCompleteLines2(filePath) {
59195
59704
  }
59196
59705
  }
59197
59706
  function* walkTranscripts(root) {
59198
- if (!fs6.existsSync(root)) return;
59707
+ if (!fs7.existsSync(root)) return;
59199
59708
  const stack = [root];
59200
59709
  while (stack.length) {
59201
59710
  const dir = stack.pop();
59202
59711
  let entries;
59203
59712
  try {
59204
- entries = fs6.readdirSync(dir, { withFileTypes: true });
59713
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
59205
59714
  } catch {
59206
59715
  continue;
59207
59716
  }
@@ -59223,7 +59732,7 @@ function* walkAllTranscripts() {
59223
59732
  }
59224
59733
  }
59225
59734
  function captureSessionStart4(_cwd) {
59226
- const rootExists = ROOTS.some((r) => fs6.existsSync(r.dir));
59735
+ const rootExists = ROOTS.some((r) => fs7.existsSync(r.dir));
59227
59736
  _state4 = {
59228
59737
  rootExists,
59229
59738
  files: /* @__PURE__ */ new Map()
@@ -59235,7 +59744,7 @@ function captureSessionStart4(_cwd) {
59235
59744
  for (const { filePath, platform } of walkAllTranscripts()) {
59236
59745
  let stat;
59237
59746
  try {
59238
- stat = fs6.statSync(filePath);
59747
+ stat = fs7.statSync(filePath);
59239
59748
  } catch {
59240
59749
  continue;
59241
59750
  }
@@ -59248,7 +59757,7 @@ function captureSessionStart4(_cwd) {
59248
59757
  });
59249
59758
  count++;
59250
59759
  }
59251
- const liveRoots = ROOTS.filter((r) => fs6.existsSync(r.dir)).map((r) => r.platform);
59760
+ const liveRoots = ROOTS.filter((r) => fs7.existsSync(r.dir)).map((r) => r.platform);
59252
59761
  console.error(`\u{1F4DD} [Antigravity] capture armed: ${count} transcript(s) across [${liveRoots.join(", ")}]`);
59253
59762
  armDirWatcher2();
59254
59763
  }
@@ -59286,14 +59795,14 @@ function parseEntry2(line) {
59286
59795
  async function flushDeltaForFile3(filePath, fileState) {
59287
59796
  let stat;
59288
59797
  try {
59289
- stat = fs6.statSync(filePath);
59798
+ stat = fs7.statSync(filePath);
59290
59799
  } catch {
59291
59800
  return { inserted: 0, skipped: 0, dedup: 0 };
59292
59801
  }
59293
59802
  if (stat.size === fileState.lastSize) return { inserted: 0, skipped: 0, dedup: 0 };
59294
59803
  let content;
59295
59804
  try {
59296
- content = fs6.readFileSync(filePath, "utf-8");
59805
+ content = fs7.readFileSync(filePath, "utf-8");
59297
59806
  } catch {
59298
59807
  return { inserted: 0, skipped: 0, dedup: 0 };
59299
59808
  }
@@ -59342,7 +59851,7 @@ async function flushAllFiles3() {
59342
59851
  if (!fileState) {
59343
59852
  let stat = null;
59344
59853
  try {
59345
- stat = fs6.statSync(filePath);
59854
+ stat = fs7.statSync(filePath);
59346
59855
  } catch {
59347
59856
  }
59348
59857
  const fileBirthMs = stat ? stat.birthtimeMs || stat.ctimeMs : SERVER_START_MS2;
@@ -59404,9 +59913,9 @@ function armDirWatcher2() {
59404
59913
  if (!_state4 || !_state4.rootExists) return;
59405
59914
  if (_watchers2.length) return;
59406
59915
  for (const { dir } of ROOTS) {
59407
- if (!fs6.existsSync(dir)) continue;
59916
+ if (!fs7.existsSync(dir)) continue;
59408
59917
  try {
59409
- const w = fs6.watch(dir, { recursive: true }, (_evt, filename) => {
59918
+ const w = fs7.watch(dir, { recursive: true }, (_evt, filename) => {
59410
59919
  if (!filename) return;
59411
59920
  if (path7.basename(filename) !== CHAT_FILE2) return;
59412
59921
  scheduleFlush3();
@@ -59463,7 +59972,10 @@ function isCaptureArmed4() {
59463
59972
  }
59464
59973
 
59465
59974
  // src/auto_save/hermes_capture.ts
59466
- import * as fs7 from "node:fs";
59975
+ init_hot_path();
59976
+ init_venue();
59977
+ init_users();
59978
+ import * as fs8 from "node:fs";
59467
59979
  import * as path8 from "node:path";
59468
59980
  import * as os9 from "node:os";
59469
59981
  var DEVICE_NAME6 = os9.hostname();
@@ -59519,8 +60031,44 @@ function parseNewRows(db2, afterId) {
59519
60031
  }
59520
60032
  return { rows, maxId };
59521
60033
  }
59522
- function lookupModel(db2, sessionId, cache2) {
59523
- if (cache2.has(sessionId)) return cache2.get(sessionId);
60034
+ function lookupSource(db2, sessionId, cache3) {
60035
+ if (cache3.has(sessionId)) return cache3.get(sessionId);
60036
+ let source = null;
60037
+ try {
60038
+ const row = db2.prepare(`SELECT source FROM sessions WHERE id = ? LIMIT 1`).get(sessionId);
60039
+ source = row?.source ?? null;
60040
+ } catch {
60041
+ source = null;
60042
+ }
60043
+ cache3.set(sessionId, source);
60044
+ return source;
60045
+ }
60046
+ var SOURCE_VENUE = { slack: "slack", cli: "terminal", subagent: "subagent", cron: "auto" };
60047
+ function rowVenue(db2, row, state) {
60048
+ const source = lookupSource(db2, row.sessionId, state.sourceCache);
60049
+ if (source !== "acp") return source ? SOURCE_VENUE[source] ?? null : null;
60050
+ const own = row.role === "user" ? buzzTurnVenue(row.message) : null;
60051
+ if (own) {
60052
+ state.buzzVenue.set(row.sessionId, own);
60053
+ return own;
60054
+ }
60055
+ if (!state.buzzVenue.has(row.sessionId)) {
60056
+ let seeded = null;
60057
+ try {
60058
+ const prev = db2.prepare(`SELECT content FROM messages WHERE session_id = ? AND id < ? AND role = 'user' ORDER BY id DESC LIMIT 20`).all(row.sessionId, row.id);
60059
+ for (const p of prev) {
60060
+ seeded = p.content ? buzzTurnVenue(p.content) : null;
60061
+ if (seeded) break;
60062
+ }
60063
+ } catch {
60064
+ seeded = null;
60065
+ }
60066
+ state.buzzVenue.set(row.sessionId, seeded);
60067
+ }
60068
+ return state.buzzVenue.get(row.sessionId) ?? null;
60069
+ }
60070
+ function lookupModel(db2, sessionId, cache3) {
60071
+ if (cache3.has(sessionId)) return cache3.get(sessionId);
59524
60072
  let model = null;
59525
60073
  try {
59526
60074
  const row = db2.prepare(`SELECT model FROM sessions WHERE id = ? LIMIT 1`).get(sessionId);
@@ -59528,7 +60076,7 @@ function lookupModel(db2, sessionId, cache2) {
59528
60076
  } catch {
59529
60077
  model = null;
59530
60078
  }
59531
- cache2.set(sessionId, model);
60079
+ cache3.set(sessionId, model);
59532
60080
  return model;
59533
60081
  }
59534
60082
  function isCaptureArmed5() {
@@ -59541,7 +60089,7 @@ async function captureSessionStart5(_cwd) {
59541
60089
  }
59542
60090
  _state5 = null;
59543
60091
  _consecutiveOpenFailures = 0;
59544
- if (!fs7.existsSync(STATE_DB)) return;
60092
+ if (!fs8.existsSync(STATE_DB)) return;
59545
60093
  const DatabaseSync = await loadSqlite2();
59546
60094
  if (!DatabaseSync) {
59547
60095
  console.error(
@@ -59573,7 +60121,7 @@ async function captureSessionStart5(_cwd) {
59573
60121
  db2.close();
59574
60122
  } catch {
59575
60123
  }
59576
- _state5 = { cursor: maxId, modelCache: /* @__PURE__ */ new Map() };
60124
+ _state5 = { cursor: maxId, modelCache: /* @__PURE__ */ new Map(), sourceCache: /* @__PURE__ */ new Map(), buzzVenue: /* @__PURE__ */ new Map(), venueByRow: /* @__PURE__ */ new Map() };
59577
60125
  console.error(`\u{1F4DD} [Hermes] capture armed: cursor=${maxId} (live-from-now)`);
59578
60126
  if (_pollTimer5) clearInterval(_pollTimer5);
59579
60127
  _pollTimer5 = setInterval(() => {
@@ -59612,6 +60160,15 @@ async function flush2() {
59612
60160
  const userId = await getDefaultUserId();
59613
60161
  for (const r of rows) {
59614
60162
  const agentModel = r.role === "user" ? null : lookupModel(db2, r.sessionId, _state5.modelCache) ?? "unknown";
60163
+ let venue = _state5.venueByRow.get(r.id);
60164
+ if (venue === void 0) {
60165
+ try {
60166
+ venue = rowVenue(db2, r, _state5);
60167
+ } catch {
60168
+ venue = null;
60169
+ }
60170
+ _state5.venueByRow.set(r.id, venue);
60171
+ }
59615
60172
  try {
59616
60173
  const res = await insertRawMemory({
59617
60174
  user_id: userId,
@@ -59620,8 +60177,10 @@ async function flush2() {
59620
60177
  role: r.role,
59621
60178
  message: r.message,
59622
60179
  external_uuid: `hermes:${r.id}`,
59623
- device_name: DEVICE_NAME6
60180
+ device_name: DEVICE_NAME6,
60181
+ venue
59624
60182
  });
60183
+ _state5.venueByRow.delete(r.id);
59625
60184
  if (res.inserted) inserted++;
59626
60185
  else dedup++;
59627
60186
  } catch (err3) {
@@ -59786,6 +60345,7 @@ subagent \uCEE8\uD14D\uC2A4\uD2B8\uB77C\uBA74 subagent=true + subagent_model + s
59786
60345
 
59787
60346
  // src/tools/task_pin.ts
59788
60347
  init_db();
60348
+ init_users();
59789
60349
  import * as os11 from "node:os";
59790
60350
  var DEVICE_NAME8 = os11.hostname();
59791
60351
  var STATUSES = ["pending", "in_progress", "done", "failed"];
@@ -59936,9 +60496,11 @@ function registerTools(server) {
59936
60496
 
59937
60497
  // src/cold_path/worker.ts
59938
60498
  init_db();
60499
+ init_buzz_envelope();
59939
60500
 
59940
60501
  // src/cold_path/dtag_promoter.ts
59941
60502
  init_db();
60503
+ init_users();
59942
60504
  function envInt2(name, fallback) {
59943
60505
  const raw = process.env[name];
59944
60506
  if (!raw) return fallback;
@@ -60087,6 +60649,7 @@ async function runDtagPromotion() {
60087
60649
 
60088
60650
  // src/librarian.ts
60089
60651
  init_db();
60652
+ init_users();
60090
60653
 
60091
60654
  // src/context_budget.ts
60092
60655
  var DEFAULT_CTX = 8192;
@@ -60704,7 +61267,10 @@ async function drainColdPath(maxTicks = 12) {
60704
61267
  }
60705
61268
 
60706
61269
  // src/auto_save/jsonl_capture.ts
60707
- import * as fs8 from "node:fs";
61270
+ init_hot_path();
61271
+ init_venue();
61272
+ init_users();
61273
+ import * as fs9 from "node:fs";
60708
61274
  import * as path9 from "node:path";
60709
61275
  import * as os12 from "node:os";
60710
61276
  var CLIENT_PLATFORM2 = "claude-code";
@@ -60723,12 +61289,12 @@ function findProjectDir(cwd) {
60723
61289
  const slug = cwdToSlug(cwd);
60724
61290
  const slugPath = path9.join(PROJECTS_ROOT, slug);
60725
61291
  try {
60726
- if (fs8.statSync(slugPath).isDirectory()) return slugPath;
61292
+ if (fs9.statSync(slugPath).isDirectory()) return slugPath;
60727
61293
  } catch {
60728
61294
  }
60729
61295
  let subdirs;
60730
61296
  try {
60731
- subdirs = fs8.readdirSync(PROJECTS_ROOT, { withFileTypes: true });
61297
+ subdirs = fs9.readdirSync(PROJECTS_ROOT, { withFileTypes: true });
60732
61298
  } catch {
60733
61299
  return null;
60734
61300
  }
@@ -60737,7 +61303,7 @@ function findProjectDir(cwd) {
60737
61303
  const dirPath = path9.join(PROJECTS_ROOT, subdir.name);
60738
61304
  let files;
60739
61305
  try {
60740
- files = fs8.readdirSync(dirPath, { withFileTypes: true });
61306
+ files = fs9.readdirSync(dirPath, { withFileTypes: true });
60741
61307
  } catch {
60742
61308
  continue;
60743
61309
  }
@@ -60752,13 +61318,13 @@ function findProjectDir(cwd) {
60752
61318
  function readCwdFromJsonl(jsonlPath) {
60753
61319
  let fd;
60754
61320
  try {
60755
- fd = fs8.openSync(jsonlPath, "r");
61321
+ fd = fs9.openSync(jsonlPath, "r");
60756
61322
  } catch {
60757
61323
  return null;
60758
61324
  }
60759
61325
  try {
60760
61326
  const buf = Buffer.alloc(2048);
60761
- const bytesRead = fs8.readSync(fd, buf, 0, 2048, 0);
61327
+ const bytesRead = fs9.readSync(fd, buf, 0, 2048, 0);
60762
61328
  const raw = buf.slice(0, bytesRead).toString("utf-8");
60763
61329
  for (const line of raw.split("\n")) {
60764
61330
  if (!line.trim()) continue;
@@ -60770,7 +61336,7 @@ function readCwdFromJsonl(jsonlPath) {
60770
61336
  }
60771
61337
  return null;
60772
61338
  } finally {
60773
- fs8.closeSync(fd);
61339
+ fs9.closeSync(fd);
60774
61340
  }
60775
61341
  }
60776
61342
  function captureSessionStart6(cwd) {
@@ -60800,7 +61366,7 @@ function _tryArm(cwd, attempt) {
60800
61366
  _state6.projectDir = projectDir;
60801
61367
  let entries;
60802
61368
  try {
60803
- entries = fs8.readdirSync(projectDir, { withFileTypes: true });
61369
+ entries = fs9.readdirSync(projectDir, { withFileTypes: true });
60804
61370
  } catch {
60805
61371
  return;
60806
61372
  }
@@ -60809,7 +61375,7 @@ function _tryArm(cwd, attempt) {
60809
61375
  const jsonlPath = path9.join(projectDir, entry.name);
60810
61376
  let stat;
60811
61377
  try {
60812
- stat = fs8.statSync(jsonlPath);
61378
+ stat = fs9.statSync(jsonlPath);
60813
61379
  } catch {
60814
61380
  continue;
60815
61381
  }
@@ -60855,27 +61421,48 @@ function parseEntry3(line) {
60855
61421
  uuid: entry.uuid ?? `${entry.timestamp ?? ""}-${Math.random()}`,
60856
61422
  role: entry.type,
60857
61423
  message: messageText,
60858
- agent_model
61424
+ agent_model,
61425
+ entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : void 0
60859
61426
  };
60860
61427
  }
61428
+ var HEAD_SCAN_BYTES = 2 * 1024 * 1024;
61429
+ function sessionVenueKind(jsonlPath) {
61430
+ const fd = fs9.openSync(jsonlPath, "r");
61431
+ try {
61432
+ const size = fs9.fstatSync(fd).size;
61433
+ const buf = Buffer.allocUnsafe(Math.min(HEAD_SCAN_BYTES, size));
61434
+ fs9.readSync(fd, buf, 0, buf.length, 0);
61435
+ for (const line of buf.toString("utf-8").split("\n")) {
61436
+ const parsed = line.trim() ? parseEntry3(line) : null;
61437
+ if (parsed?.role === "user") return classifySessionVenue(parsed.message, parsed.entrypoint);
61438
+ }
61439
+ return size >= HEAD_SCAN_BYTES ? null : void 0;
61440
+ } finally {
61441
+ fs9.closeSync(fd);
61442
+ }
61443
+ }
61444
+ var userTextOf = (line) => {
61445
+ const parsed = parseEntry3(line);
61446
+ return parsed?.role === "user" ? parsed.message : null;
61447
+ };
60861
61448
  async function flushDeltaForFile4(jsonlPath, fileState) {
60862
- if (!fs8.existsSync(jsonlPath)) return { inserted: 0, skipped: 0, dedup: 0 };
61449
+ if (!fs9.existsSync(jsonlPath)) return { inserted: 0, skipped: 0, dedup: 0 };
60863
61450
  let stat;
60864
61451
  try {
60865
- stat = fs8.statSync(jsonlPath);
61452
+ stat = fs9.statSync(jsonlPath);
60866
61453
  } catch {
60867
61454
  return { inserted: 0, skipped: 0, dedup: 0 };
60868
61455
  }
60869
61456
  if (stat.size <= fileState.cursorBytes) return { inserted: 0, skipped: 0, dedup: 0 };
60870
61457
  const length = stat.size - fileState.cursorBytes;
60871
- const fd = fs8.openSync(jsonlPath, "r");
61458
+ const fd = fs9.openSync(jsonlPath, "r");
60872
61459
  let raw;
60873
61460
  try {
60874
61461
  const buf = Buffer.alloc(length);
60875
- fs8.readSync(fd, buf, 0, length, fileState.cursorBytes);
61462
+ fs9.readSync(fd, buf, 0, length, fileState.cursorBytes);
60876
61463
  raw = buf.toString("utf-8");
60877
61464
  } finally {
60878
- fs8.closeSync(fd);
61465
+ fs9.closeSync(fd);
60879
61466
  }
60880
61467
  const lastNl = raw.lastIndexOf("\n");
60881
61468
  if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
@@ -60884,6 +61471,14 @@ async function flushDeltaForFile4(jsonlPath, fileState) {
60884
61471
  const newCursor = fileState.cursorBytes + advanceBy;
60885
61472
  const lines = parsable.split("\n");
60886
61473
  const userId = await getDefaultUserId();
61474
+ if (fileState.venueKind === void 0) {
61475
+ try {
61476
+ fileState.venueKind = sessionVenueKind(jsonlPath);
61477
+ } catch {
61478
+ fileState.venueKind = null;
61479
+ }
61480
+ }
61481
+ const deltaStart = fileState.cursorBytes;
60887
61482
  let inserted = 0, skipped = 0, dedup = 0;
60888
61483
  for (const line of lines) {
60889
61484
  const trimmed = line.trim();
@@ -60900,6 +61495,20 @@ async function flushDeltaForFile4(jsonlPath, fileState) {
60900
61495
  }
60901
61496
  fileState.contentSeen.add(ck);
60902
61497
  const externalUuid = `claude-code:${fileState.sessionId}:${parsed.uuid}`;
61498
+ let venue = fileState.venueKind ?? null;
61499
+ if (fileState.venueKind === "buzz") {
61500
+ const own = parsed.role === "user" ? buzzTurnVenue(parsed.message) : null;
61501
+ if (own) {
61502
+ fileState.buzzVenue = own;
61503
+ } else if (fileState.buzzVenue === void 0) {
61504
+ try {
61505
+ fileState.buzzVenue = lastBuzzVenueInFile(jsonlPath, deltaStart, userTextOf);
61506
+ } catch {
61507
+ fileState.buzzVenue = null;
61508
+ }
61509
+ }
61510
+ venue = own ?? fileState.buzzVenue ?? "buzz";
61511
+ }
60903
61512
  const agentModel = parsed.role === "user" ? null : parsed.agent_model ?? "unknown";
60904
61513
  try {
60905
61514
  const result = await insertRawMemory({
@@ -60909,7 +61518,8 @@ async function flushDeltaForFile4(jsonlPath, fileState) {
60909
61518
  role: parsed.role,
60910
61519
  message: parsed.message,
60911
61520
  external_uuid: externalUuid,
60912
- device_name: DEVICE_NAME9
61521
+ device_name: DEVICE_NAME9,
61522
+ venue
60913
61523
  });
60914
61524
  if (result.inserted) inserted++;
60915
61525
  else dedup++;
@@ -60924,10 +61534,10 @@ async function flushDeltaForFile4(jsonlPath, fileState) {
60924
61534
  async function flushAllFiles4() {
60925
61535
  if (!_state6 || !_state6.projectDir) return { inserted: 0, skipped: 0, dedup: 0 };
60926
61536
  const projectDir = _state6.projectDir;
60927
- if (!fs8.existsSync(projectDir)) return { inserted: 0, skipped: 0, dedup: 0 };
61537
+ if (!fs9.existsSync(projectDir)) return { inserted: 0, skipped: 0, dedup: 0 };
60928
61538
  let entries;
60929
61539
  try {
60930
- entries = fs8.readdirSync(projectDir, { withFileTypes: true });
61540
+ entries = fs9.readdirSync(projectDir, { withFileTypes: true });
60931
61541
  } catch {
60932
61542
  return { inserted: 0, skipped: 0, dedup: 0 };
60933
61543
  }
@@ -60983,7 +61593,7 @@ function armDirWatcher3() {
60983
61593
  if (!_state6 || !_state6.projectDir) return;
60984
61594
  if (_watcher2) return;
60985
61595
  try {
60986
- _watcher2 = fs8.watch(_state6.projectDir, (_eventType, filename) => {
61596
+ _watcher2 = fs9.watch(_state6.projectDir, (_eventType, filename) => {
60987
61597
  if (filename && !filename.endsWith(".jsonl")) return;
60988
61598
  scheduleFlush4();
60989
61599
  });
@@ -61027,7 +61637,9 @@ async function captureSessionEnd6() {
61027
61637
  }
61028
61638
 
61029
61639
  // src/auto_save/codex_capture.ts
61030
- import * as fs9 from "node:fs";
61640
+ init_hot_path();
61641
+ init_users();
61642
+ import * as fs10 from "node:fs";
61031
61643
  import * as path10 from "node:path";
61032
61644
  import * as os13 from "node:os";
61033
61645
  import { spawnSync } from "node:child_process";
@@ -61075,13 +61687,13 @@ function extractSessionId2(filename) {
61075
61687
  return m ? m[1] : null;
61076
61688
  }
61077
61689
  function* walkRollouts(root) {
61078
- if (!fs9.existsSync(root)) return;
61690
+ if (!fs10.existsSync(root)) return;
61079
61691
  const stack = [root];
61080
61692
  while (stack.length) {
61081
61693
  const dir = stack.pop();
61082
61694
  let entries;
61083
61695
  try {
61084
- entries = fs9.readdirSync(dir, { withFileTypes: true });
61696
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
61085
61697
  } catch {
61086
61698
  continue;
61087
61699
  }
@@ -61096,7 +61708,7 @@ function* walkRollouts(root) {
61096
61708
  }
61097
61709
  }
61098
61710
  function captureSessionStart7(_cwd) {
61099
- const rootExists = fs9.existsSync(SESSIONS_ROOT2);
61711
+ const rootExists = fs10.existsSync(SESSIONS_ROOT2);
61100
61712
  _state7 = {
61101
61713
  rootExists,
61102
61714
  files: /* @__PURE__ */ new Map()
@@ -61108,7 +61720,7 @@ function captureSessionStart7(_cwd) {
61108
61720
  for (const filePath of walkRollouts(SESSIONS_ROOT2)) {
61109
61721
  let stat;
61110
61722
  try {
61111
- stat = fs9.statSync(filePath);
61723
+ stat = fs10.statSync(filePath);
61112
61724
  } catch {
61113
61725
  continue;
61114
61726
  }
@@ -61162,23 +61774,23 @@ function extractModelFromTurnContext(line) {
61162
61774
  }
61163
61775
  }
61164
61776
  async function flushDeltaForFile5(filePath, fileState) {
61165
- if (!fs9.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
61777
+ if (!fs10.existsSync(filePath)) return { inserted: 0, skipped: 0, dedup: 0 };
61166
61778
  let stat;
61167
61779
  try {
61168
- stat = fs9.statSync(filePath);
61780
+ stat = fs10.statSync(filePath);
61169
61781
  } catch {
61170
61782
  return { inserted: 0, skipped: 0, dedup: 0 };
61171
61783
  }
61172
61784
  if (stat.size <= fileState.cursorBytes) return { inserted: 0, skipped: 0, dedup: 0 };
61173
61785
  const length = stat.size - fileState.cursorBytes;
61174
- const fd = fs9.openSync(filePath, "r");
61786
+ const fd = fs10.openSync(filePath, "r");
61175
61787
  let raw;
61176
61788
  try {
61177
61789
  const buf = Buffer.alloc(length);
61178
- fs9.readSync(fd, buf, 0, length, fileState.cursorBytes);
61790
+ fs10.readSync(fd, buf, 0, length, fileState.cursorBytes);
61179
61791
  raw = buf.toString("utf-8");
61180
61792
  } finally {
61181
- fs9.closeSync(fd);
61793
+ fs10.closeSync(fd);
61182
61794
  }
61183
61795
  const lastNl = raw.lastIndexOf("\n");
61184
61796
  if (lastNl === -1) return { inserted: 0, skipped: 0, dedup: 0 };
@@ -61242,7 +61854,7 @@ async function flushAllFiles5() {
61242
61854
  if (!sessionId) continue;
61243
61855
  let stat = null;
61244
61856
  try {
61245
- stat = fs9.statSync(filePath);
61857
+ stat = fs10.statSync(filePath);
61246
61858
  } catch {
61247
61859
  }
61248
61860
  const fileBirthMs = stat ? stat.birthtimeMs || stat.ctimeMs : SERVER_START_MS3;
@@ -61302,7 +61914,7 @@ function armDirWatcher4() {
61302
61914
  if (!_state7 || !_state7.rootExists) return;
61303
61915
  if (_watcher3) return;
61304
61916
  try {
61305
- _watcher3 = fs9.watch(SESSIONS_ROOT2, { recursive: true }, (_evt, filename) => {
61917
+ _watcher3 = fs10.watch(SESSIONS_ROOT2, { recursive: true }, (_evt, filename) => {
61306
61918
  if (!filename) return;
61307
61919
  const base = path10.basename(filename);
61308
61920
  if (!base.startsWith("rollout-") || !base.endsWith(".jsonl")) return;
@@ -61355,16 +61967,16 @@ async function captureSessionEnd7() {
61355
61967
  }
61356
61968
 
61357
61969
  // src/version.ts
61358
- import fs10 from "fs";
61970
+ import fs11 from "fs";
61359
61971
  import path11 from "path";
61360
61972
  import { fileURLToPath as fileURLToPath3 } from "url";
61361
61973
  var __dirname3 = path11.dirname(fileURLToPath3(import.meta.url));
61362
61974
  var packageJsonPath = path11.resolve(__dirname3, "..", "package.json");
61363
- var packageJson = JSON.parse(fs10.readFileSync(packageJsonPath, "utf8"));
61975
+ var packageJson = JSON.parse(fs11.readFileSync(packageJsonPath, "utf8"));
61364
61976
  var PACKAGE_VERSION2 = packageJson.version || "0.0.0";
61365
61977
 
61366
61978
  // src/index.ts
61367
- import fs12 from "fs";
61979
+ import fs13 from "fs";
61368
61980
  var BRIEF_DB_TIMEOUT_MS = 5e3;
61369
61981
  var INSTRUCTIONS_MAX_CHARS = Number(process.env.INSTRUCTIONS_MAX_CHARS ?? 1900);
61370
61982
  var INSTRUCTIONS_SEP = "\n\n---\n\n";
@@ -61523,6 +62135,12 @@ Usage:
61523
62135
  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.
61524
62136
  mcp-agents-memory setup Interactive setup \u2014 write config to ~/.config/mcp-agents-memory/.env and run migrations.
61525
62137
  mcp-agents-memory migrate Apply any pending DB migrations against the configured database.
62138
+ mcp-agents-memory buzz-ingest [--dry-run] [--max N]
62139
+ (Buzz only) Copy chat messages from the Buzz rooms the CLI identity is a member of
62140
+ into memory, one row per message, via the official \`buzz\` CLI. Oldest first, at most
62141
+ N per run (default 20). Needs BUZZ_RELAY_URL, BUZZ_PRIVATE_KEY and BUZZ_INGEST_OWNER
62142
+ (the memory owner's pubkeys, comma-separated) in the environment of this command only \u2014
62143
+ not in the shared .env; optional BUZZ_CLI.
61526
62144
  mcp-agents-memory help Show this message.
61527
62145
 
61528
62146
  Configuration is loaded from (first hit wins):
@@ -61561,7 +62179,7 @@ async function runMcpServer() {
61561
62179
  if (process.env.SSH_ENABLED === "true" && !process.env.SSH_KEY_PATH) {
61562
62180
  throw new Error("\u274C SSH_KEY_PATH is required when SSH is enabled");
61563
62181
  }
61564
- if (process.env.SSH_KEY_PATH && !fs12.existsSync(process.env.SSH_KEY_PATH)) {
62182
+ if (process.env.SSH_KEY_PATH && !fs13.existsSync(process.env.SSH_KEY_PATH)) {
61565
62183
  console.error(`\u26A0\uFE0F [WARNING] SSH key not found at: ${process.env.SSH_KEY_PATH}`);
61566
62184
  }
61567
62185
  try {
@@ -61615,6 +62233,15 @@ async function cli() {
61615
62233
  await runSetupWizard2();
61616
62234
  process.exit(0);
61617
62235
  }
62236
+ if (cmd === "buzz-ingest") {
62237
+ const { runBuzzIngest: runBuzzIngest2, parseIngestArgs: parseIngestArgs2 } = await Promise.resolve().then(() => (init_buzz_ingest(), buzz_ingest_exports));
62238
+ const opts = parseIngestArgs2(process.argv.slice(3));
62239
+ await db.connect();
62240
+ const report = await runBuzzIngest2(opts);
62241
+ console.log(JSON.stringify({ dryRun: opts.dryRun ?? false, ...report }));
62242
+ await db.close();
62243
+ process.exit(0);
62244
+ }
61618
62245
  if (cmd === "migrate") {
61619
62246
  const { runAllMigrations: runAllMigrations2 } = await Promise.resolve().then(() => (init_runner(), runner_exports));
61620
62247
  await runAllMigrations2();