chat-recall 0.7.23 → 0.7.25

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
@@ -6794,19 +6794,19 @@ function mapJsonStrings(line, fn, escapedOnly) {
6794
6794
  const q = line.indexOf('"', i);
6795
6795
  if (q < 0)
6796
6796
  return out + line.slice(i);
6797
- let j = q + 1;
6798
- while (j < line.length) {
6799
- const c = line.charCodeAt(j);
6797
+ let j2 = q + 1;
6798
+ while (j2 < line.length) {
6799
+ const c = line.charCodeAt(j2);
6800
6800
  if (c === 92)
6801
- j += 2;
6801
+ j2 += 2;
6802
6802
  else if (c === 34)
6803
6803
  break;
6804
6804
  else
6805
- j++;
6805
+ j2++;
6806
6806
  }
6807
- const token = line.slice(q, j + 1);
6807
+ const token = line.slice(q, j2 + 1);
6808
6808
  out += line.slice(i, q);
6809
- i = j + 1;
6809
+ i = j2 + 1;
6810
6810
  const body = token.slice(1, -1);
6811
6811
  const escaped = body.includes("\\");
6812
6812
  if (escapedOnly && !escaped) {
@@ -9372,6 +9372,130 @@ var init_sqlite_reader = __esm({
9372
9372
  }
9373
9373
  });
9374
9374
 
9375
+ // ../engine/dist/core/backends/opencode-v2.js
9376
+ function hasTable(db, name) {
9377
+ return !!db.prepare("SELECT 1 FROM main.sqlite_master WHERE type = 'table' AND name = ?").get(name);
9378
+ }
9379
+ function columnsOf(db, table) {
9380
+ return new Set(db.prepare(`SELECT name FROM pragma_table_info('${table}', 'main')`).all().map((r) => r.name));
9381
+ }
9382
+ function installOpencodeV2Views(db) {
9383
+ if (!hasTable(db, "session_v2") || !hasTable(db, "session_message"))
9384
+ return false;
9385
+ const v2Cols = columnsOf(db, "session_v2");
9386
+ const sessionCols = SESSION_COLUMNS.map((c) => v2Cols.has(c) ? c : `NULL AS ${c}`).join(", ");
9387
+ const hasV1 = hasTable(db, "message") && hasTable(db, "part");
9388
+ const v1Messages = hasV1 ? `SELECT id, session_id, time_created, time_updated, data FROM main.message WHERE session_id NOT IN (${V2_SESSIONS}) UNION ALL ` : "";
9389
+ const v1Parts = hasV1 ? `SELECT id, message_id, session_id, time_created, time_updated, data FROM main.part WHERE session_id NOT IN (${V2_SESSIONS}) UNION ALL ` : "";
9390
+ db.exec(`
9391
+ CREATE TEMP VIEW IF NOT EXISTS session AS SELECT ${sessionCols} FROM main.session_v2;
9392
+
9393
+ CREATE TEMP VIEW IF NOT EXISTS message AS
9394
+ ${v1Messages}
9395
+ SELECT m.id, m.session_id, m.time_created, m.time_updated,
9396
+ CASE m.type
9397
+ WHEN 'user' THEN json_object('role', 'user', 'time', ${j("$.time")})
9398
+ ELSE json_object(
9399
+ 'role', 'assistant',
9400
+ 'agent', json_extract(m.data, '$.agent'),
9401
+ 'modelID', json_extract(m.data, '$.model.id'),
9402
+ 'providerID', json_extract(m.data, '$.model.providerID'),
9403
+ 'cost', coalesce(json_extract(m.data, '$.cost'), 0),
9404
+ 'tokens', ${j("$.tokens")},
9405
+ 'time', ${j("$.time")})
9406
+ END AS data
9407
+ FROM main.session_message m WHERE m.type IN ('user', 'assistant');
9408
+
9409
+ CREATE TEMP VIEW IF NOT EXISTS part AS
9410
+ ${v1Parts}
9411
+ SELECT m.id || ':text', m.id, m.session_id, m.time_created, m.time_updated,
9412
+ json_object('type', 'text', 'text', json_extract(m.data, '$.text'))
9413
+ FROM main.session_message m
9414
+ WHERE m.type = 'user' AND json_extract(m.data, '$.text') IS NOT NULL
9415
+ UNION ALL
9416
+ SELECT m.id || ':' || c.key, m.id, m.session_id, m.time_created + c.key, m.time_updated,
9417
+ CASE json_extract(c.value, '$.type')
9418
+ WHEN 'tool' THEN json_object(
9419
+ 'type', 'tool',
9420
+ 'tool', json_extract(c.value, '$.name'),
9421
+ 'callID', json_extract(c.value, '$.id'),
9422
+ 'state', json_object(
9423
+ 'status', json_extract(c.value, '$.state.status'),
9424
+ 'input', json(coalesce(json_extract(c.value, '$.state.input'), '{}')),
9425
+ 'output', (SELECT group_concat(json_extract(o.value, '$.text'), char(10))
9426
+ FROM json_each(c.value, '$.state.content') o
9427
+ WHERE json_extract(o.value, '$.type') = 'text')))
9428
+ ELSE json_object('type', json_extract(c.value, '$.type'), 'text', json_extract(c.value, '$.text'))
9429
+ END
9430
+ FROM main.session_message m, json_each(m.data, '$.content') c
9431
+ WHERE m.type = 'assistant'
9432
+ UNION ALL
9433
+ SELECT m.id || ':step', m.id, m.session_id,
9434
+ coalesce(json_extract(m.data, '$.time.completed'), m.time_created), m.time_updated,
9435
+ json_object('type', 'step-finish', 'cost', coalesce(json_extract(m.data, '$.cost'), 0), 'tokens', ${j("$.tokens")})
9436
+ FROM main.session_message m WHERE m.type = 'assistant';
9437
+ `);
9438
+ return true;
9439
+ }
9440
+ function openOpencodeDb(path) {
9441
+ const db = openSqliteReadonly(path);
9442
+ if (!db)
9443
+ return null;
9444
+ try {
9445
+ installOpencodeV2Views(db);
9446
+ } catch {
9447
+ }
9448
+ return db;
9449
+ }
9450
+ function openOpencodeDbOrThrow(path) {
9451
+ const db = openSqliteReadonlyOrThrow(path);
9452
+ try {
9453
+ installOpencodeV2Views(db);
9454
+ } catch {
9455
+ }
9456
+ return db;
9457
+ }
9458
+ var SESSION_COLUMNS, j, V2_SESSIONS;
9459
+ var init_opencode_v2 = __esm({
9460
+ "../engine/dist/core/backends/opencode-v2.js"() {
9461
+ "use strict";
9462
+ init_sqlite_reader();
9463
+ SESSION_COLUMNS = [
9464
+ "id",
9465
+ "project_id",
9466
+ "parent_id",
9467
+ "slug",
9468
+ "directory",
9469
+ "title",
9470
+ "version",
9471
+ "share_url",
9472
+ "summary_additions",
9473
+ "summary_deletions",
9474
+ "summary_files",
9475
+ "summary_diffs",
9476
+ "revert",
9477
+ "permission",
9478
+ "time_created",
9479
+ "time_updated",
9480
+ "time_compacting",
9481
+ "time_archived",
9482
+ "workspace_id",
9483
+ "path",
9484
+ "agent",
9485
+ "model",
9486
+ "cost",
9487
+ "tokens_input",
9488
+ "tokens_output",
9489
+ "tokens_reasoning",
9490
+ "tokens_cache_read",
9491
+ "tokens_cache_write",
9492
+ "metadata"
9493
+ ];
9494
+ j = (path, fallback = "'{}'") => `json(coalesce(json_extract(m.data, '${path}'), ${fallback}))`;
9495
+ V2_SESSIONS = "SELECT session_id FROM main.session_message";
9496
+ }
9497
+ });
9498
+
9375
9499
  // ../engine/dist/core/backends/opencode.js
9376
9500
  var opencode_exports = {};
9377
9501
  __export(opencode_exports, {
@@ -9382,13 +9506,13 @@ import { existsSync as existsSync11 } from "fs";
9382
9506
  import { homedir as homedir5 } from "os";
9383
9507
  import { dirname as dirname9, join as join12 } from "path";
9384
9508
  function openReadonly(path) {
9385
- return openSqliteReadonly(path);
9509
+ return openOpencodeDb(path);
9386
9510
  }
9387
9511
  var PREFIX, OpencodeBackend, opencodeBackend;
9388
9512
  var init_opencode = __esm({
9389
9513
  "../engine/dist/core/backends/opencode.js"() {
9390
9514
  "use strict";
9391
- init_sqlite_reader();
9515
+ init_opencode_v2();
9392
9516
  init_tool_paths();
9393
9517
  init_session_outcome();
9394
9518
  init_session_git();
@@ -9679,9 +9803,9 @@ var init_opencode = __esm({
9679
9803
  return [];
9680
9804
  const parts = db.prepare(`
9681
9805
  SELECT p.id, p.message_id, p.time_created, p.data, m.data AS msg_data
9682
- FROM part p JOIN message m ON m.id = p.message_id
9806
+ FROM part p JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON m.id = p.message_id
9683
9807
  WHERE p.session_id = ? ORDER BY p.time_created ASC
9684
- `).all(ocId);
9808
+ `).all(ocId, ocId);
9685
9809
  const events = [];
9686
9810
  let lineNum = 0;
9687
9811
  for (const p of parts) {
@@ -9897,17 +10021,17 @@ var init_opencode = __esm({
9897
10021
  if (!existsSync11(dbPath))
9898
10022
  return null;
9899
10023
  const realId = this.toRawId(id);
9900
- const db = openSqliteReadonlyOrThrow(dbPath);
10024
+ const db = openOpencodeDbOrThrow(dbPath);
9901
10025
  try {
9902
10026
  const session = db.prepare(`SELECT * FROM session WHERE id = ?`).get(realId);
9903
10027
  if (!session)
9904
10028
  return null;
9905
10029
  const rows = db.prepare(`
9906
10030
  SELECT m.id AS message_id, m.data AS message_data, p.id AS part_id, p.data AS part_data, p.time_created
9907
- FROM part p JOIN message m ON p.message_id = m.id
10031
+ FROM part p JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON p.message_id = m.id
9908
10032
  WHERE p.session_id = ?
9909
10033
  ORDER BY p.time_created ASC, p.id ASC
9910
- `).all(realId);
10034
+ `).all(realId, realId);
9911
10035
  const lines = [JSON.stringify({ kind: "session", row: session })];
9912
10036
  for (const r of rows)
9913
10037
  lines.push(JSON.stringify({ kind: "part", row: r }));
@@ -14656,19 +14780,19 @@ function runTrufflehog(binary, filePath, opts = {}) {
14656
14780
  if (!line.trim())
14657
14781
  continue;
14658
14782
  try {
14659
- const j = JSON.parse(line);
14660
- const raw = j.Raw || "";
14783
+ const j2 = JSON.parse(line);
14784
+ const raw = j2.Raw || "";
14661
14785
  if (looksLikeUuid(raw))
14662
14786
  continue;
14663
14787
  out.push({
14664
- rule: j.DetectorName || "unknown",
14665
- line: j.SourceMetadata?.Data?.Filesystem?.line || 0,
14788
+ rule: j2.DetectorName || "unknown",
14789
+ line: j2.SourceMetadata?.Data?.Filesystem?.line || 0,
14666
14790
  preview: mask(raw),
14667
14791
  // trufflehog reports `Verified: true` only when --verification
14668
14792
  // ran AND the call succeeded. With --only-verified set, every
14669
14793
  // returned finding is verified live by definition, so we tag
14670
14794
  // them all true; with --no-verification, leave undefined.
14671
- verified: opts.verifyOnly ? true : j.Verified === true ? true : void 0
14795
+ verified: opts.verifyOnly ? true : j2.Verified === true ? true : void 0
14672
14796
  });
14673
14797
  } catch {
14674
14798
  }
@@ -14721,16 +14845,16 @@ function runTrufflehogDir(binary, dir, opts = {}) {
14721
14845
  if (!line.trim())
14722
14846
  continue;
14723
14847
  try {
14724
- const j = JSON.parse(line);
14725
- const raw = j.Raw || "";
14848
+ const j2 = JSON.parse(line);
14849
+ const raw = j2.Raw || "";
14726
14850
  if (looksLikeUuid(raw))
14727
14851
  continue;
14728
14852
  out.push({
14729
- rule: j.DetectorName || "unknown",
14730
- line: j.SourceMetadata?.Data?.Filesystem?.line || 0,
14853
+ rule: j2.DetectorName || "unknown",
14854
+ line: j2.SourceMetadata?.Data?.Filesystem?.line || 0,
14731
14855
  preview: mask(raw),
14732
- verified: opts.verifyOnly ? true : j.Verified === true ? true : void 0,
14733
- file: j.SourceMetadata?.Data?.Filesystem?.file || ""
14856
+ verified: opts.verifyOnly ? true : j2.Verified === true ? true : void 0,
14857
+ file: j2.SourceMetadata?.Data?.Filesystem?.file || ""
14734
14858
  });
14735
14859
  } catch {
14736
14860
  }
@@ -14898,7 +15022,7 @@ async function parseOpenCodeSubagents(parentSessionId) {
14898
15022
  if (!existsSync53(dbPath))
14899
15023
  return [];
14900
15024
  const realParent = parentSessionId.replace("opencode_", "");
14901
- const db = openSqliteReadonlyOrThrow(dbPath);
15025
+ const db = openOpencodeDbOrThrow(dbPath);
14902
15026
  try {
14903
15027
  const children = db.prepare(`
14904
15028
  SELECT id, title, time_created
@@ -14942,16 +15066,16 @@ async function parseOpenCodeTranscript(sessionId) {
14942
15066
  if (!existsSync53(dbPath))
14943
15067
  return [];
14944
15068
  const realId = sessionId.replace("opencode_", "");
14945
- const db = openSqliteReadonlyOrThrow(dbPath);
15069
+ const db = openOpencodeDbOrThrow(dbPath);
14946
15070
  const messages = [];
14947
15071
  try {
14948
15072
  const parts = db.prepare(`
14949
15073
  SELECT p.data, p.time_created, m.data as msg_data
14950
15074
  FROM part p
14951
- JOIN message m ON p.message_id = m.id
15075
+ JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON p.message_id = m.id
14952
15076
  WHERE p.session_id = ?
14953
15077
  ORDER BY p.time_created ASC
14954
- `).all(realId);
15078
+ `).all(realId, realId);
14955
15079
  let lineNum = 0;
14956
15080
  for (const part of parts) {
14957
15081
  try {
@@ -14982,7 +15106,7 @@ var log5;
14982
15106
  var init_opencode2 = __esm({
14983
15107
  "../engine/dist/transcript/opencode.js"() {
14984
15108
  "use strict";
14985
- init_sqlite_reader();
15109
+ init_opencode_v2();
14986
15110
  init_logger();
14987
15111
  log5 = createLogger("transcript-opencode");
14988
15112
  }
@@ -17651,7 +17775,7 @@ var MAX_CHUNK_CHARS8, OpenCodeSource;
17651
17775
  var init_opencode_source = __esm({
17652
17776
  "../engine/dist/parsers/opencode-source.js"() {
17653
17777
  "use strict";
17654
- init_sqlite_reader();
17778
+ init_opencode_v2();
17655
17779
  init_opencode();
17656
17780
  init_settings();
17657
17781
  MAX_CHUNK_CHARS8 = 2e3;
@@ -17668,7 +17792,7 @@ var init_opencode_source = __esm({
17668
17792
  return;
17669
17793
  let db;
17670
17794
  try {
17671
- db = openSqliteReadonlyOrThrow(this.dbPath);
17795
+ db = openOpencodeDbOrThrow(this.dbPath);
17672
17796
  } catch {
17673
17797
  return;
17674
17798
  }
@@ -17765,17 +17889,17 @@ var init_opencode_source = __esm({
17765
17889
  if (!existsSync30(this.dbPath))
17766
17890
  return [];
17767
17891
  const sessionId = item.id.replace("opencode_", "");
17768
- const db = openSqliteReadonlyOrThrow(this.dbPath);
17892
+ const db = openOpencodeDbOrThrow(this.dbPath);
17769
17893
  const chunks = [];
17770
17894
  try {
17771
17895
  const parts = db.prepare(`
17772
17896
  SELECT p.data, m.data as msg_data
17773
17897
  FROM part p
17774
- JOIN message m ON p.message_id = m.id
17898
+ JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON p.message_id = m.id
17775
17899
  WHERE p.session_id = ?
17776
17900
  AND (p.data LIKE '%"type":"text"%' OR p.data LIKE '%"type":"reasoning"%')
17777
17901
  ORDER BY p.time_created ASC
17778
- `).all(sessionId);
17902
+ `).all(sessionId, sessionId);
17779
17903
  const userTexts = [];
17780
17904
  const assistantTexts = [];
17781
17905
  for (const part of parts) {
@@ -17839,7 +17963,7 @@ var OpenCodeTodoSource;
17839
17963
  var init_opencode_todo_source = __esm({
17840
17964
  "../engine/dist/parsers/opencode-todo-source.js"() {
17841
17965
  "use strict";
17842
- init_sqlite_reader();
17966
+ init_opencode_v2();
17843
17967
  init_opencode();
17844
17968
  init_settings();
17845
17969
  OpenCodeTodoSource = class {
@@ -17855,7 +17979,7 @@ var init_opencode_todo_source = __esm({
17855
17979
  return;
17856
17980
  let db;
17857
17981
  try {
17858
- db = openSqliteReadonlyOrThrow(this.dbPath);
17982
+ db = openOpencodeDbOrThrow(this.dbPath);
17859
17983
  } catch {
17860
17984
  return;
17861
17985
  }
@@ -17903,7 +18027,7 @@ var init_opencode_todo_source = __esm({
17903
18027
  if (!existsSync31(this.dbPath))
17904
18028
  return [];
17905
18029
  const sessionId = item.extra?.sessionId || item.id.replace(`${opencodeBackend.idPrefix}todo_`, "");
17906
- const db = openSqliteReadonlyOrThrow(this.dbPath);
18030
+ const db = openOpencodeDbOrThrow(this.dbPath);
17907
18031
  const chunks = [];
17908
18032
  try {
17909
18033
  const todos = db.prepare(`
@@ -19587,12 +19711,12 @@ var require_foldFlowLines = __commonJS({
19587
19711
  ch = text[i += 1];
19588
19712
  overflow = true;
19589
19713
  }
19590
- const j = i > escEnd + 1 ? i - 2 : escStart - 1;
19591
- if (escapedFolds[j])
19714
+ const j2 = i > escEnd + 1 ? i - 2 : escStart - 1;
19715
+ if (escapedFolds[j2])
19592
19716
  return text;
19593
- folds.push(j);
19594
- escapedFolds[j] = true;
19595
- end = j + endStep;
19717
+ folds.push(j2);
19718
+ escapedFolds[j2] = true;
19719
+ end = j2 + endStep;
19596
19720
  split = void 0;
19597
19721
  } else {
19598
19722
  overflow = true;
@@ -27530,7 +27654,7 @@ var init_http = __esm({
27530
27654
  CLI_VERSION = JSON.parse(readFileSync35(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
27531
27655
  } catch {
27532
27656
  try {
27533
- if (true) CLI_VERSION = "0.7.23";
27657
+ if (true) CLI_VERSION = "0.7.25";
27534
27658
  } catch {
27535
27659
  }
27536
27660
  }
@@ -28024,7 +28148,7 @@ var init_telemetry = __esm({
28024
28148
  init_sync_client();
28025
28149
  cliVersion = () => {
28026
28150
  try {
28027
- return true ? "0.7.23" : "dev";
28151
+ return true ? "0.7.25" : "dev";
28028
28152
  } catch {
28029
28153
  return "dev";
28030
28154
  }
@@ -30389,7 +30513,7 @@ var init_sync_client = __esm({
30389
30513
  ownVersion = JSON.parse(readFileSync39(join46(dir, "../package.json"), "utf-8")).version || ownVersion;
30390
30514
  } catch {
30391
30515
  try {
30392
- if (true) ownVersion = "0.7.23";
30516
+ if (true) ownVersion = "0.7.25";
30393
30517
  } catch {
30394
30518
  }
30395
30519
  }
@@ -32820,16 +32944,16 @@ async function collectCode(opts) {
32820
32944
  ...blastTargets.map((f, i) => call(2e3 + i, "plan_change", { file: f, format: "json" }))
32821
32945
  ]);
32822
32946
  blastTargets.forEach((f, i) => {
32823
- const j = parseJson(pc, 2e3 + i, null);
32824
- if (j && typeof j === "object" && typeof j.fan_in === "number") {
32947
+ const j2 = parseJson(pc, 2e3 + i, null);
32948
+ if (j2 && typeof j2 === "object" && typeof j2.fan_in === "number") {
32825
32949
  blast[f] = {
32826
- fileRole: j.file_role ?? "regular",
32827
- fanIn: j.fan_in ?? 0,
32828
- fanOut: j.fan_out ?? 0,
32829
- direct: j.direct ?? 0,
32830
- transitive: j.transitive ?? 0,
32831
- maxDepth: j.max_depth ?? 0,
32832
- directFiles: Array.isArray(j.direct_files) ? j.direct_files.map(rel).slice(0, 20) : void 0
32950
+ fileRole: j2.file_role ?? "regular",
32951
+ fanIn: j2.fan_in ?? 0,
32952
+ fanOut: j2.fan_out ?? 0,
32953
+ direct: j2.direct ?? 0,
32954
+ transitive: j2.transitive ?? 0,
32955
+ maxDepth: j2.max_depth ?? 0,
32956
+ directFiles: Array.isArray(j2.direct_files) ? j2.direct_files.map(rel).slice(0, 20) : void 0
32833
32957
  };
32834
32958
  }
32835
32959
  });
@@ -33753,7 +33877,7 @@ try {
33753
33877
  ownVersion2 = JSON.parse(readFileSync43(join52(dir, "../package.json"), "utf-8")).version || ownVersion2;
33754
33878
  } catch {
33755
33879
  try {
33756
- if (true) ownVersion2 = "0.7.23";
33880
+ if (true) ownVersion2 = "0.7.25";
33757
33881
  } catch {
33758
33882
  }
33759
33883
  }
@@ -34138,7 +34262,7 @@ async function runCollectorMigration(opts) {
34138
34262
  init_sync_ledger();
34139
34263
  init_collector_health();
34140
34264
  import { existsSync as existsSync52, readFileSync as readFileSync49, statSync as statSync49 } from "fs";
34141
- var CLI_VERSION3 = true ? "0.7.23" : "0.0.0";
34265
+ var CLI_VERSION3 = true ? "0.7.25" : "0.0.0";
34142
34266
  var DEBOUNCE_MS = 5e3;
34143
34267
  var PLANS_DIR = claudeBackend.plansDir();
34144
34268
  var TASKS_DIR = claudeBackend.tasksDir();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat-recall",
3
- "version": "0.7.23",
3
+ "version": "0.7.25",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/munhq/chat-recall.git"
@@ -21,9 +21,13 @@ user.
21
21
 
22
22
  Codex, Antigravity, Cursor, opencode and pi sessions get the same hook. They
23
23
  report at start and at each prompt, and the hook adds waiting messages to the
24
- next prompt. Cursor adds them when the agent stops, as a follow-up prompt. These
25
- tools have no waiter: an idle session reads its messages when its user types
26
- next. Their ids carry the tool's prefix, for example `agy_…` or `opencode_ses_…`.
24
+ next prompt. Cursor adds them when the agent stops, as a follow-up prompt.
25
+ OpenCode and pi also wake an idle session: their plugin starts a turn with the
26
+ message. In Codex and Antigravity, an idle session reads its messages when its
27
+ user types next. Their ids carry the tool's prefix, for example `agy_…` or `opencode_ses_…`.
28
+
29
+ To find a running session, call `recall_session_status`. It lists the live sessions of
30
+ every AI tool. `recall_recent` lists synced transcripts, which can lag behind a live session.
27
31
 
28
32
  A session reaches only the sessions of the same person. A teammate's sessions
29
33
  are not addressable.