chat-recall 0.7.12 → 0.7.14

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
@@ -7118,7 +7118,7 @@ function markersFromTurns(sessionId, turns) {
7118
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) }));
7119
7119
  return { v: MARKERS_VERSION, sessionId, prompts, summary: summarizeMarkers(prompts) };
7120
7120
  }
7121
- 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;
7122
7122
  var init_session_sentiment = __esm({
7123
7123
  "../engine/dist/core/session-sentiment.js"() {
7124
7124
  "use strict";
@@ -7132,6 +7132,7 @@ var init_session_sentiment = __esm({
7132
7132
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
7133
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;
7134
7134
  MARKERS_VERSION = 3;
7135
+ MARKERS_TURN_OPTS = { maxTurns: 5e4, userMax: Number.POSITIVE_INFINITY };
7135
7136
  }
7136
7137
  });
7137
7138
 
@@ -7307,7 +7308,7 @@ function extractTurnsAny(sessionId, opts = {}) {
7307
7308
  const backend = getBackendForId(sessionId);
7308
7309
  if (!backend)
7309
7310
  return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
7310
- 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));
7311
7312
  }
7312
7313
  function replaySessionAny(sessionId) {
7313
7314
  const backend = getBackendForId(sessionId);
@@ -7519,6 +7520,7 @@ function shorten(text, max) {
7519
7520
  function extractTurnsFromEvents(sessionId, events, opts = {}) {
7520
7521
  const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
7521
7522
  const assistantMax = opts.assistantMax ?? DEFAULT_ASSISTANT_MAX;
7523
+ const userMax = opts.userMax ?? DEFAULT_USER_MAX;
7522
7524
  const turns = [];
7523
7525
  let startMs = 0, endMs = 0;
7524
7526
  let found = false;
@@ -7536,7 +7538,7 @@ function extractTurnsFromEvents(sessionId, events, opts = {}) {
7536
7538
  ts: e.ts,
7537
7539
  tsIso: e.tsIso,
7538
7540
  line: e.line,
7539
- text: shorten(e.text, DEFAULT_USER_MAX)
7541
+ text: shorten(e.text, userMax)
7540
7542
  });
7541
7543
  } else if (e.kind === "assistant_text" && e.text) {
7542
7544
  turns.push({
@@ -10954,12 +10956,14 @@ function lineKey(line) {
10954
10956
  try {
10955
10957
  const o = JSON.parse(line);
10956
10958
  if (typeof o?.uuid === "string" && o.uuid)
10957
- return { key: `u:${o.uuid}`, singleton: false };
10958
- if (typeof o?.type === "string" && SINGLETON_META.has(o.type))
10959
- return { key: `m:${o.type}`, singleton: true };
10959
+ return { key: `u:${o.uuid}`, meta: false, json: true };
10960
+ return { key: `h:${sha1(line)}`, meta: typeof o?.type === "string" && STATE_META.has(o.type), json: true };
10960
10961
  } catch {
10961
10962
  }
10962
- return { key: `h:${sha1(line)}`, singleton: false };
10963
+ return { key: `h:${sha1(line)}`, meta: false, json: false };
10964
+ }
10965
+ function isHalfWritten(line, currentLines) {
10966
+ return currentLines.some((c) => c.length > line.length && c.startsWith(line));
10963
10967
  }
10964
10968
  function mergeLineText(shadowText, currentText) {
10965
10969
  const shadowLines = shadowText.split("\n").filter((l) => l.trim());
@@ -10967,27 +10971,24 @@ function mergeLineText(shadowText, currentText) {
10967
10971
  const currentKeys = /* @__PURE__ */ new Set();
10968
10972
  for (const l of currentLines)
10969
10973
  currentKeys.add(lineKey(l).key);
10970
- const shadowKeys = /* @__PURE__ */ new Set();
10971
- for (const l of shadowLines)
10972
- shadowKeys.add(lineKey(l).key);
10973
- let recovered = 0;
10974
- for (const k of shadowKeys)
10975
- if (!currentKeys.has(k))
10976
- recovered++;
10977
- if (recovered === 0) {
10978
- return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
10979
- }
10980
- const out = [];
10981
- const emitted = /* @__PURE__ */ new Set();
10974
+ const kept = [];
10975
+ const keptKeys = /* @__PURE__ */ new Set();
10976
+ const counted = /* @__PURE__ */ new Set();
10982
10977
  for (const l of shadowLines) {
10983
- const { key: key2, singleton } = lineKey(l);
10984
- if (singleton && currentKeys.has(key2))
10985
- continue;
10986
- if (emitted.has(key2))
10978
+ const { key: key2, meta, json } = lineKey(l);
10979
+ if (!json && isHalfWritten(l, currentLines))
10987
10980
  continue;
10988
- out.push(l);
10989
- emitted.add(key2);
10981
+ kept.push(l);
10982
+ keptKeys.add(key2);
10983
+ if (!meta && !currentKeys.has(key2))
10984
+ counted.add(key2);
10990
10985
  }
10986
+ const recovered = counted.size;
10987
+ if (recovered === 0) {
10988
+ return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
10989
+ }
10990
+ const out = [...kept];
10991
+ const emitted = keptKeys;
10991
10992
  for (const l of currentLines) {
10992
10993
  const { key: key2 } = lineKey(l);
10993
10994
  if (emitted.has(key2))
@@ -11172,14 +11173,14 @@ function shadowSizeOnDisk(tool, rawId) {
11172
11173
  return 0;
11173
11174
  }
11174
11175
  }
11175
- var WHOLE_FILE_TOOLS, SINGLETON_META, lastLiveFingerprint;
11176
+ var WHOLE_FILE_TOOLS, STATE_META, lastLiveFingerprint;
11176
11177
  var init_shadow = __esm({
11177
11178
  "../engine/dist/transcript/shadow.js"() {
11178
11179
  "use strict";
11179
11180
  init_paths();
11180
11181
  init_raw();
11181
11182
  WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
11182
- SINGLETON_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
11183
+ STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
11183
11184
  lastLiveFingerprint = /* @__PURE__ */ new Map();
11184
11185
  }
11185
11186
  });
@@ -18936,7 +18937,7 @@ var init_http = __esm({
18936
18937
  CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18937
18938
  } catch {
18938
18939
  try {
18939
- if (true) CLI_VERSION = "0.7.12";
18940
+ if (true) CLI_VERSION = "0.7.14";
18940
18941
  } catch {
18941
18942
  }
18942
18943
  }
@@ -18980,7 +18981,7 @@ function collectDerivedRows(ref, mtime, count, maxRowBytes) {
18980
18981
  if (!fullOutcome) fullOutcome = computeOutcome(id);
18981
18982
  if (fullOutcome.found) data = fullOutcome.commits;
18982
18983
  } else if (kind === "markers") {
18983
- const turns = extractTurnsAny(id, { maxTurns: 5e4 });
18984
+ const turns = extractTurnsAny(id, MARKERS_TURN_OPTS);
18984
18985
  data = markersFromTurns(id, turns.turns);
18985
18986
  }
18986
18987
  } catch {
@@ -19434,7 +19435,7 @@ var init_telemetry = __esm({
19434
19435
  init_sync_client();
19435
19436
  cliVersion2 = () => {
19436
19437
  try {
19437
- return true ? "0.7.12" : "dev";
19438
+ return true ? "0.7.14" : "dev";
19438
19439
  } catch {
19439
19440
  return "dev";
19440
19441
  }
@@ -21815,7 +21816,7 @@ var init_sync_client = __esm({
21815
21816
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
21816
21817
  } catch {
21817
21818
  try {
21818
- if (true) ownVersion = "0.7.12";
21819
+ if (true) ownVersion = "0.7.14";
21819
21820
  } catch {
21820
21821
  }
21821
21822
  }
@@ -25977,7 +25978,7 @@ var init_intent_drain = __esm({
25977
25978
  ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
25978
25979
  } catch {
25979
25980
  try {
25980
- if (true) ownVersion2 = "0.7.12";
25981
+ if (true) ownVersion2 = "0.7.14";
25981
25982
  } catch {
25982
25983
  }
25983
25984
  }
@@ -28044,17 +28045,21 @@ function safeSlice(s, start, end) {
28044
28045
  const hi = end < s.length && /[\uD800-\uDBFF]/.test(s[end - 1] ?? "") ? end + 1 : end;
28045
28046
  return s.slice(lo, hi);
28046
28047
  }
28047
- function body(text, line, opts) {
28048
- if (opts.full || text.length <= BODY_LIMIT)
28049
- return indent(text);
28048
+ function headTail(text, how) {
28049
+ if (text.length <= BODY_LIMIT)
28050
+ return [text];
28050
28051
  const lines = text.split("\n").length;
28051
28052
  const cut = text.length - 2 * EDGE;
28052
- const how = opts.expandHint ? ` ${opts.expandHint(line)}` : "";
28053
28053
  return [
28054
- indent(safeSlice(text, 0, EDGE)),
28055
- `${INDENT}\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how} \u2026`,
28056
- indent(safeSlice(text, text.length - EDGE, text.length))
28057
- ].join("\n");
28054
+ safeSlice(text, 0, EDGE),
28055
+ `\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how ? ` ${how}` : ""} \u2026`,
28056
+ safeSlice(text, text.length - EDGE, text.length)
28057
+ ];
28058
+ }
28059
+ function body(text, line, opts) {
28060
+ if (opts.full)
28061
+ return indent(text);
28062
+ return headTail(text, opts.expandHint?.(line)).map(indent).join("\n");
28058
28063
  }
28059
28064
  function resultText(result) {
28060
28065
  if (typeof result === "string")
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":"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"}
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.12" : "0.0.0";
195
+ var VERSION = true ? "0.7.14" : "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
@@ -6035,7 +6035,7 @@ function markersFromTurns(sessionId, turns) {
6035
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) }));
6036
6036
  return { v: MARKERS_VERSION, sessionId, prompts, summary: summarizeMarkers(prompts) };
6037
6037
  }
6038
- 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;
6039
6039
  var init_session_sentiment = __esm({
6040
6040
  "../engine/dist/core/session-sentiment.js"() {
6041
6041
  "use strict";
@@ -6049,6 +6049,7 @@ var init_session_sentiment = __esm({
6049
6049
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
6050
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;
6051
6051
  MARKERS_VERSION = 3;
6052
+ MARKERS_TURN_OPTS = { maxTurns: 5e4, userMax: Number.POSITIVE_INFINITY };
6052
6053
  }
6053
6054
  });
6054
6055
 
@@ -6274,6 +6275,7 @@ function shorten(text, max) {
6274
6275
  function extractTurnsFromEvents(sessionId, events, opts = {}) {
6275
6276
  const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
6276
6277
  const assistantMax = opts.assistantMax ?? DEFAULT_ASSISTANT_MAX;
6278
+ const userMax = opts.userMax ?? DEFAULT_USER_MAX;
6277
6279
  const turns = [];
6278
6280
  let startMs = 0, endMs = 0;
6279
6281
  let found = false;
@@ -6291,7 +6293,7 @@ function extractTurnsFromEvents(sessionId, events, opts = {}) {
6291
6293
  ts: e.ts,
6292
6294
  tsIso: e.tsIso,
6293
6295
  line: e.line,
6294
- text: shorten(e.text, DEFAULT_USER_MAX)
6296
+ text: shorten(e.text, userMax)
6295
6297
  });
6296
6298
  } else if (e.kind === "assistant_text" && e.text) {
6297
6299
  turns.push({
@@ -9655,7 +9657,7 @@ function extractTurnsAny(sessionId, opts = {}) {
9655
9657
  const backend = getBackendForId(sessionId);
9656
9658
  if (!backend)
9657
9659
  return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
9658
- 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));
9659
9661
  }
9660
9662
  function replaySessionAny(sessionId) {
9661
9663
  const backend = getBackendForId(sessionId);
@@ -12020,12 +12022,14 @@ function lineKey(line) {
12020
12022
  try {
12021
12023
  const o = JSON.parse(line);
12022
12024
  if (typeof o?.uuid === "string" && o.uuid)
12023
- return { key: `u:${o.uuid}`, singleton: false };
12024
- if (typeof o?.type === "string" && SINGLETON_META.has(o.type))
12025
- return { key: `m:${o.type}`, singleton: true };
12025
+ return { key: `u:${o.uuid}`, meta: false, json: true };
12026
+ return { key: `h:${sha1(line)}`, meta: typeof o?.type === "string" && STATE_META.has(o.type), json: true };
12026
12027
  } catch {
12027
12028
  }
12028
- return { key: `h:${sha1(line)}`, singleton: false };
12029
+ return { key: `h:${sha1(line)}`, meta: false, json: false };
12030
+ }
12031
+ function isHalfWritten(line, currentLines) {
12032
+ return currentLines.some((c) => c.length > line.length && c.startsWith(line));
12029
12033
  }
12030
12034
  function mergeLineText(shadowText, currentText) {
12031
12035
  const shadowLines = shadowText.split("\n").filter((l) => l.trim());
@@ -12033,27 +12037,24 @@ function mergeLineText(shadowText, currentText) {
12033
12037
  const currentKeys = /* @__PURE__ */ new Set();
12034
12038
  for (const l of currentLines)
12035
12039
  currentKeys.add(lineKey(l).key);
12036
- const shadowKeys = /* @__PURE__ */ new Set();
12037
- for (const l of shadowLines)
12038
- shadowKeys.add(lineKey(l).key);
12039
- let recovered = 0;
12040
- for (const k of shadowKeys)
12041
- if (!currentKeys.has(k))
12042
- recovered++;
12043
- if (recovered === 0) {
12044
- return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
12045
- }
12046
- const out = [];
12047
- const emitted = /* @__PURE__ */ new Set();
12040
+ const kept = [];
12041
+ const keptKeys = /* @__PURE__ */ new Set();
12042
+ const counted = /* @__PURE__ */ new Set();
12048
12043
  for (const l of shadowLines) {
12049
- const { key: key2, singleton } = lineKey(l);
12050
- if (singleton && currentKeys.has(key2))
12051
- continue;
12052
- if (emitted.has(key2))
12044
+ const { key: key2, meta, json } = lineKey(l);
12045
+ if (!json && isHalfWritten(l, currentLines))
12053
12046
  continue;
12054
- out.push(l);
12055
- emitted.add(key2);
12047
+ kept.push(l);
12048
+ keptKeys.add(key2);
12049
+ if (!meta && !currentKeys.has(key2))
12050
+ counted.add(key2);
12051
+ }
12052
+ const recovered = counted.size;
12053
+ if (recovered === 0) {
12054
+ return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
12056
12055
  }
12056
+ const out = [...kept];
12057
+ const emitted = keptKeys;
12057
12058
  for (const l of currentLines) {
12058
12059
  const { key: key2 } = lineKey(l);
12059
12060
  if (emitted.has(key2))
@@ -12183,14 +12184,14 @@ function shadowUncompressedBytes(tool, rawId) {
12183
12184
  }
12184
12185
  }
12185
12186
  }
12186
- var WHOLE_FILE_TOOLS, SINGLETON_META;
12187
+ var WHOLE_FILE_TOOLS, STATE_META;
12187
12188
  var init_shadow = __esm({
12188
12189
  "../engine/dist/transcript/shadow.js"() {
12189
12190
  "use strict";
12190
12191
  init_paths();
12191
12192
  init_raw();
12192
12193
  WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
12193
- SINGLETON_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
12194
+ STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
12194
12195
  }
12195
12196
  });
12196
12197
 
@@ -12938,9 +12939,9 @@ function judgeHealth(h, now = Date.now()) {
12938
12939
  return (a.t.lastOkAt ?? 0) - (b.t.lastOkAt ?? 0);
12939
12940
  });
12940
12941
  const worst = ranked[0];
12941
- const when = worst.t.lastOkAt ? `${ago(now - worst.t.lastOkAt)} ago` : "never";
12942
+ const when2 = worst.t.lastOkAt ? `${ago(now - worst.t.lastOkAt)} ago` : "never";
12942
12943
  const why = worst.t.lastError ? ` (${worst.t.lastError})` : "";
12943
- reasons.push(`nothing has synced since ${when}${why}`);
12944
+ reasons.push(`nothing has synced since ${when2}${why}`);
12944
12945
  }
12945
12946
  if (reasons.length === 0)
12946
12947
  return { ok: true, summary: null, reasons: [] };
@@ -19844,7 +19845,7 @@ var init_http = __esm({
19844
19845
  CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19845
19846
  } catch {
19846
19847
  try {
19847
- if (true) CLI_VERSION = "0.7.12";
19848
+ if (true) CLI_VERSION = "0.7.14";
19848
19849
  } catch {
19849
19850
  }
19850
19851
  }
@@ -19888,7 +19889,7 @@ function collectDerivedRows(ref, mtime, count, maxRowBytes) {
19888
19889
  if (!fullOutcome) fullOutcome = computeOutcome(id);
19889
19890
  if (fullOutcome.found) data = fullOutcome.commits;
19890
19891
  } else if (kind === "markers") {
19891
- const turns = extractTurnsAny(id, { maxTurns: 5e4 });
19892
+ const turns = extractTurnsAny(id, MARKERS_TURN_OPTS);
19892
19893
  data = markersFromTurns(id, turns.turns);
19893
19894
  }
19894
19895
  } catch {
@@ -20342,7 +20343,7 @@ var init_telemetry = __esm({
20342
20343
  init_sync_client();
20343
20344
  cliVersion2 = () => {
20344
20345
  try {
20345
- return true ? "0.7.12" : "dev";
20346
+ return true ? "0.7.14" : "dev";
20346
20347
  } catch {
20347
20348
  return "dev";
20348
20349
  }
@@ -22505,7 +22506,7 @@ var init_sync_client = __esm({
22505
22506
  ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
22506
22507
  } catch {
22507
22508
  try {
22508
- if (true) ownVersion = "0.7.12";
22509
+ if (true) ownVersion = "0.7.14";
22509
22510
  } catch {
22510
22511
  }
22511
22512
  }
@@ -23982,8 +23983,8 @@ function formatDigest(input, summaryChars = 400) {
23982
23983
  const { row, outcome, related, resumeCmd } = input;
23983
23984
  const lines = [];
23984
23985
  lines.push(`### ${digestTitle(row)}`);
23985
- const when = (row.modified || "").slice(0, 16).replace("T", " ");
23986
- lines.push(`**Project:** ${projectName(row.projectPath) || "(unknown)"} \xB7 **Tool:** ${row.tool || "claude"}` + (when ? ` \xB7 **Modified:** ${when}` : "") + ` \xB7 \`${row.sessionId}\``);
23986
+ const when2 = (row.modified || "").slice(0, 16).replace("T", " ");
23987
+ lines.push(`**Project:** ${projectName(row.projectPath) || "(unknown)"} \xB7 **Tool:** ${row.tool || "claude"}` + (when2 ? ` \xB7 **Modified:** ${when2}` : "") + ` \xB7 \`${row.sessionId}\``);
23987
23988
  if (outcome) {
23988
23989
  const edits = outcome.fileCount > 0 ? ` \xB7 ${outcome.fileCount} file(s) +${outcome.totalLinesAdded}/\u2212${outcome.totalLinesRemoved}` : "";
23989
23990
  const commits = outcome.commits && outcome.commits.totalCommits > 0 ? ` \xB7 ${outcome.commits.totalCommits} commit(s)` : "";
@@ -24043,17 +24044,21 @@ function safeSlice(s, start, end) {
24043
24044
  const hi = end < s.length && /[\uD800-\uDBFF]/.test(s[end - 1] ?? "") ? end + 1 : end;
24044
24045
  return s.slice(lo, hi);
24045
24046
  }
24046
- function body(text, line, opts) {
24047
- if (opts.full || text.length <= BODY_LIMIT)
24048
- return indent(text);
24047
+ function headTail(text, how) {
24048
+ if (text.length <= BODY_LIMIT)
24049
+ return [text];
24049
24050
  const lines = text.split("\n").length;
24050
24051
  const cut = text.length - 2 * EDGE;
24051
- const how = opts.expandHint ? ` ${opts.expandHint(line)}` : "";
24052
24052
  return [
24053
- indent(safeSlice(text, 0, EDGE)),
24054
- `${INDENT}\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how} \u2026`,
24055
- indent(safeSlice(text, text.length - EDGE, text.length))
24056
- ].join("\n");
24053
+ safeSlice(text, 0, EDGE),
24054
+ `\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how ? ` ${how}` : ""} \u2026`,
24055
+ safeSlice(text, text.length - EDGE, text.length)
24056
+ ];
24057
+ }
24058
+ function body(text, line, opts) {
24059
+ if (opts.full)
24060
+ return indent(text);
24061
+ return headTail(text, opts.expandHint?.(line)).map(indent).join("\n");
24057
24062
  }
24058
24063
  function resultText(result) {
24059
24064
  if (typeof result === "string")
@@ -24175,6 +24180,59 @@ function findShowMessages(messages, query) {
24175
24180
  return messages.filter((m) => searchableText(m).toLowerCase().includes(needle));
24176
24181
  }
24177
24182
 
24183
+ // ../engine/dist/mcp/prompts-render.js
24184
+ var fold = (s) => s.toLowerCase().replace(/\s+/g, " ");
24185
+ function when(p, sessionMs) {
24186
+ if (p.ts && p.ts > 0)
24187
+ return p.ts;
24188
+ const parsed = p.tsIso ? Date.parse(p.tsIso) : NaN;
24189
+ return Number.isFinite(parsed) ? parsed : sessionMs;
24190
+ }
24191
+ function newestFirst(a, b) {
24192
+ if (b.at !== a.at)
24193
+ return b.at - a.at;
24194
+ if (a.row.sessionId === b.row.sessionId)
24195
+ return b.row.line - a.row.line;
24196
+ return 0;
24197
+ }
24198
+ function matching(rows, query) {
24199
+ const q = query ? fold(query.trim()) : "";
24200
+ return rows.filter((p) => p.text && p.text.trim() && (!q || fold(p.text).includes(q)));
24201
+ }
24202
+ function selectPrompts(rows, opts) {
24203
+ 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);
24204
+ }
24205
+ async function collectAcrossSessions(feed, fetchPrompts, opts) {
24206
+ const limit = Math.max(0, opts.limit);
24207
+ let kept = [];
24208
+ for (const s of feed) {
24209
+ if (kept.length >= limit && s.modifiedMs < kept[kept.length - 1].at)
24210
+ break;
24211
+ const rows = await fetchPrompts(s.sessionId);
24212
+ if (!rows)
24213
+ continue;
24214
+ for (const row of matching(rows, opts.query))
24215
+ kept.push({ row, at: when(row, s.modifiedMs) });
24216
+ kept = kept.sort(newestFirst).slice(0, limit);
24217
+ }
24218
+ return kept.map((x) => x.row);
24219
+ }
24220
+ function renderPrompts(rows, opts) {
24221
+ const what = opts.query ? `, containing "${opts.query.trim()}"` : "";
24222
+ const lines = [`# User prompts (${rows.length}${what}, newest first)
24223
+ `];
24224
+ for (const p of rows) {
24225
+ const t = p.tsIso?.slice(0, 16).replace("T", " ") || "";
24226
+ const markerSuffix = opts.withMarkers && p.markers.length ? ` _[${p.markers.join(", ")}]_` : "";
24227
+ lines.push(`- **${p.sessionId}** L${p.line}${t ? ` \xB7 ${t}` : ""}${markerSuffix}`);
24228
+ const how = `Call recall_show with session_id "${p.sessionId}" and expand_line ${p.line} for the whole text.`;
24229
+ for (const part of headTail(p.text, how)) {
24230
+ lines.push(part.split("\n").map((l) => ` ${l}`).join("\n"));
24231
+ }
24232
+ }
24233
+ return lines.join("\n");
24234
+ }
24235
+
24178
24236
  // ../engine/dist/mcp/show-freshness.js
24179
24237
  init_tool_backend();
24180
24238
  init_transcript();
@@ -24411,14 +24469,14 @@ function trialEndingBanner(state2, serverBase) {
24411
24469
  const days = state2.trialDaysLeft;
24412
24470
  if (typeof days !== "number" || days > TRIAL_WARN_DAYS)
24413
24471
  return null;
24414
- const when = days <= 0 ? "today" : days === 1 ? "tomorrow" : `in ${days} days`;
24472
+ const when2 = days <= 0 ? "today" : days === 1 ? "tomorrow" : `in ${days} days`;
24415
24473
  const account = `${serverBase.replace(/\/+$/, "")}/app?view=account`;
24416
24474
  return [
24417
24475
  // No trial LENGTH here. It is FREE_TRIAL_DAYS on the server, so a "7-day
24418
24476
  // trial" written into the CLI is a number that drifts the first time that is
24419
24477
  // tuned — and this file already carries two sentences that went stale that
24420
24478
  // exact way. Days REMAINING comes from the server and is always right.
24421
- `\u23F3 TRIAL ENDS ${when.toUpperCase()} \u2014 this chat-recall account is on the free trial.`,
24479
+ `\u23F3 TRIAL ENDS ${when2.toUpperCase()} \u2014 this chat-recall account is on the free trial.`,
24422
24480
  "When it ends, recall switches off: searches and session reads stop, and new sessions stop syncing.",
24423
24481
  "Nothing is deleted \u2014 the history is kept and export keeps working.",
24424
24482
  `Tell the user now, and give them this link to subscribe or see the account: ${account}`
@@ -25200,7 +25258,8 @@ var RecallImprovementsSchema = z.object({
25200
25258
  var RecallUserPromptsSchema = z.object({
25201
25259
  session_id: z.string().optional().describe("If set, only that session's prompts"),
25202
25260
  since_days: z.number().optional().default(7).describe("When session_id is omitted, look back this many days"),
25203
- limit: z.number().optional().default(50).describe("Maximum prompts to return"),
25261
+ limit: z.number().optional().default(50).describe("Maximum prompts to return, newest first"),
25262
+ query: z.string().optional().describe("Keep only the prompts that contain this text (no case)"),
25204
25263
  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.")
25205
25264
  });
25206
25265
  function resolveRecordScope(params) {
@@ -26299,15 +26358,18 @@ write.`,
26299
26358
  // ── User-prompts / decision-record ──
26300
26359
  {
26301
26360
  name: "recall_user_prompts",
26302
- description: `List the human-typed prompts from a session (or recent sessions). Tool results
26303
- and system banners are stripped \u2014 you get only what the user actually wrote. Useful for "what was
26304
- I asking yesterday?" or "what did I tell the agent in this session?".`,
26361
+ description: `List the human-typed prompts from a session (or recent sessions), newest first.
26362
+ Tool results and system banners are stripped \u2014 you get only what the user actually wrote. Useful for
26363
+ "what was I asking yesterday?" or "what did I tell the agent in this session?". Pass \`query\` for
26364
+ "did I say X?": it keeps only the prompts that contain that text. Each prompt prints whole up to 2000
26365
+ characters; a longer one prints its start and end and the recall_show expand_line call for the rest.`,
26305
26366
  inputSchema: {
26306
26367
  type: "object",
26307
26368
  properties: {
26308
26369
  session_id: { type: "string", description: "If set, only that session's prompts" },
26309
26370
  since_days: { type: "number", default: 7, description: "How far back to look, in days." },
26310
- limit: { type: "number", default: 50, description: "Maximum prompts to return, newest first." }
26371
+ limit: { type: "number", default: 50, description: "Maximum prompts to return, newest first." },
26372
+ query: { type: "string", description: "Keep only the prompts that contain this text. Case and runs of whitespace are ignored." }
26311
26373
  }
26312
26374
  }
26313
26375
  },
@@ -27472,9 +27534,9 @@ _Edits stayed local or window was off \u2014 try increasing buffer_minutes._` }]
27472
27534
  const r = remote.results[i];
27473
27535
  const kind = r.chunkType?.match(/:(\w+):imp([0-9])/);
27474
27536
  const tag = kind ? ` \xB7 ${kind[1]} (imp${kind[2]})` : "";
27475
- const when = r.mtime ? ` \xB7 ${new Date(r.mtime).toISOString().slice(0, 10)}` : "";
27537
+ const when2 = r.mtime ? ` \xB7 ${new Date(r.mtime).toISOString().slice(0, 10)}` : "";
27476
27538
  lines.push(`## #${i + 1} [${r.sourceType}${tag}] ${(r.title || r.itemId).slice(0, 90)}`);
27477
- lines.push(`**id:** \`${r.itemId}\`${when}${r.projectPath ? ` \xB7 ${r.projectPath}` : ""}`);
27539
+ lines.push(`**id:** \`${r.itemId}\`${when2}${r.projectPath ? ` \xB7 ${r.projectPath}` : ""}`);
27478
27540
  if (r.sourceType === "session")
27479
27541
  lines.push(`_fetch: recall_context / recall_show "${r.itemId}"_`);
27480
27542
  const snippets = (r.matchedChunks?.length ? r.matchedChunks.map((c) => c.text) : [r.text]).filter(Boolean).slice(0, 3);
@@ -27766,8 +27828,8 @@ _Edits stayed local or window was off \u2014 try increasing buffer_minutes._` }]
27766
27828
  const total = rows.reduce((n, r) => n + r.sessions, 0);
27767
27829
  lines.push(`## ${member} \u2014 ${total} session(s)`);
27768
27830
  for (const r of [...rows].sort((x, y) => y.lastMtime - x.lastMtime)) {
27769
- const when = r.lastMtime ? new Date(r.lastMtime).toISOString().slice(0, 10) : "????";
27770
- lines.push(`- ${r.projectId} \xB7 ${r.sessions} session(s) \xB7 last active ${when}`);
27831
+ const when2 = r.lastMtime ? new Date(r.lastMtime).toISOString().slice(0, 10) : "????";
27832
+ lines.push(`- ${r.projectId} \xB7 ${r.sessions} session(s) \xB7 last active ${when2}`);
27771
27833
  }
27772
27834
  lines.push("");
27773
27835
  }
@@ -28448,54 +28510,35 @@ ${JSON.stringify(r, null, 2)}
28448
28510
  case "recall_user_prompts": {
28449
28511
  const params = RecallUserPromptsSchema.parse(args);
28450
28512
  requireRemote();
28451
- const renderPrompts = (rows) => {
28452
- const lines = [`# User prompts (${rows.length})
28453
- `];
28454
- for (const p of rows) {
28455
- const t = p.tsIso?.slice(0, 16).replace("T", " ") || "";
28456
- const snippet = p.text.length > 240 ? p.text.slice(0, 240) + "\u2026" : p.text;
28457
- const markerSuffix = params.with_markers && p.markers.length ? ` _[${p.markers.join(", ")}]_` : "";
28458
- lines.push(`- **${p.sessionId}** L${p.line}${t ? ` \xB7 ${t}` : ""}${markerSuffix}`);
28459
- lines.push(` ${snippet.replace(/\n/g, " ")}`);
28460
- }
28461
- return lines.join("\n");
28462
- };
28513
+ const toRows = (sessionId, prompts) => prompts.map((p) => ({ sessionId, line: p.line, ts: p.ts, tsIso: p.tsIso, markers: p.markers || [], text: p.text }));
28514
+ const select = { limit: params.limit, query: params.query };
28515
+ const render = (rows) => renderPrompts(rows, { withMarkers: params.with_markers, query: params.query });
28516
+ const none = params.query ? `No user prompts contain "${params.query.trim()}".` : "No user prompts found.";
28463
28517
  if (params.session_id) {
28464
28518
  const soft = await remoteGetSoft(`/api/conversations/${encodeURIComponent(params.session_id)}/markers`);
28465
28519
  if (!soft.data) {
28466
28520
  return { content: [{ type: "text", text: soft.message || (soft.status === 404 ? `Session ${params.session_id} not found.` : `Prompts not synced yet for ${params.session_id}.`) }] };
28467
28521
  }
28468
- 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 }));
28469
- if (rows.length === 0) {
28470
- return { content: [{ type: "text", text: "No user prompts found." }] };
28471
- }
28472
- return { content: [{ type: "text", text: renderPrompts(rows) }] };
28522
+ const rows = selectPrompts(toRows(params.session_id, soft.data.prompts), select);
28523
+ return { content: [{ type: "text", text: rows.length ? render(rows) : none }] };
28473
28524
  }
28474
28525
  const sinceHours = params.since_days * 24;
28475
28526
  const recent = await remoteGetQS("/api/conversations/recent", { limit: 200, since_hours: sinceHours });
28476
- const sessionIds = (recent.sessions || []).map((s) => s.sessionId);
28477
- if (sessionIds.length === 0) {
28478
- return { content: [{ type: "text", text: "No user prompts found." }] };
28479
- }
28480
- const collected = [];
28481
- for (const sid of sessionIds) {
28482
- if (collected.length >= params.limit)
28483
- break;
28527
+ const feed = (recent.sessions || []).map((s) => ({
28528
+ sessionId: s.sessionId,
28529
+ modifiedMs: s.fileMtime || (s.modified ? Date.parse(s.modified) : 0) || 0
28530
+ }));
28531
+ if (feed.length === 0) {
28532
+ return { content: [{ type: "text", text: none }] };
28533
+ }
28534
+ const collected = await collectAcrossSessions(feed, async (sid) => {
28484
28535
  const soft = await remoteGetSoft(`/api/conversations/${encodeURIComponent(sid)}/markers`);
28485
- if (!soft.data)
28486
- continue;
28487
- for (const p of soft.data.prompts) {
28488
- if (!p.text || !p.text.trim())
28489
- continue;
28490
- collected.push({ sessionId: sid, line: p.line, tsIso: p.tsIso, markers: p.markers || [], text: p.text });
28491
- if (collected.length >= params.limit)
28492
- break;
28493
- }
28494
- }
28536
+ return soft.data ? toRows(sid, soft.data.prompts) : null;
28537
+ }, select);
28495
28538
  if (collected.length === 0) {
28496
- return { content: [{ type: "text", text: "No user prompts found (none of the recent sessions have synced markers yet)." }] };
28539
+ return { content: [{ type: "text", text: params.query ? none : "No user prompts found (none of the recent sessions have synced markers yet)." }] };
28497
28540
  }
28498
- return { content: [{ type: "text", text: renderPrompts(collected) }] };
28541
+ return { content: [{ type: "text", text: render(collected) }] };
28499
28542
  }
28500
28543
  // ── Decision recording ────────────────────────────────────
28501
28544
  case "recall_decision_record": {
@@ -29091,9 +29134,9 @@ _Tell the user \u2014 this changes how future sessions treat their repo._` }] };
29091
29134
  }
29092
29135
  const lines = [`# Files resembling \`${params.filename}\` (${data.hits.length})`, "_You may have built this before._\n"];
29093
29136
  for (const h of data.hits) {
29094
- const when = h.mtime ? new Date(h.mtime).toISOString().slice(0, 10) : "";
29137
+ const when2 = h.mtime ? new Date(h.mtime).toISOString().slice(0, 10) : "";
29095
29138
  lines.push(`- \`${h.file}\` \u2014 ${h.reason} (score ${h.score})`);
29096
- lines.push(` ${h.project || "(no project)"} \xB7 ${h.sessionId}${when ? ` \xB7 ${when}` : ""}`);
29139
+ lines.push(` ${h.project || "(no project)"} \xB7 ${h.sessionId}${when2 ? ` \xB7 ${when2}` : ""}`);
29097
29140
  }
29098
29141
  return { content: [{ type: "text", text: lines.join("\n") }] };
29099
29142
  }
@@ -29135,8 +29178,8 @@ _Tell the user \u2014 this changes how future sessions treat their repo._` }] };
29135
29178
  const lines = [`# ${params.source_type} items (${data.items.length})
29136
29179
  `];
29137
29180
  for (const it of data.items) {
29138
- const when = it.mtime ? new Date(it.mtime).toISOString().slice(0, 10) : "";
29139
- lines.push(`- **${it.id}**${it.title ? ` \u2014 ${it.title}` : ""}${when ? ` \xB7 ${when}` : ""}`);
29181
+ const when2 = it.mtime ? new Date(it.mtime).toISOString().slice(0, 10) : "";
29182
+ lines.push(`- **${it.id}**${it.title ? ` \u2014 ${it.title}` : ""}${when2 ? ` \xB7 ${when2}` : ""}`);
29140
29183
  }
29141
29184
  return { content: [{ type: "text", text: lines.join("\n") }] };
29142
29185
  }
@@ -29454,7 +29497,7 @@ function installCrashGuards() {
29454
29497
  reportClientEvent2("mcp_crash", { message: err.stack || err.message });
29455
29498
  });
29456
29499
  }
29457
- var RELAY_VERSION = true ? "0.7.12" : "0.0.0";
29500
+ var RELAY_VERSION = true ? "0.7.14" : "0.0.0";
29458
29501
  function daemonIdleSecs() {
29459
29502
  const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
29460
29503
  if (raw === void 0) return 900;
@@ -5829,7 +5829,7 @@ function markersFromTurns(sessionId, turns) {
5829
5829
  const prompts = turns.filter((t) => t.kind === "user" && t.text).map((t) => ({ line: t.line, ts: t.ts, tsIso: t.tsIso, ...markPrompt(t.text) }));
5830
5830
  return { v: MARKERS_VERSION, sessionId, prompts, summary: summarizeMarkers(prompts) };
5831
5831
  }
5832
- var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION;
5832
+ var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION, MARKERS_TURN_OPTS;
5833
5833
  var init_session_sentiment = __esm({
5834
5834
  "../engine/dist/core/session-sentiment.js"() {
5835
5835
  "use strict";
@@ -5843,6 +5843,7 @@ var init_session_sentiment = __esm({
5843
5843
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
5844
5844
  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;
5845
5845
  MARKERS_VERSION = 3;
5846
+ MARKERS_TURN_OPTS = { maxTurns: 5e4, userMax: Number.POSITIVE_INFINITY };
5846
5847
  }
5847
5848
  });
5848
5849
 
@@ -6068,6 +6069,7 @@ function shorten(text, max) {
6068
6069
  function extractTurnsFromEvents(sessionId, events, opts = {}) {
6069
6070
  const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
6070
6071
  const assistantMax = opts.assistantMax ?? DEFAULT_ASSISTANT_MAX;
6072
+ const userMax = opts.userMax ?? DEFAULT_USER_MAX;
6071
6073
  const turns = [];
6072
6074
  let startMs = 0, endMs = 0;
6073
6075
  let found = false;
@@ -6085,7 +6087,7 @@ function extractTurnsFromEvents(sessionId, events, opts = {}) {
6085
6087
  ts: e.ts,
6086
6088
  tsIso: e.tsIso,
6087
6089
  line: e.line,
6088
- text: shorten(e.text, DEFAULT_USER_MAX)
6090
+ text: shorten(e.text, userMax)
6089
6091
  });
6090
6092
  } else if (e.kind === "assistant_text" && e.text) {
6091
6093
  turns.push({
@@ -9444,7 +9446,7 @@ function extractTurnsAny(sessionId, opts = {}) {
9444
9446
  const backend = getBackendForId(sessionId);
9445
9447
  if (!backend)
9446
9448
  return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
9447
- return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
9449
+ return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
9448
9450
  }
9449
9451
  function replaySessionAny(sessionId) {
9450
9452
  const backend = getBackendForId(sessionId);
@@ -10493,12 +10495,14 @@ function lineKey(line) {
10493
10495
  try {
10494
10496
  const o = JSON.parse(line);
10495
10497
  if (typeof o?.uuid === "string" && o.uuid)
10496
- return { key: `u:${o.uuid}`, singleton: false };
10497
- if (typeof o?.type === "string" && SINGLETON_META.has(o.type))
10498
- return { key: `m:${o.type}`, singleton: true };
10498
+ return { key: `u:${o.uuid}`, meta: false, json: true };
10499
+ return { key: `h:${sha1(line)}`, meta: typeof o?.type === "string" && STATE_META.has(o.type), json: true };
10499
10500
  } catch {
10500
10501
  }
10501
- return { key: `h:${sha1(line)}`, singleton: false };
10502
+ return { key: `h:${sha1(line)}`, meta: false, json: false };
10503
+ }
10504
+ function isHalfWritten(line, currentLines) {
10505
+ return currentLines.some((c) => c.length > line.length && c.startsWith(line));
10502
10506
  }
10503
10507
  function mergeLineText(shadowText, currentText) {
10504
10508
  const shadowLines = shadowText.split("\n").filter((l) => l.trim());
@@ -10506,27 +10510,24 @@ function mergeLineText(shadowText, currentText) {
10506
10510
  const currentKeys = /* @__PURE__ */ new Set();
10507
10511
  for (const l of currentLines)
10508
10512
  currentKeys.add(lineKey(l).key);
10509
- const shadowKeys = /* @__PURE__ */ new Set();
10510
- for (const l of shadowLines)
10511
- shadowKeys.add(lineKey(l).key);
10512
- let recovered = 0;
10513
- for (const k of shadowKeys)
10514
- if (!currentKeys.has(k))
10515
- recovered++;
10516
- if (recovered === 0) {
10517
- return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
10518
- }
10519
- const out = [];
10520
- const emitted = /* @__PURE__ */ new Set();
10513
+ const kept = [];
10514
+ const keptKeys = /* @__PURE__ */ new Set();
10515
+ const counted = /* @__PURE__ */ new Set();
10521
10516
  for (const l of shadowLines) {
10522
- const { key, singleton } = lineKey(l);
10523
- if (singleton && currentKeys.has(key))
10524
- continue;
10525
- if (emitted.has(key))
10517
+ const { key, meta, json } = lineKey(l);
10518
+ if (!json && isHalfWritten(l, currentLines))
10526
10519
  continue;
10527
- out.push(l);
10528
- emitted.add(key);
10520
+ kept.push(l);
10521
+ keptKeys.add(key);
10522
+ if (!meta && !currentKeys.has(key))
10523
+ counted.add(key);
10524
+ }
10525
+ const recovered = counted.size;
10526
+ if (recovered === 0) {
10527
+ return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
10529
10528
  }
10529
+ const out = [...kept];
10530
+ const emitted = keptKeys;
10530
10531
  for (const l of currentLines) {
10531
10532
  const { key } = lineKey(l);
10532
10533
  if (emitted.has(key))
@@ -10536,13 +10537,13 @@ function mergeLineText(shadowText, currentText) {
10536
10537
  }
10537
10538
  return { text: out.join("\n") + "\n", recovered, totalRecords: out.length };
10538
10539
  }
10539
- var SINGLETON_META;
10540
+ var STATE_META;
10540
10541
  var init_shadow = __esm({
10541
10542
  "../engine/dist/transcript/shadow.js"() {
10542
10543
  "use strict";
10543
10544
  init_paths();
10544
10545
  init_raw();
10545
- SINGLETON_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
10546
+ STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
10546
10547
  }
10547
10548
  });
10548
10549
 
@@ -11114,7 +11115,7 @@ function collectDerivedRows(ref, mtime, count, maxRowBytes) {
11114
11115
  if (!fullOutcome) fullOutcome = computeOutcome(id);
11115
11116
  if (fullOutcome.found) data = fullOutcome.commits;
11116
11117
  } else if (kind === "markers") {
11117
- const turns = extractTurnsAny(id, { maxTurns: 5e4 });
11118
+ const turns = extractTurnsAny(id, MARKERS_TURN_OPTS);
11118
11119
  data = markersFromTurns(id, turns.turns);
11119
11120
  }
11120
11121
  } catch {
package/dist/watch.js CHANGED
@@ -6850,12 +6850,14 @@ function lineKey(line) {
6850
6850
  try {
6851
6851
  const o = JSON.parse(line);
6852
6852
  if (typeof o?.uuid === "string" && o.uuid)
6853
- return { key: `u:${o.uuid}`, singleton: false };
6854
- if (typeof o?.type === "string" && SINGLETON_META.has(o.type))
6855
- return { key: `m:${o.type}`, singleton: true };
6853
+ return { key: `u:${o.uuid}`, meta: false, json: true };
6854
+ return { key: `h:${sha1(line)}`, meta: typeof o?.type === "string" && STATE_META.has(o.type), json: true };
6856
6855
  } catch {
6857
6856
  }
6858
- return { key: `h:${sha1(line)}`, singleton: false };
6857
+ return { key: `h:${sha1(line)}`, meta: false, json: false };
6858
+ }
6859
+ function isHalfWritten(line, currentLines) {
6860
+ return currentLines.some((c) => c.length > line.length && c.startsWith(line));
6859
6861
  }
6860
6862
  function mergeLineText(shadowText, currentText) {
6861
6863
  const shadowLines = shadowText.split("\n").filter((l) => l.trim());
@@ -6863,27 +6865,24 @@ function mergeLineText(shadowText, currentText) {
6863
6865
  const currentKeys = /* @__PURE__ */ new Set();
6864
6866
  for (const l of currentLines)
6865
6867
  currentKeys.add(lineKey(l).key);
6866
- const shadowKeys = /* @__PURE__ */ new Set();
6867
- for (const l of shadowLines)
6868
- shadowKeys.add(lineKey(l).key);
6869
- let recovered = 0;
6870
- for (const k of shadowKeys)
6871
- if (!currentKeys.has(k))
6872
- recovered++;
6873
- if (recovered === 0) {
6874
- return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
6875
- }
6876
- const out = [];
6877
- const emitted = /* @__PURE__ */ new Set();
6868
+ const kept = [];
6869
+ const keptKeys = /* @__PURE__ */ new Set();
6870
+ const counted = /* @__PURE__ */ new Set();
6878
6871
  for (const l of shadowLines) {
6879
- const { key: key2, singleton } = lineKey(l);
6880
- if (singleton && currentKeys.has(key2))
6872
+ const { key: key2, meta, json } = lineKey(l);
6873
+ if (!json && isHalfWritten(l, currentLines))
6881
6874
  continue;
6882
- if (emitted.has(key2))
6883
- continue;
6884
- out.push(l);
6885
- emitted.add(key2);
6875
+ kept.push(l);
6876
+ keptKeys.add(key2);
6877
+ if (!meta && !currentKeys.has(key2))
6878
+ counted.add(key2);
6879
+ }
6880
+ const recovered = counted.size;
6881
+ if (recovered === 0) {
6882
+ return { text: currentText.endsWith("\n") ? currentText : currentText + "\n", recovered: 0, totalRecords: currentKeys.size };
6886
6883
  }
6884
+ const out = [...kept];
6885
+ const emitted = keptKeys;
6887
6886
  for (const l of currentLines) {
6888
6887
  const { key: key2 } = lineKey(l);
6889
6888
  if (emitted.has(key2))
@@ -7068,14 +7067,14 @@ function shadowSizeOnDisk(tool, rawId) {
7068
7067
  return 0;
7069
7068
  }
7070
7069
  }
7071
- var WHOLE_FILE_TOOLS, SINGLETON_META, lastLiveFingerprint;
7070
+ var WHOLE_FILE_TOOLS, STATE_META, lastLiveFingerprint;
7072
7071
  var init_shadow = __esm({
7073
7072
  "../engine/dist/transcript/shadow.js"() {
7074
7073
  "use strict";
7075
7074
  init_paths();
7076
7075
  init_raw();
7077
7076
  WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
7078
- SINGLETON_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
7077
+ STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
7079
7078
  lastLiveFingerprint = /* @__PURE__ */ new Map();
7080
7079
  }
7081
7080
  });
@@ -7457,7 +7456,7 @@ function markersFromTurns(sessionId, turns) {
7457
7456
  const prompts = turns.filter((t) => t.kind === "user" && t.text).map((t) => ({ line: t.line, ts: t.ts, tsIso: t.tsIso, ...markPrompt(t.text) }));
7458
7457
  return { v: MARKERS_VERSION, sessionId, prompts, summary: summarizeMarkers(prompts) };
7459
7458
  }
7460
- var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION;
7459
+ var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION, MARKERS_TURN_OPTS;
7461
7460
  var init_session_sentiment = __esm({
7462
7461
  "../engine/dist/core/session-sentiment.js"() {
7463
7462
  "use strict";
@@ -7471,6 +7470,7 @@ var init_session_sentiment = __esm({
7471
7470
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
7472
7471
  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;
7473
7472
  MARKERS_VERSION = 3;
7473
+ MARKERS_TURN_OPTS = { maxTurns: 5e4, userMax: Number.POSITIVE_INFINITY };
7474
7474
  }
7475
7475
  });
7476
7476
 
@@ -7646,7 +7646,7 @@ function extractTurnsAny(sessionId, opts = {}) {
7646
7646
  const backend = getBackendForId(sessionId);
7647
7647
  if (!backend)
7648
7648
  return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
7649
- return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
7649
+ return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
7650
7650
  }
7651
7651
  function replaySessionAny(sessionId) {
7652
7652
  const backend = getBackendForId(sessionId);
@@ -8054,6 +8054,7 @@ function shorten(text, max) {
8054
8054
  function extractTurnsFromEvents(sessionId, events, opts = {}) {
8055
8055
  const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
8056
8056
  const assistantMax = opts.assistantMax ?? DEFAULT_ASSISTANT_MAX;
8057
+ const userMax = opts.userMax ?? DEFAULT_USER_MAX;
8057
8058
  const turns = [];
8058
8059
  let startMs = 0, endMs = 0;
8059
8060
  let found = false;
@@ -8071,7 +8072,7 @@ function extractTurnsFromEvents(sessionId, events, opts = {}) {
8071
8072
  ts: e.ts,
8072
8073
  tsIso: e.tsIso,
8073
8074
  line: e.line,
8074
- text: shorten(e.text, DEFAULT_USER_MAX)
8075
+ text: shorten(e.text, userMax)
8075
8076
  });
8076
8077
  } else if (e.kind === "assistant_text" && e.text) {
8077
8078
  turns.push({
@@ -18483,7 +18484,7 @@ var init_http = __esm({
18483
18484
  CLI_VERSION = JSON.parse(readFileSync32(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18484
18485
  } catch {
18485
18486
  try {
18486
- if (true) CLI_VERSION = "0.7.12";
18487
+ if (true) CLI_VERSION = "0.7.14";
18487
18488
  } catch {
18488
18489
  }
18489
18490
  }
@@ -18527,7 +18528,7 @@ function collectDerivedRows(ref, mtime, count, maxRowBytes) {
18527
18528
  if (!fullOutcome) fullOutcome = computeOutcome(id);
18528
18529
  if (fullOutcome.found) data = fullOutcome.commits;
18529
18530
  } else if (kind === "markers") {
18530
- const turns = extractTurnsAny(id, { maxTurns: 5e4 });
18531
+ const turns = extractTurnsAny(id, MARKERS_TURN_OPTS);
18531
18532
  data = markersFromTurns(id, turns.turns);
18532
18533
  }
18533
18534
  } catch {
@@ -18977,7 +18978,7 @@ var init_telemetry = __esm({
18977
18978
  init_sync_client();
18978
18979
  cliVersion = () => {
18979
18980
  try {
18980
- return true ? "0.7.12" : "dev";
18981
+ return true ? "0.7.14" : "dev";
18981
18982
  } catch {
18982
18983
  return "dev";
18983
18984
  }
@@ -21110,7 +21111,7 @@ var init_sync_client = __esm({
21110
21111
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
21111
21112
  } catch {
21112
21113
  try {
21113
- if (true) ownVersion = "0.7.12";
21114
+ if (true) ownVersion = "0.7.14";
21114
21115
  } catch {
21115
21116
  }
21116
21117
  }
@@ -24247,7 +24248,7 @@ try {
24247
24248
  ownVersion2 = JSON.parse(readFileSync39(join48(dir, "../package.json"), "utf-8")).version || ownVersion2;
24248
24249
  } catch {
24249
24250
  try {
24250
- if (true) ownVersion2 = "0.7.12";
24251
+ if (true) ownVersion2 = "0.7.14";
24251
24252
  } catch {
24252
24253
  }
24253
24254
  }
@@ -24632,7 +24633,7 @@ async function runCollectorMigration(opts) {
24632
24633
  init_sync_ledger();
24633
24634
  init_collector_health();
24634
24635
  import { existsSync as existsSync50, readFileSync as readFileSync45, statSync as statSync45 } from "fs";
24635
- var CLI_VERSION3 = true ? "0.7.12" : "0.0.0";
24636
+ var CLI_VERSION3 = true ? "0.7.14" : "0.0.0";
24636
24637
  var DEBOUNCE_MS = 5e3;
24637
24638
  var PLANS_DIR = claudeBackend.plansDir();
24638
24639
  var TASKS_DIR = claudeBackend.tasksDir();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat-recall",
3
- "version": "0.7.12",
3
+ "version": "0.7.14",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/munhq/chat-recall.git"