chat-recall 0.7.11 → 0.7.12

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/watch.js CHANGED
@@ -194,8 +194,15 @@ var init_claude_prompt_origin = __esm({
194
194
  INJECTED_PREFIXES = [
195
195
  ["<task-notification", "task-notification"],
196
196
  ["<agent-message", "peer"],
197
+ ["<cross-session-message", "peer"],
198
+ ["[Cross-session delivery notice]", "peer"],
197
199
  ["Another Claude session sent a message:", "peer"],
198
- ["Stop hook feedback:", "hook"]
200
+ ["Stop hook feedback:", "hook"],
201
+ ["<bash-stdout>", "command-output"],
202
+ ["<bash-stderr>", "command-output"],
203
+ ["<local-command-stdout>", "command-output"],
204
+ ["<local-command-stderr>", "command-output"],
205
+ ["<<autonomous-loop", "meta"]
199
206
  ];
200
207
  }
201
208
  });
@@ -6449,7 +6456,11 @@ __export(raw_exports, {
6449
6456
  gzipContainer: () => gzipContainer,
6450
6457
  mapContainerText: () => mapContainerText,
6451
6458
  parseOpenCodeDumpText: () => parseOpenCodeDumpText,
6452
- parseTranscriptFromContainer: () => parseTranscriptFromContainer
6459
+ parseTranscriptFromContainer: () => parseTranscriptFromContainer,
6460
+ redactContainer: () => redactContainer,
6461
+ redactJsonLine: () => redactJsonLine,
6462
+ repairContainer: () => repairContainer,
6463
+ repairRedactedJsonl: () => repairRedactedJsonl
6453
6464
  });
6454
6465
  import { gzipSync, gunzipSync } from "zlib";
6455
6466
  import { createHash } from "crypto";
@@ -6490,6 +6501,134 @@ function gunzipContainer(gz) {
6490
6501
  function mapContainerText(c, fn) {
6491
6502
  return { ...c, files: c.files.map((f) => ({ name: f.name, text: fn(f.text) })) };
6492
6503
  }
6504
+ function repairRedactedJsonl(text) {
6505
+ let repaired = 0;
6506
+ const parts = [];
6507
+ let copied = 0;
6508
+ let at = text.indexOf("[REDACTED:");
6509
+ while (at >= 0) {
6510
+ const start = text.lastIndexOf("\n", at) + 1;
6511
+ let end = text.indexOf("\n", at);
6512
+ if (end < 0)
6513
+ end = text.length;
6514
+ const fixed = repairLine(text.slice(start, end));
6515
+ if (fixed !== null) {
6516
+ parts.push(text.slice(copied, start), fixed);
6517
+ copied = end;
6518
+ repaired++;
6519
+ }
6520
+ at = text.indexOf("[REDACTED:", end);
6521
+ }
6522
+ if (repaired === 0)
6523
+ return { text, repaired: 0 };
6524
+ parts.push(text.slice(copied));
6525
+ return { text: parts.join(""), repaired };
6526
+ }
6527
+ function parseError(line) {
6528
+ try {
6529
+ JSON.parse(line);
6530
+ return null;
6531
+ } catch (e) {
6532
+ const m = /position (\d+)/.exec(e instanceof Error ? e.message : "");
6533
+ return m ? Number(m[1]) : line.length;
6534
+ }
6535
+ }
6536
+ function repairLine(line) {
6537
+ if (!line.includes("[REDACTED:") || parseError(line) === null)
6538
+ return null;
6539
+ let out = line.replace(/(?<!\\)((?:\\\\)*)\\(\[REDACTED:)/g, "$1$2");
6540
+ for (let attempt = 0; attempt < 64; attempt++) {
6541
+ const at = parseError(out);
6542
+ if (at === null)
6543
+ return out;
6544
+ let site = -1;
6545
+ for (const m of out.matchAll(MARKER_QUOTE)) {
6546
+ const q = m.index + m[0].length - 1;
6547
+ if (q < at)
6548
+ site = q;
6549
+ }
6550
+ if (site < 0)
6551
+ return null;
6552
+ out = out.slice(0, site) + "\\" + out.slice(site);
6553
+ }
6554
+ return null;
6555
+ }
6556
+ function redactJsonLine(line, redact) {
6557
+ if (!line.includes('"'))
6558
+ return redact(line);
6559
+ try {
6560
+ JSON.parse(line);
6561
+ } catch {
6562
+ return redact(line);
6563
+ }
6564
+ const decoded = mapJsonStrings(line, redact, true);
6565
+ const out = redact(decoded);
6566
+ try {
6567
+ JSON.parse(out);
6568
+ return out;
6569
+ } catch {
6570
+ return mapJsonStrings(decoded, redact, false);
6571
+ }
6572
+ }
6573
+ function mapJsonStrings(line, fn, escapedOnly) {
6574
+ let out = "";
6575
+ let i = 0;
6576
+ for (; ; ) {
6577
+ const q = line.indexOf('"', i);
6578
+ if (q < 0)
6579
+ return out + line.slice(i);
6580
+ let j = q + 1;
6581
+ while (j < line.length) {
6582
+ const c = line.charCodeAt(j);
6583
+ if (c === 92)
6584
+ j += 2;
6585
+ else if (c === 34)
6586
+ break;
6587
+ else
6588
+ j++;
6589
+ }
6590
+ const token = line.slice(q, j + 1);
6591
+ out += line.slice(i, q);
6592
+ i = j + 1;
6593
+ const body = token.slice(1, -1);
6594
+ const escaped = body.includes("\\");
6595
+ if (escapedOnly && !escaped) {
6596
+ out += token;
6597
+ continue;
6598
+ }
6599
+ let value = body;
6600
+ if (escaped) {
6601
+ try {
6602
+ value = JSON.parse(token);
6603
+ } catch {
6604
+ out += token;
6605
+ continue;
6606
+ }
6607
+ }
6608
+ const next = fn(value);
6609
+ out += next === value ? token : JSON.stringify(next);
6610
+ }
6611
+ }
6612
+ function redactContainer(c, redact) {
6613
+ return {
6614
+ ...c,
6615
+ files: c.files.map((f) => ({
6616
+ name: f.name,
6617
+ text: f.name.endsWith(".jsonl") ? f.text.split("\n").map((l) => l ? redactJsonLine(l, redact) : l).join("\n") : redact(f.text)
6618
+ }))
6619
+ };
6620
+ }
6621
+ function repairContainer(c) {
6622
+ let repaired = 0;
6623
+ const files = c.files.map((f) => {
6624
+ if (!f.name.endsWith(".jsonl"))
6625
+ return f;
6626
+ const r = repairRedactedJsonl(f.text);
6627
+ repaired += r.repaired;
6628
+ return r.repaired > 0 ? { name: f.name, text: r.text } : f;
6629
+ });
6630
+ return repaired > 0 ? { container: { ...c, files }, repaired } : { container: c, repaired: 0 };
6631
+ }
6493
6632
  function subagentKind(id) {
6494
6633
  return id.includes("acompact") ? "compact" : id.includes("aside_question") ? "aside" : id.includes("explore") || /^agent-a[0-9a-f]{16,}$/i.test(id) ? "explore" : "other";
6495
6634
  }
@@ -6660,6 +6799,7 @@ function requireFs() {
6660
6799
  function requirePath() {
6661
6800
  return pathMod;
6662
6801
  }
6802
+ var MARKER_QUOTE;
6663
6803
  var init_raw = __esm({
6664
6804
  "../engine/dist/transcript/raw.js"() {
6665
6805
  "use strict";
@@ -6667,6 +6807,7 @@ var init_raw = __esm({
6667
6807
  init_claude();
6668
6808
  init_codex();
6669
6809
  init_from_events();
6810
+ MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
6670
6811
  }
6671
6812
  });
6672
6813
 
@@ -7329,7 +7470,7 @@ var init_session_sentiment = __esm({
7329
7470
  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;
7330
7471
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
7331
7472
  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;
7332
- MARKERS_VERSION = 2;
7473
+ MARKERS_VERSION = 3;
7333
7474
  }
7334
7475
  });
7335
7476
 
@@ -12163,7 +12304,13 @@ function redactSecrets(text, opts = {}) {
12163
12304
  return whole;
12164
12305
  if (opts.count)
12165
12306
  opts.count.redactions++;
12166
- return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
12307
+ let last = args.length - 1;
12308
+ if (typeof args[last] === "object")
12309
+ last--;
12310
+ const source = args[last];
12311
+ const secretStart = args[last - 1] + whole.length - secret.length;
12312
+ const head = escapeHead(source, secretStart, secret.length);
12313
+ return whole.slice(0, whole.length - secret.length + head) + `[REDACTED:${r.label}]` + escapeKept(whole, whole.length);
12167
12314
  });
12168
12315
  }
12169
12316
  const ctx = findContextualSecrets(out);
@@ -12171,10 +12318,25 @@ function redactSecrets(text, opts = {}) {
12171
12318
  const { start, end } = ctx[i];
12172
12319
  if (opts.count)
12173
12320
  opts.count.redactions++;
12174
- out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
12321
+ const head = escapeHead(out, start, end - start);
12322
+ out = out.slice(0, start + head) + "[REDACTED:secret-context]" + escapeKept(out, end) + out.slice(end);
12175
12323
  }
12176
12324
  return out;
12177
12325
  }
12326
+ function escapeKept(text, end) {
12327
+ let run = 0;
12328
+ while (run < end && text.charCodeAt(end - 1 - run) === 92)
12329
+ run++;
12330
+ return run % 2 === 1 ? "\\" : "";
12331
+ }
12332
+ function escapeHead(text, start, length) {
12333
+ let run = 0;
12334
+ while (run < start && text.charCodeAt(start - 1 - run) === 92)
12335
+ run++;
12336
+ if (run % 2 === 0)
12337
+ return 0;
12338
+ return Math.min(text[start] === "u" ? 5 : 1, length);
12339
+ }
12178
12340
  function maskSecret(s) {
12179
12341
  if (!s)
12180
12342
  return "";
@@ -12281,7 +12443,12 @@ var init_secret_redactor = __esm({
12281
12443
  // userinfo URL walked through in cleartext — and a `jdbc:postgresql://…`
12282
12444
  // prefix broke the \b anchor for the one family it did cover. Any scheme
12283
12445
  // followed by userinfo is a credential, so match the shape, not a list.
12284
- { label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/]{1,64}:([^@\s/]{1,256})(?=@)/g },
12446
+ //
12447
+ // A quote in userinfo is percent-encoded, so neither part takes a `"`. The
12448
+ // sync redacts JSONL text, and a match that ran on through `","is_error":`
12449
+ // to a later `@` replaced JSON structure: 4 lines on one machine stopped
12450
+ // parsing, and their records were dropped.
12451
+ { label: "url-password", secret: 1, pattern: /\b[a-zA-Z][a-zA-Z0-9+.-]{1,31}:\/\/[^:@\s/"]{1,64}:([^@\s/"]{1,256})(?=@)/g },
12285
12452
  // Bare AWS secret-access-key VALUE: EXACTLY 40 base64 chars, word-bounded,
12286
12453
  // containing upper+lower+digit. This is the gitleaks heuristic — the mixed-case
12287
12454
  // requirement excludes 40-char lowercase-hex git SHAs, and the exact-40 bound
@@ -13315,6 +13482,10 @@ __export(transcript_exports, {
13315
13482
  parseTranscript: () => parseTranscript,
13316
13483
  parseTranscriptFromContainer: () => parseTranscriptFromContainer,
13317
13484
  readShadowContainer: () => readShadowContainer,
13485
+ redactContainer: () => redactContainer,
13486
+ redactJsonLine: () => redactJsonLine,
13487
+ repairContainer: () => repairContainer,
13488
+ repairRedactedJsonl: () => repairRedactedJsonl,
13318
13489
  seedShadow: () => seedShadow,
13319
13490
  shadowFileFor: () => shadowFileFor,
13320
13491
  shadowRoot: () => shadowRoot,
@@ -18312,7 +18483,7 @@ var init_http = __esm({
18312
18483
  CLI_VERSION = JSON.parse(readFileSync32(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18313
18484
  } catch {
18314
18485
  try {
18315
- if (true) CLI_VERSION = "0.7.11";
18486
+ if (true) CLI_VERSION = "0.7.12";
18316
18487
  } catch {
18317
18488
  }
18318
18489
  }
@@ -18806,7 +18977,7 @@ var init_telemetry = __esm({
18806
18977
  init_sync_client();
18807
18978
  cliVersion = () => {
18808
18979
  try {
18809
- return true ? "0.7.11" : "dev";
18980
+ return true ? "0.7.12" : "dev";
18810
18981
  } catch {
18811
18982
  return "dev";
18812
18983
  }
@@ -20739,7 +20910,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
20739
20910
  } else {
20740
20911
  try {
20741
20912
  const count2 = { redactions: 0 };
20742
- const redacted = mapContainerText(container, (t) => redactSecrets(t, { force: true, count: count2 }));
20913
+ const redacted = redactContainer(container, (t) => redactSecrets(t, { force: true, count: count2 }));
20743
20914
  count.redactions += count2.redactions;
20744
20915
  const { gz, size } = gzipContainer(redacted);
20745
20916
  if (gz.length <= 8 * 1024 * 1024) {
@@ -20939,7 +21110,7 @@ var init_sync_client = __esm({
20939
21110
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
20940
21111
  } catch {
20941
21112
  try {
20942
- if (true) ownVersion = "0.7.11";
21113
+ if (true) ownVersion = "0.7.12";
20943
21114
  } catch {
20944
21115
  }
20945
21116
  }
@@ -24076,7 +24247,7 @@ try {
24076
24247
  ownVersion2 = JSON.parse(readFileSync39(join48(dir, "../package.json"), "utf-8")).version || ownVersion2;
24077
24248
  } catch {
24078
24249
  try {
24079
- if (true) ownVersion2 = "0.7.11";
24250
+ if (true) ownVersion2 = "0.7.12";
24080
24251
  } catch {
24081
24252
  }
24082
24253
  }
@@ -24461,7 +24632,7 @@ async function runCollectorMigration(opts) {
24461
24632
  init_sync_ledger();
24462
24633
  init_collector_health();
24463
24634
  import { existsSync as existsSync50, readFileSync as readFileSync45, statSync as statSync45 } from "fs";
24464
- var CLI_VERSION3 = true ? "0.7.11" : "0.0.0";
24635
+ var CLI_VERSION3 = true ? "0.7.12" : "0.0.0";
24465
24636
  var DEBOUNCE_MS = 5e3;
24466
24637
  var PLANS_DIR = claudeBackend.plansDir();
24467
24638
  var TASKS_DIR = claudeBackend.tasksDir();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat-recall",
3
- "version": "0.7.11",
3
+ "version": "0.7.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/munhq/chat-recall.git"