chat-recall 0.7.11 → 0.7.13

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/dist/derived.js CHANGED
@@ -22,7 +22,7 @@
22
22
  import { withSessionReadCache } from '@chat-recall/engine/core/live-session-scan.js';
23
23
  import { replaySessionAny, extractTurnsAny } from '@chat-recall/engine/core/session-multi-tool.js';
24
24
  import { computeOutcome } from '@chat-recall/engine/core/session-outcome.js';
25
- import { markersFromTurns } from '@chat-recall/engine/core/session-sentiment.js';
25
+ import { markersFromTurns, MARKERS_TURN_OPTS } from '@chat-recall/engine/core/session-sentiment.js';
26
26
  import { redactDeep } from './redact-deep.js';
27
27
  /** Which compute rows a session ships. */
28
28
  export const COMPUTE_KINDS = ['diff', 'outcome', 'commits', 'markers'];
@@ -57,7 +57,7 @@ export function collectDerivedRows(ref, mtime, count, maxRowBytes) {
57
57
  else if (kind === 'markers') {
58
58
  // Analysis-only use of the turns extractor (R4) — markers need
59
59
  // per-prompt line/ts, not render fidelity.
60
- const turns = extractTurnsAny(id, { maxTurns: 50_000 });
60
+ const turns = extractTurnsAny(id, MARKERS_TURN_OPTS);
61
61
  data = markersFromTurns(id, turns.turns);
62
62
  }
63
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"derived.js","sourceRoot":"","sources":["../src/derived.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"derived.js","sourceRoot":"","sources":["../src/derived.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,0CAA0C;AAC1C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAU,CAAC;AAShF,MAAM,UAAU,kBAAkB,CAChC,GAA0D,EAC1D,KAAa,EACb,KAA6B,EAC7B,WAAmB;IAEnB,MAAM,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;IAC1B,MAAM,OAAO,GAA0D,EAAE,CAAC;IAC1E,IAAI,WAAW,GAA6C,IAAI,CAAC;IAEjE,6EAA6E;IAC7E,4EAA4E;IAC5E,0DAA0D;IAC1D,OAAO,oBAAoB,CAAC,GAAG,EAAE;QACjC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,IAAI,IAAI,GAAY,IAAI,CAAC;YACzB,IAAI,CAAC;gBACH,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACpB,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;oBACpC,IAAI,MAAM,CAAC,KAAK;wBAAE,IAAI,GAAG,MAAM,CAAC;gBAClC,CAAC;qBAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC9B,WAAW,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;oBACjC,IAAI,WAAW,CAAC,KAAK;wBAAE,IAAI,GAAG,WAAW,CAAC;gBAC5C,CAAC;qBAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC9B,iEAAiE;oBACjE,IAAI,CAAC,WAAW;wBAAE,WAAW,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;oBACnD,IAAI,WAAW,CAAC,KAAK;wBAAE,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC;gBACpD,CAAC;qBAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC9B,+DAA+D;oBAC/D,2CAA2C;oBAC3C,MAAM,KAAK,GAAG,eAAe,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAC;oBACrD,IAAI,GAAG,gBAAgB,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC3C,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC;YAAC,CAAC;YACxB,IAAI,IAAI,KAAK,IAAI;gBAAE,SAAS;YAC5B,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,WAAW;gBAAE,SAAS;YAC5D,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChD,CAAC;QAED,yEAAyE;QACzE,uDAAuD;QACvD,IAAI,UAAU,GAAmC,IAAI,CAAC;QACtD,IAAI,WAAW,EAAE,KAAK,EAAE,CAAC;YACvB,UAAU,GAAG;gBACX,SAAS,EAAE,EAAE;gBACb,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,MAAM,EAAE,WAAW,CAAC,MAAa;gBACjC,MAAM,EAAE,WAAW,CAAC,MAAM;gBAC1B,SAAS,EAAE,KAAK;gBAChB,QAAQ,EAAE,CAAC;gBACX,WAAW,EAAE,EAAE;gBACf,SAAS,EAAE,WAAW,CAAC,SAAS;gBAChC,UAAU,EAAE,WAAW,CAAC,eAAe;gBACvC,YAAY,EAAE,WAAW,CAAC,iBAAiB;gBAC3C,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,YAAY,IAAI,CAAC;gBAC/C,MAAM,EAAE,IAAI;gBACZ,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;gBACxB,iBAAiB,EAAE,CAAC;aACrB,CAAC;QACJ,CAAC;QAED,OAAO;YACL,UAAU,EAAE,EAAE;YACd,KAAK;YACL,OAAO;YACP,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,GAAG,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;SAC5F,CAAC;IACF,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/mcp-relay.js CHANGED
@@ -192,7 +192,7 @@ function bridge(input, output, sock, exit, opts = {}) {
192
192
  }
193
193
 
194
194
  // src/mcp-relay.ts
195
- var VERSION = true ? "0.7.11" : "0.0.0";
195
+ var VERSION = true ? "0.7.13" : "0.0.0";
196
196
  var SERVER_URL = new URL("./mcp.js", import.meta.url);
197
197
  var SERVER_PATH = fileURLToPath(SERVER_URL);
198
198
  var START_TIMEOUT_MS = 15e3;
package/dist/mcp.js CHANGED
@@ -5065,8 +5065,15 @@ var init_claude_prompt_origin = __esm({
5065
5065
  INJECTED_PREFIXES = [
5066
5066
  ["<task-notification", "task-notification"],
5067
5067
  ["<agent-message", "peer"],
5068
+ ["<cross-session-message", "peer"],
5069
+ ["[Cross-session delivery notice]", "peer"],
5068
5070
  ["Another Claude session sent a message:", "peer"],
5069
- ["Stop hook feedback:", "hook"]
5071
+ ["Stop hook feedback:", "hook"],
5072
+ ["<bash-stdout>", "command-output"],
5073
+ ["<bash-stderr>", "command-output"],
5074
+ ["<local-command-stdout>", "command-output"],
5075
+ ["<local-command-stderr>", "command-output"],
5076
+ ["<<autonomous-loop", "meta"]
5070
5077
  ];
5071
5078
  }
5072
5079
  });
@@ -6028,7 +6035,7 @@ function markersFromTurns(sessionId, turns) {
6028
6035
  const prompts = turns.filter((t) => t.kind === "user" && t.text).map((t) => ({ line: t.line, ts: t.ts, tsIso: t.tsIso, ...markPrompt(t.text) }));
6029
6036
  return { v: MARKERS_VERSION, sessionId, prompts, summary: summarizeMarkers(prompts) };
6030
6037
  }
6031
- var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION;
6038
+ var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION, MARKERS_TURN_OPTS;
6032
6039
  var init_session_sentiment = __esm({
6033
6040
  "../engine/dist/core/session-sentiment.js"() {
6034
6041
  "use strict";
@@ -6041,7 +6048,8 @@ var init_session_sentiment = __esm({
6041
6048
  DIRECTIVE_HEAD = /^(?:please\s+)?(?:use|implement|build|make|add|create|fix|remove|delete|update|refactor|run|deploy|push|merge|test|check|investigate|find|search|show|list|generate|set\s+up|ensure)\b/i;
6042
6049
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
6043
6050
  FRUSTRATED_PHRASES = /\b(?:why (?:the )?(?:fuck|hell)|wtf|are you (?:kidding|serious)|stop slacking|stop making|why did you|why didn'?t you|i (?:fucking )?(?:asked|told you)|are you (?:listening|even)|come\s*on|just\s+do)\b/i;
6044
- MARKERS_VERSION = 2;
6051
+ MARKERS_VERSION = 3;
6052
+ MARKERS_TURN_OPTS = { maxTurns: 5e4, userMax: Number.POSITIVE_INFINITY };
6045
6053
  }
6046
6054
  });
6047
6055
 
@@ -6267,6 +6275,7 @@ function shorten(text, max) {
6267
6275
  function extractTurnsFromEvents(sessionId, events, opts = {}) {
6268
6276
  const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
6269
6277
  const assistantMax = opts.assistantMax ?? DEFAULT_ASSISTANT_MAX;
6278
+ const userMax = opts.userMax ?? DEFAULT_USER_MAX;
6270
6279
  const turns = [];
6271
6280
  let startMs = 0, endMs = 0;
6272
6281
  let found = false;
@@ -6284,7 +6293,7 @@ function extractTurnsFromEvents(sessionId, events, opts = {}) {
6284
6293
  ts: e.ts,
6285
6294
  tsIso: e.tsIso,
6286
6295
  line: e.line,
6287
- text: shorten(e.text, DEFAULT_USER_MAX)
6296
+ text: shorten(e.text, userMax)
6288
6297
  });
6289
6298
  } else if (e.kind === "assistant_text" && e.text) {
6290
6299
  turns.push({
@@ -9648,7 +9657,7 @@ function extractTurnsAny(sessionId, opts = {}) {
9648
9657
  const backend = getBackendForId(sessionId);
9649
9658
  if (!backend)
9650
9659
  return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
9651
- return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
9660
+ return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
9652
9661
  }
9653
9662
  function replaySessionAny(sessionId) {
9654
9663
  const backend = getBackendForId(sessionId);
@@ -11619,7 +11628,11 @@ __export(raw_exports, {
11619
11628
  gzipContainer: () => gzipContainer,
11620
11629
  mapContainerText: () => mapContainerText,
11621
11630
  parseOpenCodeDumpText: () => parseOpenCodeDumpText,
11622
- parseTranscriptFromContainer: () => parseTranscriptFromContainer
11631
+ parseTranscriptFromContainer: () => parseTranscriptFromContainer,
11632
+ redactContainer: () => redactContainer,
11633
+ redactJsonLine: () => redactJsonLine,
11634
+ repairContainer: () => repairContainer,
11635
+ repairRedactedJsonl: () => repairRedactedJsonl
11623
11636
  });
11624
11637
  import { gzipSync, gunzipSync } from "zlib";
11625
11638
  import { createHash as createHash3 } from "crypto";
@@ -11660,6 +11673,134 @@ function gunzipContainer(gz) {
11660
11673
  function mapContainerText(c, fn) {
11661
11674
  return { ...c, files: c.files.map((f) => ({ name: f.name, text: fn(f.text) })) };
11662
11675
  }
11676
+ function repairRedactedJsonl(text) {
11677
+ let repaired = 0;
11678
+ const parts = [];
11679
+ let copied = 0;
11680
+ let at = text.indexOf("[REDACTED:");
11681
+ while (at >= 0) {
11682
+ const start = text.lastIndexOf("\n", at) + 1;
11683
+ let end = text.indexOf("\n", at);
11684
+ if (end < 0)
11685
+ end = text.length;
11686
+ const fixed = repairLine(text.slice(start, end));
11687
+ if (fixed !== null) {
11688
+ parts.push(text.slice(copied, start), fixed);
11689
+ copied = end;
11690
+ repaired++;
11691
+ }
11692
+ at = text.indexOf("[REDACTED:", end);
11693
+ }
11694
+ if (repaired === 0)
11695
+ return { text, repaired: 0 };
11696
+ parts.push(text.slice(copied));
11697
+ return { text: parts.join(""), repaired };
11698
+ }
11699
+ function parseError(line) {
11700
+ try {
11701
+ JSON.parse(line);
11702
+ return null;
11703
+ } catch (e) {
11704
+ const m = /position (\d+)/.exec(e instanceof Error ? e.message : "");
11705
+ return m ? Number(m[1]) : line.length;
11706
+ }
11707
+ }
11708
+ function repairLine(line) {
11709
+ if (!line.includes("[REDACTED:") || parseError(line) === null)
11710
+ return null;
11711
+ let out = line.replace(/(?<!\\)((?:\\\\)*)\\(\[REDACTED:)/g, "$1$2");
11712
+ for (let attempt = 0; attempt < 64; attempt++) {
11713
+ const at = parseError(out);
11714
+ if (at === null)
11715
+ return out;
11716
+ let site = -1;
11717
+ for (const m of out.matchAll(MARKER_QUOTE)) {
11718
+ const q = m.index + m[0].length - 1;
11719
+ if (q < at)
11720
+ site = q;
11721
+ }
11722
+ if (site < 0)
11723
+ return null;
11724
+ out = out.slice(0, site) + "\\" + out.slice(site);
11725
+ }
11726
+ return null;
11727
+ }
11728
+ function redactJsonLine(line, redact) {
11729
+ if (!line.includes('"'))
11730
+ return redact(line);
11731
+ try {
11732
+ JSON.parse(line);
11733
+ } catch {
11734
+ return redact(line);
11735
+ }
11736
+ const decoded = mapJsonStrings(line, redact, true);
11737
+ const out = redact(decoded);
11738
+ try {
11739
+ JSON.parse(out);
11740
+ return out;
11741
+ } catch {
11742
+ return mapJsonStrings(decoded, redact, false);
11743
+ }
11744
+ }
11745
+ function mapJsonStrings(line, fn, escapedOnly) {
11746
+ let out = "";
11747
+ let i = 0;
11748
+ for (; ; ) {
11749
+ const q = line.indexOf('"', i);
11750
+ if (q < 0)
11751
+ return out + line.slice(i);
11752
+ let j = q + 1;
11753
+ while (j < line.length) {
11754
+ const c = line.charCodeAt(j);
11755
+ if (c === 92)
11756
+ j += 2;
11757
+ else if (c === 34)
11758
+ break;
11759
+ else
11760
+ j++;
11761
+ }
11762
+ const token = line.slice(q, j + 1);
11763
+ out += line.slice(i, q);
11764
+ i = j + 1;
11765
+ const body2 = token.slice(1, -1);
11766
+ const escaped = body2.includes("\\");
11767
+ if (escapedOnly && !escaped) {
11768
+ out += token;
11769
+ continue;
11770
+ }
11771
+ let value = body2;
11772
+ if (escaped) {
11773
+ try {
11774
+ value = JSON.parse(token);
11775
+ } catch {
11776
+ out += token;
11777
+ continue;
11778
+ }
11779
+ }
11780
+ const next = fn(value);
11781
+ out += next === value ? token : JSON.stringify(next);
11782
+ }
11783
+ }
11784
+ function redactContainer(c, redact) {
11785
+ return {
11786
+ ...c,
11787
+ files: c.files.map((f) => ({
11788
+ name: f.name,
11789
+ text: f.name.endsWith(".jsonl") ? f.text.split("\n").map((l) => l ? redactJsonLine(l, redact) : l).join("\n") : redact(f.text)
11790
+ }))
11791
+ };
11792
+ }
11793
+ function repairContainer(c) {
11794
+ let repaired = 0;
11795
+ const files = c.files.map((f) => {
11796
+ if (!f.name.endsWith(".jsonl"))
11797
+ return f;
11798
+ const r = repairRedactedJsonl(f.text);
11799
+ repaired += r.repaired;
11800
+ return r.repaired > 0 ? { name: f.name, text: r.text } : f;
11801
+ });
11802
+ return repaired > 0 ? { container: { ...c, files }, repaired } : { container: c, repaired: 0 };
11803
+ }
11663
11804
  function subagentKind(id) {
11664
11805
  return id.includes("acompact") ? "compact" : id.includes("aside_question") ? "aside" : id.includes("explore") || /^agent-a[0-9a-f]{16,}$/i.test(id) ? "explore" : "other";
11665
11806
  }
@@ -11830,6 +11971,7 @@ function requireFs() {
11830
11971
  function requirePath() {
11831
11972
  return pathMod;
11832
11973
  }
11974
+ var MARKER_QUOTE;
11833
11975
  var init_raw = __esm({
11834
11976
  "../engine/dist/transcript/raw.js"() {
11835
11977
  "use strict";
@@ -11837,6 +11979,7 @@ var init_raw = __esm({
11837
11979
  init_claude2();
11838
11980
  init_codex2();
11839
11981
  init_from_events();
11982
+ MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
11840
11983
  }
11841
11984
  });
11842
11985
 
@@ -12797,9 +12940,9 @@ function judgeHealth(h, now = Date.now()) {
12797
12940
  return (a.t.lastOkAt ?? 0) - (b.t.lastOkAt ?? 0);
12798
12941
  });
12799
12942
  const worst = ranked[0];
12800
- const when = worst.t.lastOkAt ? `${ago(now - worst.t.lastOkAt)} ago` : "never";
12943
+ const when2 = worst.t.lastOkAt ? `${ago(now - worst.t.lastOkAt)} ago` : "never";
12801
12944
  const why = worst.t.lastError ? ` (${worst.t.lastError})` : "";
12802
- reasons.push(`nothing has synced since ${when}${why}`);
12945
+ reasons.push(`nothing has synced since ${when2}${why}`);
12803
12946
  }
12804
12947
  if (reasons.length === 0)
12805
12948
  return { ok: true, summary: null, reasons: [] };
@@ -13173,7 +13316,13 @@ function redactSecrets(text, opts = {}) {
13173
13316
  return whole;
13174
13317
  if (opts.count)
13175
13318
  opts.count.redactions++;
13176
- return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
13319
+ let last = args.length - 1;
13320
+ if (typeof args[last] === "object")
13321
+ last--;
13322
+ const source = args[last];
13323
+ const secretStart = args[last - 1] + whole.length - secret.length;
13324
+ const head = escapeHead(source, secretStart, secret.length);
13325
+ return whole.slice(0, whole.length - secret.length + head) + `[REDACTED:${r.label}]` + escapeKept(whole, whole.length);
13177
13326
  });
13178
13327
  }
13179
13328
  const ctx = findContextualSecrets(out);
@@ -13181,10 +13330,25 @@ function redactSecrets(text, opts = {}) {
13181
13330
  const { start, end } = ctx[i];
13182
13331
  if (opts.count)
13183
13332
  opts.count.redactions++;
13184
- out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
13333
+ const head = escapeHead(out, start, end - start);
13334
+ out = out.slice(0, start + head) + "[REDACTED:secret-context]" + escapeKept(out, end) + out.slice(end);
13185
13335
  }
13186
13336
  return out;
13187
13337
  }
13338
+ function escapeKept(text, end) {
13339
+ let run = 0;
13340
+ while (run < end && text.charCodeAt(end - 1 - run) === 92)
13341
+ run++;
13342
+ return run % 2 === 1 ? "\\" : "";
13343
+ }
13344
+ function escapeHead(text, start, length) {
13345
+ let run = 0;
13346
+ while (run < start && text.charCodeAt(start - 1 - run) === 92)
13347
+ run++;
13348
+ if (run % 2 === 0)
13349
+ return 0;
13350
+ return Math.min(text[start] === "u" ? 5 : 1, length);
13351
+ }
13188
13352
  function maskSecret(s) {
13189
13353
  if (!s)
13190
13354
  return "";
@@ -13291,7 +13455,12 @@ var init_secret_redactor = __esm({
13291
13455
  // userinfo URL walked through in cleartext — and a `jdbc:postgresql://…`
13292
13456
  // prefix broke the \b anchor for the one family it did cover. Any scheme
13293
13457
  // followed by userinfo is a credential, so match the shape, not a list.
13294
- { label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/]{1,64}:([^@\s/]{1,256})(?=@)/g },
13458
+ //
13459
+ // A quote in userinfo is percent-encoded, so neither part takes a `"`. The
13460
+ // sync redacts JSONL text, and a match that ran on through `","is_error":`
13461
+ // to a later `@` replaced JSON structure: 4 lines on one machine stopped
13462
+ // parsing, and their records were dropped.
13463
+ { label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/"]{1,64}:([^@\s/"]{1,256})(?=@)/g },
13295
13464
  // Bare AWS secret-access-key VALUE: EXACTLY 40 base64 chars, word-bounded,
13296
13465
  // containing upper+lower+digit. This is the gitleaks heuristic — the mixed-case
13297
13466
  // requirement excludes 40-char lowercase-hex git SHAs, and the exact-40 bound
@@ -19677,7 +19846,7 @@ var init_http = __esm({
19677
19846
  CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19678
19847
  } catch {
19679
19848
  try {
19680
- if (true) CLI_VERSION = "0.7.11";
19849
+ if (true) CLI_VERSION = "0.7.13";
19681
19850
  } catch {
19682
19851
  }
19683
19852
  }
@@ -19721,7 +19890,7 @@ function collectDerivedRows(ref, mtime, count, maxRowBytes) {
19721
19890
  if (!fullOutcome) fullOutcome = computeOutcome(id);
19722
19891
  if (fullOutcome.found) data = fullOutcome.commits;
19723
19892
  } else if (kind === "markers") {
19724
- const turns = extractTurnsAny(id, { maxTurns: 5e4 });
19893
+ const turns = extractTurnsAny(id, MARKERS_TURN_OPTS);
19725
19894
  data = markersFromTurns(id, turns.turns);
19726
19895
  }
19727
19896
  } catch {
@@ -20175,7 +20344,7 @@ var init_telemetry = __esm({
20175
20344
  init_sync_client();
20176
20345
  cliVersion2 = () => {
20177
20346
  try {
20178
- return true ? "0.7.11" : "dev";
20347
+ return true ? "0.7.13" : "dev";
20179
20348
  } catch {
20180
20349
  return "dev";
20181
20350
  }
@@ -22138,7 +22307,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22138
22307
  } else {
22139
22308
  try {
22140
22309
  const count2 = { redactions: 0 };
22141
- const redacted = mapContainerText(container, (t) => redactSecrets(t, { force: true, count: count2 }));
22310
+ const redacted = redactContainer(container, (t) => redactSecrets(t, { force: true, count: count2 }));
22142
22311
  count.redactions += count2.redactions;
22143
22312
  const { gz, size } = gzipContainer(redacted);
22144
22313
  if (gz.length <= 8 * 1024 * 1024) {
@@ -22338,7 +22507,7 @@ var init_sync_client = __esm({
22338
22507
  ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
22339
22508
  } catch {
22340
22509
  try {
22341
- if (true) ownVersion = "0.7.11";
22510
+ if (true) ownVersion = "0.7.13";
22342
22511
  } catch {
22343
22512
  }
22344
22513
  }
@@ -23815,8 +23984,8 @@ function formatDigest(input, summaryChars = 400) {
23815
23984
  const { row, outcome, related, resumeCmd } = input;
23816
23985
  const lines = [];
23817
23986
  lines.push(`### ${digestTitle(row)}`);
23818
- const when = (row.modified || "").slice(0, 16).replace("T", " ");
23819
- lines.push(`**Project:** ${projectName(row.projectPath) || "(unknown)"} \xB7 **Tool:** ${row.tool || "claude"}` + (when ? ` \xB7 **Modified:** ${when}` : "") + ` \xB7 \`${row.sessionId}\``);
23987
+ const when2 = (row.modified || "").slice(0, 16).replace("T", " ");
23988
+ lines.push(`**Project:** ${projectName(row.projectPath) || "(unknown)"} \xB7 **Tool:** ${row.tool || "claude"}` + (when2 ? ` \xB7 **Modified:** ${when2}` : "") + ` \xB7 \`${row.sessionId}\``);
23820
23989
  if (outcome) {
23821
23990
  const edits = outcome.fileCount > 0 ? ` \xB7 ${outcome.fileCount} file(s) +${outcome.totalLinesAdded}/\u2212${outcome.totalLinesRemoved}` : "";
23822
23991
  const commits = outcome.commits && outcome.commits.totalCommits > 0 ? ` \xB7 ${outcome.commits.totalCommits} commit(s)` : "";
@@ -23876,17 +24045,21 @@ function safeSlice(s, start, end) {
23876
24045
  const hi = end < s.length && /[\uD800-\uDBFF]/.test(s[end - 1] ?? "") ? end + 1 : end;
23877
24046
  return s.slice(lo, hi);
23878
24047
  }
23879
- function body(text, line, opts) {
23880
- if (opts.full || text.length <= BODY_LIMIT)
23881
- return indent(text);
24048
+ function headTail(text, how) {
24049
+ if (text.length <= BODY_LIMIT)
24050
+ return [text];
23882
24051
  const lines = text.split("\n").length;
23883
24052
  const cut = text.length - 2 * EDGE;
23884
- const how = opts.expandHint ? ` ${opts.expandHint(line)}` : "";
23885
24053
  return [
23886
- indent(safeSlice(text, 0, EDGE)),
23887
- `${INDENT}\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how} \u2026`,
23888
- indent(safeSlice(text, text.length - EDGE, text.length))
23889
- ].join("\n");
24054
+ safeSlice(text, 0, EDGE),
24055
+ `\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how ? ` ${how}` : ""} \u2026`,
24056
+ safeSlice(text, text.length - EDGE, text.length)
24057
+ ];
24058
+ }
24059
+ function body(text, line, opts) {
24060
+ if (opts.full)
24061
+ return indent(text);
24062
+ return headTail(text, opts.expandHint?.(line)).map(indent).join("\n");
23890
24063
  }
23891
24064
  function resultText(result) {
23892
24065
  if (typeof result === "string")
@@ -24008,6 +24181,59 @@ function findShowMessages(messages, query) {
24008
24181
  return messages.filter((m) => searchableText(m).toLowerCase().includes(needle));
24009
24182
  }
24010
24183
 
24184
+ // ../engine/dist/mcp/prompts-render.js
24185
+ var fold = (s) => s.toLowerCase().replace(/\s+/g, " ");
24186
+ function when(p, sessionMs) {
24187
+ if (p.ts && p.ts > 0)
24188
+ return p.ts;
24189
+ const parsed = p.tsIso ? Date.parse(p.tsIso) : NaN;
24190
+ return Number.isFinite(parsed) ? parsed : sessionMs;
24191
+ }
24192
+ function newestFirst(a, b) {
24193
+ if (b.at !== a.at)
24194
+ return b.at - a.at;
24195
+ if (a.row.sessionId === b.row.sessionId)
24196
+ return b.row.line - a.row.line;
24197
+ return 0;
24198
+ }
24199
+ function matching(rows, query) {
24200
+ const q = query ? fold(query.trim()) : "";
24201
+ return rows.filter((p) => p.text && p.text.trim() && (!q || fold(p.text).includes(q)));
24202
+ }
24203
+ function selectPrompts(rows, opts) {
24204
+ return matching(rows, opts.query).map((row) => ({ row, at: when(row, 0) })).sort(newestFirst).slice(0, Math.max(0, opts.limit)).map((x) => x.row);
24205
+ }
24206
+ async function collectAcrossSessions(feed, fetchPrompts, opts) {
24207
+ const limit = Math.max(0, opts.limit);
24208
+ let kept = [];
24209
+ for (const s of feed) {
24210
+ if (kept.length >= limit && s.modifiedMs < kept[kept.length - 1].at)
24211
+ break;
24212
+ const rows = await fetchPrompts(s.sessionId);
24213
+ if (!rows)
24214
+ continue;
24215
+ for (const row of matching(rows, opts.query))
24216
+ kept.push({ row, at: when(row, s.modifiedMs) });
24217
+ kept = kept.sort(newestFirst).slice(0, limit);
24218
+ }
24219
+ return kept.map((x) => x.row);
24220
+ }
24221
+ function renderPrompts(rows, opts) {
24222
+ const what = opts.query ? `, containing "${opts.query.trim()}"` : "";
24223
+ const lines = [`# User prompts (${rows.length}${what}, newest first)
24224
+ `];
24225
+ for (const p of rows) {
24226
+ const t = p.tsIso?.slice(0, 16).replace("T", " ") || "";
24227
+ const markerSuffix = opts.withMarkers && p.markers.length ? ` _[${p.markers.join(", ")}]_` : "";
24228
+ lines.push(`- **${p.sessionId}** L${p.line}${t ? ` \xB7 ${t}` : ""}${markerSuffix}`);
24229
+ const how = `Call recall_show with session_id "${p.sessionId}" and expand_line ${p.line} for the whole text.`;
24230
+ for (const part of headTail(p.text, how)) {
24231
+ lines.push(part.split("\n").map((l) => ` ${l}`).join("\n"));
24232
+ }
24233
+ }
24234
+ return lines.join("\n");
24235
+ }
24236
+
24011
24237
  // ../engine/dist/mcp/show-freshness.js
24012
24238
  init_tool_backend();
24013
24239
  init_transcript();
@@ -24244,14 +24470,14 @@ function trialEndingBanner(state2, serverBase) {
24244
24470
  const days = state2.trialDaysLeft;
24245
24471
  if (typeof days !== "number" || days > TRIAL_WARN_DAYS)
24246
24472
  return null;
24247
- const when = days <= 0 ? "today" : days === 1 ? "tomorrow" : `in ${days} days`;
24473
+ const when2 = days <= 0 ? "today" : days === 1 ? "tomorrow" : `in ${days} days`;
24248
24474
  const account = `${serverBase.replace(/\/+$/, "")}/app?view=account`;
24249
24475
  return [
24250
24476
  // No trial LENGTH here. It is FREE_TRIAL_DAYS on the server, so a "7-day
24251
24477
  // trial" written into the CLI is a number that drifts the first time that is
24252
24478
  // tuned — and this file already carries two sentences that went stale that
24253
24479
  // exact way. Days REMAINING comes from the server and is always right.
24254
- `\u23F3 TRIAL ENDS ${when.toUpperCase()} \u2014 this chat-recall account is on the free trial.`,
24480
+ `\u23F3 TRIAL ENDS ${when2.toUpperCase()} \u2014 this chat-recall account is on the free trial.`,
24255
24481
  "When it ends, recall switches off: searches and session reads stop, and new sessions stop syncing.",
24256
24482
  "Nothing is deleted \u2014 the history is kept and export keeps working.",
24257
24483
  `Tell the user now, and give them this link to subscribe or see the account: ${account}`
@@ -25033,7 +25259,8 @@ var RecallImprovementsSchema = z.object({
25033
25259
  var RecallUserPromptsSchema = z.object({
25034
25260
  session_id: z.string().optional().describe("If set, only that session's prompts"),
25035
25261
  since_days: z.number().optional().default(7).describe("When session_id is omitted, look back this many days"),
25036
- limit: z.number().optional().default(50).describe("Maximum prompts to return"),
25262
+ limit: z.number().optional().default(50).describe("Maximum prompts to return, newest first"),
25263
+ query: z.string().optional().describe("Keep only the prompts that contain this text (no case)"),
25037
25264
  with_markers: z.boolean().optional().default(true).describe("Tag each prompt with sentiment / corrective markers (interrupt, frustrated, correction, approval, \u2026). Set false to revert to legacy text-only output.")
25038
25265
  });
25039
25266
  function resolveRecordScope(params) {
@@ -26132,15 +26359,18 @@ write.`,
26132
26359
  // ── User-prompts / decision-record ──
26133
26360
  {
26134
26361
  name: "recall_user_prompts",
26135
- description: `List the human-typed prompts from a session (or recent sessions). Tool results
26136
- and system banners are stripped \u2014 you get only what the user actually wrote. Useful for "what was
26137
- I asking yesterday?" or "what did I tell the agent in this session?".`,
26362
+ description: `List the human-typed prompts from a session (or recent sessions), newest first.
26363
+ Tool results and system banners are stripped \u2014 you get only what the user actually wrote. Useful for
26364
+ "what was I asking yesterday?" or "what did I tell the agent in this session?". Pass \`query\` for
26365
+ "did I say X?": it keeps only the prompts that contain that text. Each prompt prints whole up to 2000
26366
+ characters; a longer one prints its start and end and the recall_show expand_line call for the rest.`,
26138
26367
  inputSchema: {
26139
26368
  type: "object",
26140
26369
  properties: {
26141
26370
  session_id: { type: "string", description: "If set, only that session's prompts" },
26142
26371
  since_days: { type: "number", default: 7, description: "How far back to look, in days." },
26143
- limit: { type: "number", default: 50, description: "Maximum prompts to return, newest first." }
26372
+ limit: { type: "number", default: 50, description: "Maximum prompts to return, newest first." },
26373
+ query: { type: "string", description: "Keep only the prompts that contain this text. Case and runs of whitespace are ignored." }
26144
26374
  }
26145
26375
  }
26146
26376
  },
@@ -27305,9 +27535,9 @@ _Edits stayed local or window was off \u2014 try increasing buffer_minutes._` }]
27305
27535
  const r = remote.results[i];
27306
27536
  const kind = r.chunkType?.match(/:(\w+):imp([0-9])/);
27307
27537
  const tag = kind ? ` \xB7 ${kind[1]} (imp${kind[2]})` : "";
27308
- const when = r.mtime ? ` \xB7 ${new Date(r.mtime).toISOString().slice(0, 10)}` : "";
27538
+ const when2 = r.mtime ? ` \xB7 ${new Date(r.mtime).toISOString().slice(0, 10)}` : "";
27309
27539
  lines.push(`## #${i + 1} [${r.sourceType}${tag}] ${(r.title || r.itemId).slice(0, 90)}`);
27310
- lines.push(`**id:** \`${r.itemId}\`${when}${r.projectPath ? ` \xB7 ${r.projectPath}` : ""}`);
27540
+ lines.push(`**id:** \`${r.itemId}\`${when2}${r.projectPath ? ` \xB7 ${r.projectPath}` : ""}`);
27311
27541
  if (r.sourceType === "session")
27312
27542
  lines.push(`_fetch: recall_context / recall_show "${r.itemId}"_`);
27313
27543
  const snippets = (r.matchedChunks?.length ? r.matchedChunks.map((c) => c.text) : [r.text]).filter(Boolean).slice(0, 3);
@@ -27599,8 +27829,8 @@ _Edits stayed local or window was off \u2014 try increasing buffer_minutes._` }]
27599
27829
  const total = rows.reduce((n, r) => n + r.sessions, 0);
27600
27830
  lines.push(`## ${member} \u2014 ${total} session(s)`);
27601
27831
  for (const r of [...rows].sort((x, y) => y.lastMtime - x.lastMtime)) {
27602
- const when = r.lastMtime ? new Date(r.lastMtime).toISOString().slice(0, 10) : "????";
27603
- lines.push(`- ${r.projectId} \xB7 ${r.sessions} session(s) \xB7 last active ${when}`);
27832
+ const when2 = r.lastMtime ? new Date(r.lastMtime).toISOString().slice(0, 10) : "????";
27833
+ lines.push(`- ${r.projectId} \xB7 ${r.sessions} session(s) \xB7 last active ${when2}`);
27604
27834
  }
27605
27835
  lines.push("");
27606
27836
  }
@@ -28281,54 +28511,35 @@ ${JSON.stringify(r, null, 2)}
28281
28511
  case "recall_user_prompts": {
28282
28512
  const params = RecallUserPromptsSchema.parse(args);
28283
28513
  requireRemote();
28284
- const renderPrompts = (rows) => {
28285
- const lines = [`# User prompts (${rows.length})
28286
- `];
28287
- for (const p of rows) {
28288
- const t = p.tsIso?.slice(0, 16).replace("T", " ") || "";
28289
- const snippet = p.text.length > 240 ? p.text.slice(0, 240) + "\u2026" : p.text;
28290
- const markerSuffix = params.with_markers && p.markers.length ? ` _[${p.markers.join(", ")}]_` : "";
28291
- lines.push(`- **${p.sessionId}** L${p.line}${t ? ` \xB7 ${t}` : ""}${markerSuffix}`);
28292
- lines.push(` ${snippet.replace(/\n/g, " ")}`);
28293
- }
28294
- return lines.join("\n");
28295
- };
28514
+ const toRows = (sessionId, prompts) => prompts.map((p) => ({ sessionId, line: p.line, ts: p.ts, tsIso: p.tsIso, markers: p.markers || [], text: p.text }));
28515
+ const select = { limit: params.limit, query: params.query };
28516
+ const render = (rows) => renderPrompts(rows, { withMarkers: params.with_markers, query: params.query });
28517
+ const none = params.query ? `No user prompts contain "${params.query.trim()}".` : "No user prompts found.";
28296
28518
  if (params.session_id) {
28297
28519
  const soft = await remoteGetSoft(`/api/conversations/${encodeURIComponent(params.session_id)}/markers`);
28298
28520
  if (!soft.data) {
28299
28521
  return { content: [{ type: "text", text: soft.message || (soft.status === 404 ? `Session ${params.session_id} not found.` : `Prompts not synced yet for ${params.session_id}.`) }] };
28300
28522
  }
28301
- const rows = soft.data.prompts.filter((p) => p.text && p.text.trim()).slice(0, params.limit).map((p) => ({ sessionId: params.session_id, line: p.line, tsIso: p.tsIso, markers: p.markers || [], text: p.text }));
28302
- if (rows.length === 0) {
28303
- return { content: [{ type: "text", text: "No user prompts found." }] };
28304
- }
28305
- return { content: [{ type: "text", text: renderPrompts(rows) }] };
28523
+ const rows = selectPrompts(toRows(params.session_id, soft.data.prompts), select);
28524
+ return { content: [{ type: "text", text: rows.length ? render(rows) : none }] };
28306
28525
  }
28307
28526
  const sinceHours = params.since_days * 24;
28308
28527
  const recent = await remoteGetQS("/api/conversations/recent", { limit: 200, since_hours: sinceHours });
28309
- const sessionIds = (recent.sessions || []).map((s) => s.sessionId);
28310
- if (sessionIds.length === 0) {
28311
- return { content: [{ type: "text", text: "No user prompts found." }] };
28312
- }
28313
- const collected = [];
28314
- for (const sid of sessionIds) {
28315
- if (collected.length >= params.limit)
28316
- break;
28528
+ const feed = (recent.sessions || []).map((s) => ({
28529
+ sessionId: s.sessionId,
28530
+ modifiedMs: s.fileMtime || (s.modified ? Date.parse(s.modified) : 0) || 0
28531
+ }));
28532
+ if (feed.length === 0) {
28533
+ return { content: [{ type: "text", text: none }] };
28534
+ }
28535
+ const collected = await collectAcrossSessions(feed, async (sid) => {
28317
28536
  const soft = await remoteGetSoft(`/api/conversations/${encodeURIComponent(sid)}/markers`);
28318
- if (!soft.data)
28319
- continue;
28320
- for (const p of soft.data.prompts) {
28321
- if (!p.text || !p.text.trim())
28322
- continue;
28323
- collected.push({ sessionId: sid, line: p.line, tsIso: p.tsIso, markers: p.markers || [], text: p.text });
28324
- if (collected.length >= params.limit)
28325
- break;
28326
- }
28327
- }
28537
+ return soft.data ? toRows(sid, soft.data.prompts) : null;
28538
+ }, select);
28328
28539
  if (collected.length === 0) {
28329
- return { content: [{ type: "text", text: "No user prompts found (none of the recent sessions have synced markers yet)." }] };
28540
+ return { content: [{ type: "text", text: params.query ? none : "No user prompts found (none of the recent sessions have synced markers yet)." }] };
28330
28541
  }
28331
- return { content: [{ type: "text", text: renderPrompts(collected) }] };
28542
+ return { content: [{ type: "text", text: render(collected) }] };
28332
28543
  }
28333
28544
  // ── Decision recording ────────────────────────────────────
28334
28545
  case "recall_decision_record": {
@@ -28924,9 +29135,9 @@ _Tell the user \u2014 this changes how future sessions treat their repo._` }] };
28924
29135
  }
28925
29136
  const lines = [`# Files resembling \`${params.filename}\` (${data.hits.length})`, "_You may have built this before._\n"];
28926
29137
  for (const h of data.hits) {
28927
- const when = h.mtime ? new Date(h.mtime).toISOString().slice(0, 10) : "";
29138
+ const when2 = h.mtime ? new Date(h.mtime).toISOString().slice(0, 10) : "";
28928
29139
  lines.push(`- \`${h.file}\` \u2014 ${h.reason} (score ${h.score})`);
28929
- lines.push(` ${h.project || "(no project)"} \xB7 ${h.sessionId}${when ? ` \xB7 ${when}` : ""}`);
29140
+ lines.push(` ${h.project || "(no project)"} \xB7 ${h.sessionId}${when2 ? ` \xB7 ${when2}` : ""}`);
28930
29141
  }
28931
29142
  return { content: [{ type: "text", text: lines.join("\n") }] };
28932
29143
  }
@@ -28968,8 +29179,8 @@ _Tell the user \u2014 this changes how future sessions treat their repo._` }] };
28968
29179
  const lines = [`# ${params.source_type} items (${data.items.length})
28969
29180
  `];
28970
29181
  for (const it of data.items) {
28971
- const when = it.mtime ? new Date(it.mtime).toISOString().slice(0, 10) : "";
28972
- lines.push(`- **${it.id}**${it.title ? ` \u2014 ${it.title}` : ""}${when ? ` \xB7 ${when}` : ""}`);
29182
+ const when2 = it.mtime ? new Date(it.mtime).toISOString().slice(0, 10) : "";
29183
+ lines.push(`- **${it.id}**${it.title ? ` \u2014 ${it.title}` : ""}${when2 ? ` \xB7 ${when2}` : ""}`);
28973
29184
  }
28974
29185
  return { content: [{ type: "text", text: lines.join("\n") }] };
28975
29186
  }
@@ -29287,7 +29498,7 @@ function installCrashGuards() {
29287
29498
  reportClientEvent2("mcp_crash", { message: err.stack || err.message });
29288
29499
  });
29289
29500
  }
29290
- var RELAY_VERSION = true ? "0.7.11" : "0.0.0";
29501
+ var RELAY_VERSION = true ? "0.7.13" : "0.0.0";
29291
29502
  function daemonIdleSecs() {
29292
29503
  const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
29293
29504
  if (raw === void 0) return 900;