chat-recall 0.7.8 → 0.7.10

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
@@ -4568,6 +4568,72 @@ var init_banners = __esm({
4568
4568
  }
4569
4569
  });
4570
4570
 
4571
+ // ../engine/dist/core/claude-prompt-origin.js
4572
+ function originFromText(text) {
4573
+ const head = text.trimStart();
4574
+ for (const [prefix, origin] of INJECTED_PREFIXES) {
4575
+ if (head.startsWith(prefix))
4576
+ return origin;
4577
+ }
4578
+ return "human";
4579
+ }
4580
+ function declaredOrigin(value) {
4581
+ if (!value || typeof value !== "object")
4582
+ return void 0;
4583
+ const kind = value.kind;
4584
+ return typeof kind === "string" && kind ? kind : void 0;
4585
+ }
4586
+ function userRecordOrigin(obj, text) {
4587
+ if (obj.isSidechain === true)
4588
+ return "subagent";
4589
+ const declared = declaredOrigin(obj.origin);
4590
+ if (declared && declared !== "human")
4591
+ return declared;
4592
+ if (obj.isMeta === true)
4593
+ return "meta";
4594
+ if (declared === "human")
4595
+ return "human";
4596
+ return originFromText(text);
4597
+ }
4598
+ function queuedCommandPrompt(obj) {
4599
+ if (obj.type !== "attachment")
4600
+ return null;
4601
+ const att = obj.attachment;
4602
+ if (!att || att.type !== "queued_command")
4603
+ return null;
4604
+ const text = promptText(att.prompt);
4605
+ if (!text)
4606
+ return null;
4607
+ if (obj.isSidechain === true)
4608
+ return { text, origin: "subagent" };
4609
+ const declared = declaredOrigin(att.origin);
4610
+ if (declared)
4611
+ return { text, origin: declared };
4612
+ return { text, origin: att.humanTurn === true ? "human" : originFromText(text) };
4613
+ }
4614
+ function promptText(prompt) {
4615
+ if (typeof prompt === "string")
4616
+ return prompt;
4617
+ if (!Array.isArray(prompt))
4618
+ return "";
4619
+ return prompt.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
4620
+ }
4621
+ function isPersonMessage(m) {
4622
+ return m.role === "user" && !m.origin;
4623
+ }
4624
+ var INJECTED_PREFIXES;
4625
+ var init_claude_prompt_origin = __esm({
4626
+ "../engine/dist/core/claude-prompt-origin.js"() {
4627
+ "use strict";
4628
+ INJECTED_PREFIXES = [
4629
+ ["<task-notification", "task-notification"],
4630
+ ["<agent-message", "peer"],
4631
+ ["Another Claude session sent a message:", "peer"],
4632
+ ["Stop hook feedback:", "hook"]
4633
+ ];
4634
+ }
4635
+ });
4636
+
4571
4637
  // ../engine/dist/core/project-path-match.js
4572
4638
  function canonicalProjectPath(p) {
4573
4639
  return (p || "").trim().replace(/\\/g, "/").replace(/[-_]/g, "/").replace(/\/{2,}/g, "/").replace(/\/+$/, "").toLowerCase();
@@ -5809,7 +5875,8 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
5809
5875
  const filesToParse = hasSubagentsDir(sessionPath) ? [sessionPath, ...getSubagentPaths(sessionPath)] : [sessionPath];
5810
5876
  let userCount = 0;
5811
5877
  const seenPrompts = /* @__PURE__ */ new Set();
5812
- const recordPrompt = (raw, line, dedupe = false) => {
5878
+ const pendingQueued = /* @__PURE__ */ new Set();
5879
+ const recordPrompt = (raw, line, via = "record") => {
5813
5880
  if (userCount >= maxMessages)
5814
5881
  return;
5815
5882
  const withoutReminders = raw.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "");
@@ -5818,8 +5885,14 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
5818
5885
  const cleanedText = stripInjectedBanners(withoutReminders).trim();
5819
5886
  if (cleanedText.length < 10)
5820
5887
  return;
5821
- if (dedupe && seenPrompts.has(cleanedText))
5888
+ if (via === "enqueue") {
5889
+ if (seenPrompts.has(cleanedText))
5890
+ return;
5891
+ pendingQueued.add(cleanedText);
5892
+ } else if (pendingQueued.has(cleanedText)) {
5893
+ pendingQueued.delete(cleanedText);
5822
5894
  return;
5895
+ }
5823
5896
  seenPrompts.add(cleanedText);
5824
5897
  content.userMessages.push({ text: cleanedText, lineNumber: line, contentType: "user" });
5825
5898
  userCount++;
@@ -5904,12 +5977,16 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
5904
5977
  text = textParts.join("\n");
5905
5978
  }
5906
5979
  }
5907
- if (text)
5980
+ if (text && userRecordOrigin(obj, text) === "human")
5908
5981
  recordPrompt(text, lineNum);
5909
5982
  } else if (msgType === "queue-operation") {
5910
- if (obj.operation === "enqueue" && typeof obj.content === "string") {
5911
- recordPrompt(obj.content, lineNum, true);
5983
+ if (obj.operation === "enqueue" && typeof obj.content === "string" && originFromText(obj.content) === "human") {
5984
+ recordPrompt(obj.content, lineNum, "enqueue");
5912
5985
  }
5986
+ } else if (msgType === "attachment") {
5987
+ const queued = queuedCommandPrompt(obj);
5988
+ if (queued?.origin === "human")
5989
+ recordPrompt(queued.text, lineNum, "delivery");
5913
5990
  } else if (msgType === "assistant") {
5914
5991
  const msg = obj.message;
5915
5992
  if (!msg || typeof msg !== "object")
@@ -6013,6 +6090,7 @@ var init_session = __esm({
6013
6090
  "../engine/dist/parsers/session.js"() {
6014
6091
  "use strict";
6015
6092
  init_banners();
6093
+ init_claude_prompt_origin();
6016
6094
  init_claude2();
6017
6095
  init_tool_paths();
6018
6096
  init_project_dir_name();
@@ -6177,7 +6255,16 @@ function messagesFromRawLines(raw) {
6177
6255
  const cleaned = stripBanners(text);
6178
6256
  if (!cleaned)
6179
6257
  continue;
6180
- messages.push({ line, role: "user", content: cleaned, timestamp });
6258
+ const origin = userRecordOrigin(obj, cleaned);
6259
+ messages.push({ line, role: "user", content: cleaned, timestamp, ...origin === "human" ? {} : { origin } });
6260
+ } else if (obj.type === "attachment") {
6261
+ const queued = queuedCommandPrompt(obj);
6262
+ if (!queued)
6263
+ continue;
6264
+ const cleaned = stripBanners(queued.text);
6265
+ if (!cleaned)
6266
+ continue;
6267
+ messages.push({ line, role: "user", content: cleaned, timestamp, ...queued.origin === "human" ? {} : { origin: queued.origin } });
6181
6268
  } else if (obj.type === "assistant") {
6182
6269
  const blocks = Array.isArray(obj.message?.content) ? obj.message.content : [];
6183
6270
  const thinkingBlock = blocks.find((c) => c?.type === "thinking");
@@ -6273,6 +6360,7 @@ var init_claude = __esm({
6273
6360
  "../engine/dist/transcript/claude.js"() {
6274
6361
  "use strict";
6275
6362
  init_logger();
6363
+ init_claude_prompt_origin();
6276
6364
  log2 = createLogger("transcript-claude");
6277
6365
  INJECTED_BANNERS2 = [
6278
6366
  /MCP issues detected\. ?Run \/mcp list for status\.?/g,
@@ -6885,10 +6973,15 @@ function summarizeMarkers(marked) {
6885
6973
  }
6886
6974
  return counts;
6887
6975
  }
6888
- var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES;
6976
+ function markersFromTurns(sessionId, turns) {
6977
+ 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
+ return { v: MARKERS_VERSION, sessionId, prompts, summary: summarizeMarkers(prompts) };
6979
+ }
6980
+ var PROFANITY, PROFANITY_LOOSE, APPROVAL_HEAD, NEG_HEAD, QUESTION_HEAD, DIRECTIVE_HEAD, CLARIFY_PHRASES, FRUSTRATED_PHRASES, MARKERS_VERSION;
6889
6981
  var init_session_sentiment = __esm({
6890
6982
  "../engine/dist/core/session-sentiment.js"() {
6891
6983
  "use strict";
6984
+ init_claude_prompt_origin();
6892
6985
  PROFANITY = /\b(fuck(?:ing|er|ed)?|shit|ffs|wtf|bullshit|crap|damn(?:it)?|hell)\b/i;
6893
6986
  PROFANITY_LOOSE = /\b(fu[ckj]+(?:ing|er|ed|c?k)?|fukc|fucj|sht|fk|wt[ft])\b/i;
6894
6987
  APPROVAL_HEAD = /^(?:yes|y|ok(?:ay)?|sure|alright|fine|i\s*approve|approved|go\s*(?:on|ahead)|do\s*it|continue|please\s*do|sounds?\s*good|let'?s\s*go|ship\s*it|👍|✓+)\b/i;
@@ -6897,6 +6990,7 @@ var init_session_sentiment = __esm({
6897
6990
  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;
6898
6991
  CLARIFY_PHRASES = /\b(?:wdym|what do you mean|explain|clarify|what is|what's|huh\??|i don'?t understand)\b/i;
6899
6992
  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;
6900
6994
  }
6901
6995
  });
6902
6996
 
@@ -11365,6 +11459,7 @@ var init_claude2 = __esm({
11365
11459
  init_first_prompt();
11366
11460
  init_tail_read();
11367
11461
  init_project_dir_name();
11462
+ init_claude_prompt_origin();
11368
11463
  init_generic_engine();
11369
11464
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
11370
11465
  ClaudeBackend = class {
@@ -11672,6 +11767,15 @@ var init_claude2 = __esm({
11672
11767
  }
11673
11768
  appendEventsFromText(events, raw, mtime, startLine) {
11674
11769
  let lineNum = startLine;
11770
+ const queued = /* @__PURE__ */ new Map();
11771
+ const superseded = /* @__PURE__ */ new Set();
11772
+ const pushPrompt = (e) => {
11773
+ const pending = queued.get(e.text);
11774
+ const earlier = pending?.shift();
11775
+ if (earlier)
11776
+ superseded.add(earlier);
11777
+ events.push(e);
11778
+ };
11675
11779
  {
11676
11780
  for (const line of raw.split("\n")) {
11677
11781
  lineNum++;
@@ -11687,13 +11791,26 @@ var init_claude2 = __esm({
11687
11791
  const ts = tsIso ? Date.parse(tsIso) || mtime : mtime;
11688
11792
  if (obj.type === "queue-operation") {
11689
11793
  if (obj.operation === "enqueue" && typeof obj.content === "string") {
11690
- const queued = stripReminders(obj.content);
11691
- if (queued && !queued.startsWith("<task-notification")) {
11692
- events.push({ kind: "user", ts, tsIso, line: lineNum, text: queued });
11794
+ const text = stripReminders(obj.content);
11795
+ if (text && originFromText(text) === "human") {
11796
+ const e = { kind: "user", ts, tsIso, line: lineNum, text };
11797
+ events.push(e);
11798
+ const list = queued.get(text);
11799
+ if (list)
11800
+ list.push(e);
11801
+ else
11802
+ queued.set(text, [e]);
11693
11803
  }
11694
11804
  }
11695
11805
  continue;
11696
11806
  }
11807
+ const delivered = queuedCommandPrompt(obj);
11808
+ if (delivered) {
11809
+ const text = stripReminders(delivered.text);
11810
+ if (text && delivered.origin === "human")
11811
+ pushPrompt({ kind: "user", ts, tsIso, line: lineNum, text });
11812
+ continue;
11813
+ }
11697
11814
  if (obj.type === "user") {
11698
11815
  const msg = obj.message;
11699
11816
  const content = msg?.content;
@@ -11728,8 +11845,9 @@ var init_claude2 = __esm({
11728
11845
  }
11729
11846
  }
11730
11847
  const spoken = stripReminders(text);
11731
- if (spoken)
11732
- events.push({ kind: "user", ts, tsIso, line: lineNum, text: spoken });
11848
+ if (spoken && userRecordOrigin(obj, spoken) === "human") {
11849
+ pushPrompt({ kind: "user", ts, tsIso, line: lineNum, text: spoken });
11850
+ }
11733
11851
  for (const tr of toolResults) {
11734
11852
  events.push({
11735
11853
  kind: "tool_result",
@@ -11778,6 +11896,13 @@ var init_claude2 = __esm({
11778
11896
  }
11779
11897
  }
11780
11898
  }
11899
+ if (superseded.size > 0) {
11900
+ let w = 0;
11901
+ for (const e of events)
11902
+ if (!superseded.has(e))
11903
+ events[w++] = e;
11904
+ events.length = w;
11905
+ }
11781
11906
  return lineNum;
11782
11907
  }
11783
11908
  // ── Per-session operations — all delegate to the generic engine ─
@@ -18640,7 +18765,7 @@ var init_http = __esm({
18640
18765
  CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18641
18766
  } catch {
18642
18767
  try {
18643
- if (true) CLI_VERSION = "0.7.8";
18768
+ if (true) CLI_VERSION = "0.7.10";
18644
18769
  } catch {
18645
18770
  }
18646
18771
  }
@@ -18685,8 +18810,7 @@ function collectDerivedRows(ref, mtime, count, maxRowBytes) {
18685
18810
  if (fullOutcome.found) data = fullOutcome.commits;
18686
18811
  } else if (kind === "markers") {
18687
18812
  const turns = extractTurnsAny(id, { maxTurns: 5e4 });
18688
- const prompts = turns.turns.filter((t) => t.kind === "user" && t.text).map((t) => ({ line: t.line, ts: t.ts, tsIso: t.tsIso, ...markPrompt(t.text) }));
18689
- data = { sessionId: id, prompts, summary: summarizeMarkers(prompts) };
18813
+ data = markersFromTurns(id, turns.turns);
18690
18814
  }
18691
18815
  } catch {
18692
18816
  data = null;
@@ -19139,7 +19263,7 @@ var init_telemetry = __esm({
19139
19263
  init_sync_client();
19140
19264
  cliVersion2 = () => {
19141
19265
  try {
19142
- return true ? "0.7.8" : "dev";
19266
+ return true ? "0.7.10" : "dev";
19143
19267
  } catch {
19144
19268
  return "dev";
19145
19269
  }
@@ -19156,9 +19280,13 @@ __export(collector_health_exports, {
19156
19280
  STALE_AFTER_MS: () => STALE_AFTER_MS,
19157
19281
  applySyncOutcome: () => applySyncOutcome,
19158
19282
  collectorHealthPath: () => collectorHealthPath,
19283
+ crashesAtBoot: () => crashesAtBoot,
19284
+ firstSyncInProgress: () => firstSyncInProgress,
19159
19285
  judgeHealth: () => judgeHealth,
19286
+ markCleanExit: () => markCleanExit,
19160
19287
  progressLine: () => progressLine,
19161
19288
  readCollectorHealth: () => readCollectorHealth,
19289
+ recentCrashes: () => recentCrashes,
19162
19290
  reportWalkProgress: () => reportWalkProgress,
19163
19291
  updateCollectorHealth: () => updateCollectorHealth,
19164
19292
  writeCollectorHealth: () => writeCollectorHealth
@@ -19186,7 +19314,11 @@ function progressLine(h) {
19186
19314
  if (!p || p.complete || p.total <= 0)
19187
19315
  return null;
19188
19316
  const pct = Math.min(99, Math.floor(100 * p.done / p.total));
19189
- return `first sync in progress \u2014 ${p.done.toLocaleString()} of ${p.total.toLocaleString()} sessions (${pct}%)`;
19317
+ return `${p.first ? "first sync" : "sync"} in progress \u2014 ${p.done.toLocaleString()} of ${p.total.toLocaleString()} sessions (${pct}%)`;
19318
+ }
19319
+ function firstSyncInProgress(h) {
19320
+ const p = h?.progress;
19321
+ return !!p && p.first === true && !p.complete && p.total > 0;
19190
19322
  }
19191
19323
  function collectorHealthPath() {
19192
19324
  return healthPath();
@@ -19227,6 +19359,20 @@ function reportWalkProgress(p) {
19227
19359
  lastProgressWrite = now;
19228
19360
  updateCollectorHealth({ progress: p });
19229
19361
  }
19362
+ function crashesAtBoot(prior, procStart) {
19363
+ const history = (prior?.crashes ?? []).filter((t) => typeof t === "number");
19364
+ if (!prior)
19365
+ return history;
19366
+ const leftOnPurpose = typeof prior.cleanExitAt === "number" && prior.cleanExitAt >= prior.startedAt;
19367
+ return leftOnPurpose ? history : [...history, procStart];
19368
+ }
19369
+ function recentCrashes(crashes, now = Date.now()) {
19370
+ const cutoff = now - 36e5;
19371
+ return [...new Set(crashes.filter((t) => t > cutoff))].sort((a, b) => a - b).slice(-20);
19372
+ }
19373
+ function markCleanExit(now = Date.now()) {
19374
+ updateCollectorHealth({ cleanExitAt: now });
19375
+ }
19230
19376
  function judgeHealth(h, now = Date.now()) {
19231
19377
  if (!h)
19232
19378
  return { ok: true, summary: null, reasons: [] };
@@ -19236,7 +19382,7 @@ function judgeHealth(h, now = Date.now()) {
19236
19382
  reasons.push(`the collector has not reported for ${ago(silent)} \u2014 it may not be running`);
19237
19383
  }
19238
19384
  if (h.restartsLastHour >= CRASHLOOP_RESTARTS) {
19239
- reasons.push(`it restarted ${h.restartsLastHour} times in the last hour`);
19385
+ reasons.push(`it crashed ${h.restartsLastHour} times in the last hour`);
19240
19386
  }
19241
19387
  const targets = Object.entries(h.targets ?? {});
19242
19388
  const stale = targets.filter(([, t]) => t.lastOkAt === null || now - t.lastOkAt > STALE_AFTER_MS);
@@ -20709,7 +20855,8 @@ async function syncToTargetInScope(cred, opts = {}) {
20709
20855
  }
20710
20856
  trace?.("conversation walk start");
20711
20857
  const walkStartedAt = Date.now();
20712
- reportWalkProgress({ done: 0, total: slice.length, startedAt: walkStartedAt, complete: false });
20858
+ const firstWalk = (ledger ?? getSyncedRows(base3)).size === 0;
20859
+ reportWalkProgress({ done: 0, total: slice.length, startedAt: walkStartedAt, complete: false, first: firstWalk });
20713
20860
  walkProgressForUpload = { done: 0, total: slice.length, complete: false };
20714
20861
  let traceN = 0;
20715
20862
  let considered = 0;
@@ -20717,7 +20864,7 @@ async function syncToTargetInScope(cred, opts = {}) {
20717
20864
  await new Promise((resolve3) => setImmediate(resolve3));
20718
20865
  considered++;
20719
20866
  const walkDone = considered >= slice.length;
20720
- reportWalkProgress({ done: considered, total: slice.length, startedAt: walkStartedAt, complete: walkDone });
20867
+ reportWalkProgress({ done: considered, total: slice.length, startedAt: walkStartedAt, complete: walkDone, first: firstWalk });
20721
20868
  walkProgressForUpload = { done: considered, total: slice.length, complete: walkDone };
20722
20869
  if (trace && ++traceN % 500 === 0) trace(`walk ${traceN}/${slice.length} \xB7 at ${ref.prefixedId}`);
20723
20870
  localSessionIds.add(ref.prefixedId);
@@ -20989,7 +21136,7 @@ async function syncToTargetInScope(cred, opts = {}) {
20989
21136
  }
20990
21137
  }
20991
21138
  if (opts.prune) await post({ prune_empty_sessions: true });
20992
- reportWalkProgress({ done: slice.length, total: slice.length, startedAt: walkStartedAt, complete: true });
21139
+ reportWalkProgress({ done: slice.length, total: slice.length, startedAt: walkStartedAt, complete: true, first: firstWalk });
20993
21140
  walkProgressForUpload = { done: slice.length, total: slice.length, complete: true };
20994
21141
  return {
20995
21142
  uploaded: convBatch.sent + appendResults.uploaded,
@@ -21120,7 +21267,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21120
21267
  if (!transcript) return null;
21121
21268
  const textMessages = transcript.messages.filter((m) => m.role !== "summary" && m.content?.trim());
21122
21269
  if (textMessages.length === 0 && !transcript.messages.some((m) => m.toolCalls?.length)) return null;
21123
- const firstUser = textMessages.find((m) => m.role === "user");
21270
+ const firstUser = textMessages.find(isPersonMessage);
21124
21271
  if (isInternalToolPrompt(firstUser?.content)) return null;
21125
21272
  const count = { redactions: 0 };
21126
21273
  const envelope = redactDeep(
@@ -21129,7 +21276,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21129
21276
  );
21130
21277
  let projectPath = ref.projectPath;
21131
21278
  const meta = { messageCount: textMessages.length };
21132
- if (textMessages.filter((m) => m.role === "user").length <= 1) meta.oneShot = true;
21279
+ if (textMessages.filter(isPersonMessage).length <= 1) meta.oneShot = true;
21133
21280
  try {
21134
21281
  const cwd = ref.fullPath ? readCwdFromJsonl(ref.fullPath) : "";
21135
21282
  if (cwd) projectPath = cwd;
@@ -21281,7 +21428,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21281
21428
  envelope,
21282
21429
  raw_b64,
21283
21430
  raw_size,
21284
- first_prompt: envTexts.find((m) => m.role === "user")?.content?.slice(0, 200),
21431
+ first_prompt: envTexts.find(isPersonMessage)?.content?.slice(0, 200),
21285
21432
  // Byte offset this FULL sync is synced THROUGH (file size at build time,
21286
21433
  // or the end of the bounded tail for an oversized session). The server
21287
21434
  // stores it as the envelope's `o`; the ledger records the SAME value
@@ -21337,7 +21484,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
21337
21484
  bytes: 0
21338
21485
  };
21339
21486
  }
21340
- const firstUser = textMessages.find((m) => m.role === "user");
21487
+ const firstUser = textMessages.find(isPersonMessage);
21341
21488
  if (isInternalToolPrompt(firstUser?.content)) return null;
21342
21489
  const count = { redactions: 0 };
21343
21490
  const envelope = redactDeep(
@@ -21414,6 +21561,7 @@ var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServer
21414
21561
  var init_sync_client = __esm({
21415
21562
  "src/sync-client.ts"() {
21416
21563
  "use strict";
21564
+ init_claude_prompt_origin();
21417
21565
  init_credentials_env();
21418
21566
  init_secret_redactor();
21419
21567
  init_source_discovery();
@@ -21455,7 +21603,7 @@ var init_sync_client = __esm({
21455
21603
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
21456
21604
  } catch {
21457
21605
  try {
21458
- if (true) ownVersion = "0.7.8";
21606
+ if (true) ownVersion = "0.7.10";
21459
21607
  } catch {
21460
21608
  }
21461
21609
  }
@@ -23659,7 +23807,7 @@ function extractCorrections(messages, cap = 5) {
23659
23807
  const seen = /* @__PURE__ */ new Set();
23660
23808
  let userTurns = 0;
23661
23809
  for (const msg of messages) {
23662
- if (msg.role !== "user") continue;
23810
+ if (!isPersonMessage(msg)) continue;
23663
23811
  userTurns++;
23664
23812
  if (userTurns === 1) continue;
23665
23813
  if (isNoiseUserMessage(msg.content)) continue;
@@ -23765,6 +23913,7 @@ var CORRECTION_STARTS, CORRECTION_CONTAINS, TERMINAL_STATUSES;
23765
23913
  var init_escalate = __esm({
23766
23914
  "src/escalate.ts"() {
23767
23915
  "use strict";
23916
+ init_claude_prompt_origin();
23768
23917
  CORRECTION_STARTS = [
23769
23918
  /^no+\b[,.! ]/i,
23770
23919
  /^(nope|wrong|incorrect|not quite|not right)\b/i,
@@ -25616,7 +25765,7 @@ var init_intent_drain = __esm({
25616
25765
  ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
25617
25766
  } catch {
25618
25767
  try {
25619
- if (true) ownVersion2 = "0.7.8";
25768
+ if (true) ownVersion2 = "0.7.10";
25620
25769
  } catch {
25621
25770
  }
25622
25771
  }
@@ -27633,17 +27782,18 @@ var init_bulk_delete = __esm({
27633
27782
  });
27634
27783
 
27635
27784
  // ../engine/dist/core/resume-command.js
27785
+ var SESSION_ID_PREFIXES = {
27786
+ codex: "codex_",
27787
+ opencode: "opencode_",
27788
+ agy: "agy_",
27789
+ cursor: "cursor_",
27790
+ gemini: "gemini_"
27791
+ };
27636
27792
  function toolOfSessionId(sessionId) {
27637
- if (sessionId.startsWith("codex_"))
27638
- return "codex";
27639
- if (sessionId.startsWith("opencode_"))
27640
- return "opencode";
27641
- if (sessionId.startsWith("agy_"))
27642
- return "agy";
27643
- if (sessionId.startsWith("cursor_"))
27644
- return "cursor";
27645
- if (sessionId.startsWith("gemini_"))
27646
- return "gemini";
27793
+ for (const [tool, prefix] of Object.entries(SESSION_ID_PREFIXES)) {
27794
+ if (sessionId.startsWith(prefix))
27795
+ return tool;
27796
+ }
27647
27797
  return "claude";
27648
27798
  }
27649
27799
  function resumeCommandFor(sessionId, tool) {
@@ -27754,7 +27904,7 @@ function renderShowMessages(messages, opts = {}) {
27754
27904
  const calls = msg.toolCalls ?? [];
27755
27905
  if (!text.trim() && !thinking.trim() && !calls.length && msg.thinking !== void 0)
27756
27906
  continue;
27757
- out.push(`**${msg.role}** (line ${msg.line})`);
27907
+ out.push(msg.origin ? `**${msg.role}** (line ${msg.line}, written by the harness: ${msg.origin})` : `**${msg.role}** (line ${msg.line})`);
27758
27908
  if (thinking.trim())
27759
27909
  out.push(`_(thinking)_
27760
27910
  ${thinking}`);
@@ -27768,6 +27918,13 @@ ${thinking}`);
27768
27918
  }
27769
27919
  return out;
27770
27920
  }
27921
+ function nearestMessageLines(messages, line, count = 4) {
27922
+ return [...new Set(messages.map((m) => m.line))].sort((a, b) => Math.abs(a - line) - Math.abs(b - line) || a - b).slice(0, count).sort((a, b) => a - b);
27923
+ }
27924
+ function noMessageAtLine(messages, line, sessionId) {
27925
+ const near = nearestMessageLines(messages, line);
27926
+ return `No message at line ${line} in ${sessionId}. The nearest messages are at lines ${near.join(", ")}.`;
27927
+ }
27771
27928
 
27772
27929
  // src/cli.ts
27773
27930
  init_paths();
@@ -28524,7 +28681,7 @@ program.command("search <query>").description("Search your server for relevant s
28524
28681
  (c) => c.chunkType !== "summary" && c.chunkType !== "first_prompt"
28525
28682
  );
28526
28683
  if (chunk) {
28527
- const label = chunk.chunkType === "assistant" ? "Discussed" : chunk.chunkType === "user_context" ? "Asked about" : chunk.chunkType === "tool_result" ? "Tool result" : "Context";
28684
+ const label = chunk.chunkType === "assistant" ? "Discussed" : chunk.chunkType === "user_context" ? "Asked about" : chunk.chunkType === "tool_result" ? "Tool result" : chunk.chunkType === "harness" ? "Harness message" : "Context";
28528
28685
  let text = chunk.text.replace(/\n/g, " ").trim();
28529
28686
  if (text.length > 150) text = text.slice(0, 150) + "...";
28530
28687
  console.log(` ${chalk2.magenta(label + ":")} ${text}`);
@@ -28590,8 +28747,7 @@ program.command("show <session_id>").description("Show conversation content from
28590
28747
  const line = parseInt(options.line, 10);
28591
28748
  const hit = messagesList.find((m) => m.line === line);
28592
28749
  if (!hit) {
28593
- const lines = messagesList.map((m) => m.line);
28594
- console.log(chalk2.yellow(`No message at line ${options.line}. Its lines run from ${Math.min(...lines)} to ${Math.max(...lines)}.`));
28750
+ console.log(chalk2.yellow(noMessageAtLine(messagesList, line, sessionId)));
28595
28751
  process.exit(1);
28596
28752
  }
28597
28753
  console.log(renderShowMessages([hit], { full: true }).join("\n"));