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/cli.js CHANGED
@@ -4628,8 +4628,15 @@ var init_claude_prompt_origin = __esm({
4628
4628
  INJECTED_PREFIXES = [
4629
4629
  ["<task-notification", "task-notification"],
4630
4630
  ["<agent-message", "peer"],
4631
+ ["<cross-session-message", "peer"],
4632
+ ["[Cross-session delivery notice]", "peer"],
4631
4633
  ["Another Claude session sent a message:", "peer"],
4632
- ["Stop hook feedback:", "hook"]
4634
+ ["Stop hook feedback:", "hook"],
4635
+ ["<bash-stdout>", "command-output"],
4636
+ ["<bash-stderr>", "command-output"],
4637
+ ["<local-command-stdout>", "command-output"],
4638
+ ["<local-command-stderr>", "command-output"],
4639
+ ["<<autonomous-loop", "meta"]
4633
4640
  ];
4634
4641
  }
4635
4642
  });
@@ -6618,7 +6625,11 @@ __export(raw_exports, {
6618
6625
  gzipContainer: () => gzipContainer,
6619
6626
  mapContainerText: () => mapContainerText,
6620
6627
  parseOpenCodeDumpText: () => parseOpenCodeDumpText,
6621
- parseTranscriptFromContainer: () => parseTranscriptFromContainer
6628
+ parseTranscriptFromContainer: () => parseTranscriptFromContainer,
6629
+ redactContainer: () => redactContainer,
6630
+ redactJsonLine: () => redactJsonLine,
6631
+ repairContainer: () => repairContainer,
6632
+ repairRedactedJsonl: () => repairRedactedJsonl
6622
6633
  });
6623
6634
  import { gzipSync, gunzipSync } from "zlib";
6624
6635
  import { createHash } from "crypto";
@@ -6659,6 +6670,134 @@ function gunzipContainer(gz) {
6659
6670
  function mapContainerText(c, fn) {
6660
6671
  return { ...c, files: c.files.map((f) => ({ name: f.name, text: fn(f.text) })) };
6661
6672
  }
6673
+ function repairRedactedJsonl(text) {
6674
+ let repaired = 0;
6675
+ const parts = [];
6676
+ let copied = 0;
6677
+ let at = text.indexOf("[REDACTED:");
6678
+ while (at >= 0) {
6679
+ const start = text.lastIndexOf("\n", at) + 1;
6680
+ let end = text.indexOf("\n", at);
6681
+ if (end < 0)
6682
+ end = text.length;
6683
+ const fixed = repairLine(text.slice(start, end));
6684
+ if (fixed !== null) {
6685
+ parts.push(text.slice(copied, start), fixed);
6686
+ copied = end;
6687
+ repaired++;
6688
+ }
6689
+ at = text.indexOf("[REDACTED:", end);
6690
+ }
6691
+ if (repaired === 0)
6692
+ return { text, repaired: 0 };
6693
+ parts.push(text.slice(copied));
6694
+ return { text: parts.join(""), repaired };
6695
+ }
6696
+ function parseError(line) {
6697
+ try {
6698
+ JSON.parse(line);
6699
+ return null;
6700
+ } catch (e) {
6701
+ const m = /position (\d+)/.exec(e instanceof Error ? e.message : "");
6702
+ return m ? Number(m[1]) : line.length;
6703
+ }
6704
+ }
6705
+ function repairLine(line) {
6706
+ if (!line.includes("[REDACTED:") || parseError(line) === null)
6707
+ return null;
6708
+ let out = line.replace(/(?<!\\)((?:\\\\)*)\\(\[REDACTED:)/g, "$1$2");
6709
+ for (let attempt = 0; attempt < 64; attempt++) {
6710
+ const at = parseError(out);
6711
+ if (at === null)
6712
+ return out;
6713
+ let site = -1;
6714
+ for (const m of out.matchAll(MARKER_QUOTE)) {
6715
+ const q = m.index + m[0].length - 1;
6716
+ if (q < at)
6717
+ site = q;
6718
+ }
6719
+ if (site < 0)
6720
+ return null;
6721
+ out = out.slice(0, site) + "\\" + out.slice(site);
6722
+ }
6723
+ return null;
6724
+ }
6725
+ function redactJsonLine(line, redact) {
6726
+ if (!line.includes('"'))
6727
+ return redact(line);
6728
+ try {
6729
+ JSON.parse(line);
6730
+ } catch {
6731
+ return redact(line);
6732
+ }
6733
+ const decoded = mapJsonStrings(line, redact, true);
6734
+ const out = redact(decoded);
6735
+ try {
6736
+ JSON.parse(out);
6737
+ return out;
6738
+ } catch {
6739
+ return mapJsonStrings(decoded, redact, false);
6740
+ }
6741
+ }
6742
+ function mapJsonStrings(line, fn, escapedOnly) {
6743
+ let out = "";
6744
+ let i = 0;
6745
+ for (; ; ) {
6746
+ const q = line.indexOf('"', i);
6747
+ if (q < 0)
6748
+ return out + line.slice(i);
6749
+ let j = q + 1;
6750
+ while (j < line.length) {
6751
+ const c = line.charCodeAt(j);
6752
+ if (c === 92)
6753
+ j += 2;
6754
+ else if (c === 34)
6755
+ break;
6756
+ else
6757
+ j++;
6758
+ }
6759
+ const token = line.slice(q, j + 1);
6760
+ out += line.slice(i, q);
6761
+ i = j + 1;
6762
+ const body2 = token.slice(1, -1);
6763
+ const escaped = body2.includes("\\");
6764
+ if (escapedOnly && !escaped) {
6765
+ out += token;
6766
+ continue;
6767
+ }
6768
+ let value = body2;
6769
+ if (escaped) {
6770
+ try {
6771
+ value = JSON.parse(token);
6772
+ } catch {
6773
+ out += token;
6774
+ continue;
6775
+ }
6776
+ }
6777
+ const next = fn(value);
6778
+ out += next === value ? token : JSON.stringify(next);
6779
+ }
6780
+ }
6781
+ function redactContainer(c, redact) {
6782
+ return {
6783
+ ...c,
6784
+ files: c.files.map((f) => ({
6785
+ name: f.name,
6786
+ text: f.name.endsWith(".jsonl") ? f.text.split("\n").map((l) => l ? redactJsonLine(l, redact) : l).join("\n") : redact(f.text)
6787
+ }))
6788
+ };
6789
+ }
6790
+ function repairContainer(c) {
6791
+ let repaired = 0;
6792
+ const files = c.files.map((f) => {
6793
+ if (!f.name.endsWith(".jsonl"))
6794
+ return f;
6795
+ const r = repairRedactedJsonl(f.text);
6796
+ repaired += r.repaired;
6797
+ return r.repaired > 0 ? { name: f.name, text: r.text } : f;
6798
+ });
6799
+ return repaired > 0 ? { container: { ...c, files }, repaired } : { container: c, repaired: 0 };
6800
+ }
6662
6801
  function subagentKind(id) {
6663
6802
  return id.includes("acompact") ? "compact" : id.includes("aside_question") ? "aside" : id.includes("explore") || /^agent-a[0-9a-f]{16,}$/i.test(id) ? "explore" : "other";
6664
6803
  }
@@ -6829,6 +6968,7 @@ function requireFs() {
6829
6968
  function requirePath() {
6830
6969
  return pathMod;
6831
6970
  }
6971
+ var MARKER_QUOTE;
6832
6972
  var init_raw = __esm({
6833
6973
  "../engine/dist/transcript/raw.js"() {
6834
6974
  "use strict";
@@ -6836,6 +6976,7 @@ var init_raw = __esm({
6836
6976
  init_claude();
6837
6977
  init_codex();
6838
6978
  init_from_events();
6979
+ MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
6839
6980
  }
6840
6981
  });
6841
6982
 
@@ -6977,7 +7118,7 @@ function markersFromTurns(sessionId, turns) {
6977
7118
  const prompts = turns.filter((t) => t.kind === "user" && t.text).map((t) => ({ line: t.line, ts: t.ts, tsIso: t.tsIso, ...markPrompt(t.text) }));
6978
7119
  return { v: MARKERS_VERSION, sessionId, prompts, summary: summarizeMarkers(prompts) };
6979
7120
  }
6980
- var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION;
7121
+ var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION, MARKERS_TURN_OPTS;
6981
7122
  var init_session_sentiment = __esm({
6982
7123
  "../engine/dist/core/session-sentiment.js"() {
6983
7124
  "use strict";
@@ -6990,7 +7131,8 @@ var init_session_sentiment = __esm({
6990
7131
  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;
6991
7132
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
6992
7133
  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;
6993
- MARKERS_VERSION = 2;
7134
+ MARKERS_VERSION = 3;
7135
+ MARKERS_TURN_OPTS = { maxTurns: 5e4, userMax: Number.POSITIVE_INFINITY };
6994
7136
  }
6995
7137
  });
6996
7138
 
@@ -7166,7 +7308,7 @@ function extractTurnsAny(sessionId, opts = {}) {
7166
7308
  const backend = getBackendForId(sessionId);
7167
7309
  if (!backend)
7168
7310
  return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
7169
- return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
7311
+ return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
7170
7312
  }
7171
7313
  function replaySessionAny(sessionId) {
7172
7314
  const backend = getBackendForId(sessionId);
@@ -7378,6 +7520,7 @@ function shorten(text, max) {
7378
7520
  function extractTurnsFromEvents(sessionId, events, opts = {}) {
7379
7521
  const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
7380
7522
  const assistantMax = opts.assistantMax ?? DEFAULT_ASSISTANT_MAX;
7523
+ const userMax = opts.userMax ?? DEFAULT_USER_MAX;
7381
7524
  const turns = [];
7382
7525
  let startMs = 0, endMs = 0;
7383
7526
  let found = false;
@@ -7395,7 +7538,7 @@ function extractTurnsFromEvents(sessionId, events, opts = {}) {
7395
7538
  ts: e.ts,
7396
7539
  tsIso: e.tsIso,
7397
7540
  line: e.line,
7398
- text: shorten(e.text, DEFAULT_USER_MAX)
7541
+ text: shorten(e.text, userMax)
7399
7542
  });
7400
7543
  } else if (e.kind === "assistant_text" && e.text) {
7401
7544
  turns.push({
@@ -12662,7 +12805,13 @@ function redactSecrets(text, opts = {}) {
12662
12805
  return whole;
12663
12806
  if (opts.count)
12664
12807
  opts.count.redactions++;
12665
- return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
12808
+ let last = args.length - 1;
12809
+ if (typeof args[last] === "object")
12810
+ last--;
12811
+ const source = args[last];
12812
+ const secretStart = args[last - 1] + whole.length - secret.length;
12813
+ const head = escapeHead(source, secretStart, secret.length);
12814
+ return whole.slice(0, whole.length - secret.length + head) + `[REDACTED:${r.label}]` + escapeKept(whole, whole.length);
12666
12815
  });
12667
12816
  }
12668
12817
  const ctx = findContextualSecrets(out);
@@ -12670,10 +12819,25 @@ function redactSecrets(text, opts = {}) {
12670
12819
  const { start, end } = ctx[i];
12671
12820
  if (opts.count)
12672
12821
  opts.count.redactions++;
12673
- out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
12822
+ const head = escapeHead(out, start, end - start);
12823
+ out = out.slice(0, start + head) + "[REDACTED:secret-context]" + escapeKept(out, end) + out.slice(end);
12674
12824
  }
12675
12825
  return out;
12676
12826
  }
12827
+ function escapeKept(text, end) {
12828
+ let run = 0;
12829
+ while (run < end && text.charCodeAt(end - 1 - run) === 92)
12830
+ run++;
12831
+ return run % 2 === 1 ? "\\" : "";
12832
+ }
12833
+ function escapeHead(text, start, length) {
12834
+ let run = 0;
12835
+ while (run < start && text.charCodeAt(start - 1 - run) === 92)
12836
+ run++;
12837
+ if (run % 2 === 0)
12838
+ return 0;
12839
+ return Math.min(text[start] === "u" ? 5 : 1, length);
12840
+ }
12677
12841
  function maskSecret(s) {
12678
12842
  if (!s)
12679
12843
  return "";
@@ -12828,7 +12992,12 @@ var init_secret_redactor = __esm({
12828
12992
  // userinfo URL walked through in cleartext — and a `jdbc:postgresql://…`
12829
12993
  // prefix broke the \b anchor for the one family it did cover. Any scheme
12830
12994
  // followed by userinfo is a credential, so match the shape, not a list.
12831
- { label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/]{1,64}:([^@\s/]{1,256})(?=@)/g },
12995
+ //
12996
+ // A quote in userinfo is percent-encoded, so neither part takes a `"`. The
12997
+ // sync redacts JSONL text, and a match that ran on through `","is_error":`
12998
+ // to a later `@` replaced JSON structure: 4 lines on one machine stopped
12999
+ // parsing, and their records were dropped.
13000
+ { label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/"]{1,64}:([^@\s/"]{1,256})(?=@)/g },
12832
13001
  // Bare AWS secret-access-key VALUE: EXACTLY 40 base64 chars, word-bounded,
12833
13002
  // containing upper+lower+digit. This is the gitleaks heuristic — the mixed-case
12834
13003
  // requirement excludes 40-char lowercase-hex git SHAs, and the exact-40 bound
@@ -14150,6 +14319,10 @@ __export(transcript_exports, {
14150
14319
  parseTranscript: () => parseTranscript,
14151
14320
  parseTranscriptFromContainer: () => parseTranscriptFromContainer,
14152
14321
  readShadowContainer: () => readShadowContainer,
14322
+ redactContainer: () => redactContainer,
14323
+ redactJsonLine: () => redactJsonLine,
14324
+ repairContainer: () => repairContainer,
14325
+ repairRedactedJsonl: () => repairRedactedJsonl,
14153
14326
  seedShadow: () => seedShadow,
14154
14327
  shadowFileFor: () => shadowFileFor,
14155
14328
  shadowRoot: () => shadowRoot,
@@ -18765,7 +18938,7 @@ var init_http = __esm({
18765
18938
  CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18766
18939
  } catch {
18767
18940
  try {
18768
- if (true) CLI_VERSION = "0.7.11";
18941
+ if (true) CLI_VERSION = "0.7.13";
18769
18942
  } catch {
18770
18943
  }
18771
18944
  }
@@ -18809,7 +18982,7 @@ function collectDerivedRows(ref, mtime, count, maxRowBytes) {
18809
18982
  if (!fullOutcome) fullOutcome = computeOutcome(id);
18810
18983
  if (fullOutcome.found) data = fullOutcome.commits;
18811
18984
  } else if (kind === "markers") {
18812
- const turns = extractTurnsAny(id, { maxTurns: 5e4 });
18985
+ const turns = extractTurnsAny(id, MARKERS_TURN_OPTS);
18813
18986
  data = markersFromTurns(id, turns.turns);
18814
18987
  }
18815
18988
  } catch {
@@ -19263,7 +19436,7 @@ var init_telemetry = __esm({
19263
19436
  init_sync_client();
19264
19437
  cliVersion2 = () => {
19265
19438
  try {
19266
- return true ? "0.7.11" : "dev";
19439
+ return true ? "0.7.13" : "dev";
19267
19440
  } catch {
19268
19441
  return "dev";
19269
19442
  }
@@ -21444,7 +21617,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21444
21617
  } else {
21445
21618
  try {
21446
21619
  const count2 = { redactions: 0 };
21447
- const redacted = mapContainerText(container, (t) => redactSecrets(t, { force: true, count: count2 }));
21620
+ const redacted = redactContainer(container, (t) => redactSecrets(t, { force: true, count: count2 }));
21448
21621
  count.redactions += count2.redactions;
21449
21622
  const { gz, size } = gzipContainer(redacted);
21450
21623
  if (gz.length <= 8 * 1024 * 1024) {
@@ -21644,7 +21817,7 @@ var init_sync_client = __esm({
21644
21817
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
21645
21818
  } catch {
21646
21819
  try {
21647
- if (true) ownVersion = "0.7.11";
21820
+ if (true) ownVersion = "0.7.13";
21648
21821
  } catch {
21649
21822
  }
21650
21823
  }
@@ -25806,7 +25979,7 @@ var init_intent_drain = __esm({
25806
25979
  ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
25807
25980
  } catch {
25808
25981
  try {
25809
- if (true) ownVersion2 = "0.7.11";
25982
+ if (true) ownVersion2 = "0.7.13";
25810
25983
  } catch {
25811
25984
  }
25812
25985
  }
@@ -27873,17 +28046,21 @@ function safeSlice(s, start, end) {
27873
28046
  const hi = end < s.length && /[\uD800-\uDBFF]/.test(s[end - 1] ?? "") ? end + 1 : end;
27874
28047
  return s.slice(lo, hi);
27875
28048
  }
27876
- function body(text, line, opts) {
27877
- if (opts.full || text.length <= BODY_LIMIT)
27878
- return indent(text);
28049
+ function headTail(text, how) {
28050
+ if (text.length <= BODY_LIMIT)
28051
+ return [text];
27879
28052
  const lines = text.split("\n").length;
27880
28053
  const cut = text.length - 2 * EDGE;
27881
- const how = opts.expandHint ? ` ${opts.expandHint(line)}` : "";
27882
28054
  return [
27883
- indent(safeSlice(text, 0, EDGE)),
27884
- `${INDENT}\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how} \u2026`,
27885
- indent(safeSlice(text, text.length - EDGE, text.length))
27886
- ].join("\n");
28055
+ safeSlice(text, 0, EDGE),
28056
+ `\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how ? ` ${how}` : ""} \u2026`,
28057
+ safeSlice(text, text.length - EDGE, text.length)
28058
+ ];
28059
+ }
28060
+ function body(text, line, opts) {
28061
+ if (opts.full)
28062
+ return indent(text);
28063
+ return headTail(text, opts.expandHint?.(line)).map(indent).join("\n");
27887
28064
  }
27888
28065
  function resultText(result) {
27889
28066
  if (typeof result === "string")
@@ -29368,7 +29545,7 @@ program.command("verify").description("Check that the server holds everything th
29368
29545
  }
29369
29546
  const { verifyAgainstServer: verifyAgainstServer2, verifyTargets: verifyTargets2 } = await Promise.resolve().then(() => (init_verify_deep(), verify_deep_exports));
29370
29547
  const { getBackend: getBackend2 } = await Promise.resolve().then(() => (init_tool_backend(), tool_backend_exports));
29371
- const { gzipContainer: gzipContainer2, mapContainerText: mapContainerText2 } = await Promise.resolve().then(() => (init_transcript(), transcript_exports));
29548
+ const { gzipContainer: gzipContainer2, redactContainer: redactContainer2 } = await Promise.resolve().then(() => (init_transcript(), transcript_exports));
29372
29549
  const { redactSecrets: redactSecrets2 } = await Promise.resolve().then(() => (init_secret_redactor(), secret_redactor_exports));
29373
29550
  const { fetchWithTimeout: fetchWithTimeout2 } = await Promise.resolve().then(() => (init_http(), http_exports));
29374
29551
  const { forceFullResync: forceFullResync2 } = await Promise.resolve().then(() => (init_verify_repair(), verify_repair_exports));
@@ -29417,7 +29594,7 @@ program.command("verify").description("Check that the server holds everything th
29417
29594
  mtime: exp.mtime,
29418
29595
  files: exp.files.map((f) => ({ name: f.name, text: f.bytes.toString("utf-8") }))
29419
29596
  };
29420
- const redacted = mapContainerText2(container, (t) => redactSecrets2(t, { force: true }));
29597
+ const redacted = redactContainer2(container, (t) => redactSecrets2(t, { force: true }));
29421
29598
  return gzipContainer2(redacted).size;
29422
29599
  } catch {
29423
29600
  return null;