chat-logbook 0.7.1 → 0.8.0

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/api/dist/index.js CHANGED
@@ -4758,12 +4758,12 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
4758
4758
  var schema_exports = {};
4759
4759
  __export(schema_exports, {
4760
4760
  archiveMeta: () => archiveMeta,
4761
+ chatScanState: () => chatScanState,
4762
+ chats: () => chats,
4761
4763
  ingestionEvents: () => ingestionEvents,
4762
4764
  messages: () => messages,
4763
4765
  rawMessages: () => rawMessages,
4764
- schemaVersion: () => schemaVersion,
4765
- sessionScanState: () => sessionScanState,
4766
- sessions: () => sessions
4766
+ schemaVersion: () => schemaVersion
4767
4767
  });
4768
4768
 
4769
4769
  // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
@@ -8179,26 +8179,24 @@ var schemaVersion = sqliteTable("schema_version", {
8179
8179
  version: integer("version").primaryKey(),
8180
8180
  appliedAt: integer("applied_at", { mode: "timestamp_ms" }).notNull()
8181
8181
  });
8182
- var sessions = sqliteTable(
8183
- "sessions",
8182
+ var chats = sqliteTable(
8183
+ "chats",
8184
8184
  {
8185
8185
  id: text("id").primaryKey(),
8186
- shortCode: text("short_code").notNull().unique(),
8186
+ chatId: text("chat_id").notNull().unique(),
8187
8187
  agent: text("agent").notNull(),
8188
- sourceSessionId: text("source_session_id").notNull(),
8188
+ sourceId: text("source_id").notNull(),
8189
8189
  firstSeenAt: integer("first_seen_at", { mode: "timestamp_ms" }).notNull(),
8190
8190
  project: text("project")
8191
8191
  },
8192
- (t) => [
8193
- uniqueIndex("sessions_agent_source_idx").on(t.agent, t.sourceSessionId)
8194
- ]
8192
+ (t) => [uniqueIndex("chats_agent_source_idx").on(t.agent, t.sourceId)]
8195
8193
  );
8196
8194
  var rawMessages = sqliteTable(
8197
8195
  "raw_messages",
8198
8196
  {
8199
8197
  id: integer("id").primaryKey({ autoIncrement: true }),
8200
8198
  agent: text("agent").notNull(),
8201
- sessionId: text("session_id").notNull(),
8199
+ sourceId: text("source_id").notNull(),
8202
8200
  sourcePath: text("source_path").notNull(),
8203
8201
  sourceLocator: text("source_locator").notNull(),
8204
8202
  rawPayload: text("raw_payload").notNull(),
@@ -8206,11 +8204,7 @@ var rawMessages = sqliteTable(
8206
8204
  ingestedAt: integer("ingested_at", { mode: "timestamp_ms" }).notNull()
8207
8205
  },
8208
8206
  (t) => [
8209
- uniqueIndex("raw_messages_idem_idx").on(
8210
- t.agent,
8211
- t.sessionId,
8212
- t.payloadHash
8213
- )
8207
+ uniqueIndex("raw_messages_idem_idx").on(t.agent, t.sourceId, t.payloadHash)
8214
8208
  ]
8215
8209
  );
8216
8210
  var messages = sqliteTable(
@@ -8218,7 +8212,7 @@ var messages = sqliteTable(
8218
8212
  {
8219
8213
  id: integer("id").primaryKey({ autoIncrement: true }),
8220
8214
  agent: text("agent").notNull(),
8221
- sessionId: text("session_id").notNull(),
8215
+ sourceId: text("source_id").notNull(),
8222
8216
  messageId: text("message_id").notNull(),
8223
8217
  role: text("role").notNull(),
8224
8218
  ts: integer("ts", { mode: "timestamp_ms" }).notNull(),
@@ -8227,15 +8221,15 @@ var messages = sqliteTable(
8227
8221
  rawId: integer("raw_id").notNull().references(() => rawMessages.id)
8228
8222
  },
8229
8223
  (t) => [
8230
- uniqueIndex("messages_canonical_idx").on(t.agent, t.sessionId, t.messageId)
8224
+ uniqueIndex("messages_canonical_idx").on(t.agent, t.sourceId, t.messageId)
8231
8225
  ]
8232
8226
  );
8233
- var sessionScanState = sqliteTable(
8227
+ var chatScanState = sqliteTable(
8234
8228
  "session_scan_state",
8235
8229
  {
8236
8230
  id: integer("id").primaryKey({ autoIncrement: true }),
8237
8231
  agent: text("agent").notNull(),
8238
- sessionId: text("session_id").notNull(),
8232
+ sourceId: text("source_id").notNull(),
8239
8233
  sourcePath: text("source_path").notNull(),
8240
8234
  lastMtimeMs: integer("last_mtime_ms").notNull(),
8241
8235
  lastSizeBytes: integer("last_size_bytes").notNull(),
@@ -8243,12 +8237,12 @@ var sessionScanState = sqliteTable(
8243
8237
  mode: "timestamp_ms"
8244
8238
  }).notNull()
8245
8239
  },
8246
- (t) => [uniqueIndex("session_scan_state_idx").on(t.agent, t.sessionId)]
8240
+ (t) => [uniqueIndex("session_scan_state_idx").on(t.agent, t.sourceId)]
8247
8241
  );
8248
8242
  var ingestionEvents = sqliteTable("ingestion_events", {
8249
8243
  id: integer("id").primaryKey({ autoIncrement: true }),
8250
8244
  agent: text("agent").notNull(),
8251
- sessionId: text("session_id").notNull(),
8245
+ sourceId: text("source_id").notNull(),
8252
8246
  sourcePath: text("source_path").notNull(),
8253
8247
  eventType: text("event_type").notNull(),
8254
8248
  detail: text("detail", { mode: "json" }).notNull(),
@@ -8256,7 +8250,7 @@ var ingestionEvents = sqliteTable("ingestion_events", {
8256
8250
  });
8257
8251
 
8258
8252
  // src/visibility.ts
8259
- function loadSessionVisibility(metadata2, opts) {
8253
+ function loadChatVisibility(metadata2, opts) {
8260
8254
  const trashed = new Set(metadata2.listDeletedIds());
8261
8255
  const showTrashed = opts.includeTrashed === true;
8262
8256
  return {
@@ -8279,59 +8273,59 @@ function toApiBlock(block) {
8279
8273
  var CLAUDE_CODE_AGENT = "claude-code";
8280
8274
  function createApp({ archive: archive2, metadata: metadata2, webDistDir: webDistDir2 }) {
8281
8275
  const app2 = new Hono2();
8282
- function findArchiveSessionBySourceId(sourceSessionId) {
8283
- return archive2.db.select().from(sessions).where(eq(sessions.sourceSessionId, sourceSessionId)).get();
8276
+ function findArchiveChatBySourceId(sourceId) {
8277
+ return archive2.db.select().from(chats).where(eq(chats.sourceId, sourceId)).get();
8284
8278
  }
8285
- app2.get("/api/sessions", (c) => {
8286
- const visibility = loadSessionVisibility(metadata2, {
8279
+ app2.get("/api/chats", (c) => {
8280
+ const visibility = loadChatVisibility(metadata2, {
8287
8281
  includeTrashed: c.req.query("includeTrashed") === "true"
8288
8282
  });
8289
- const rows = archive2.db.select().from(sessions).all();
8290
- const sessions2 = [];
8283
+ const rows = archive2.db.select().from(chats).all();
8284
+ const chats2 = [];
8291
8285
  for (const row of rows) {
8292
8286
  if (!visibility.isVisible(row.id)) continue;
8293
8287
  const isDeleted = visibility.isTrashed(row.id);
8294
8288
  const lastMessage = archive2.db.select({ ts: messages.ts }).from(messages).where(
8295
8289
  and(
8296
8290
  eq(messages.agent, CLAUDE_CODE_AGENT),
8297
- eq(messages.sessionId, row.sourceSessionId)
8291
+ eq(messages.sourceId, row.sourceId)
8298
8292
  )
8299
8293
  ).orderBy(desc(messages.ts)).limit(1).get();
8300
- const session = {
8301
- id: row.sourceSessionId,
8294
+ const chat = {
8295
+ id: row.sourceId,
8302
8296
  title: deriveTitle(archive2, metadata2, row),
8303
8297
  project: row.project ?? "",
8304
8298
  createdAt: row.firstSeenAt.getTime(),
8305
8299
  updatedAt: lastMessage ? lastMessage.ts.getTime() : row.firstSeenAt.getTime()
8306
8300
  };
8307
- if (isDeleted) session.isDeleted = true;
8308
- sessions2.push(session);
8301
+ if (isDeleted) chat.isDeleted = true;
8302
+ chats2.push(chat);
8309
8303
  }
8310
- return c.json({ sessions: sessions2 });
8304
+ return c.json({ chats: chats2 });
8311
8305
  });
8312
- app2.delete("/api/sessions/:id", (c) => {
8313
- const sessionId = c.req.param("id");
8314
- const row = findArchiveSessionBySourceId(sessionId);
8306
+ app2.delete("/api/chats/:id", (c) => {
8307
+ const id = c.req.param("id");
8308
+ const row = findArchiveChatBySourceId(id);
8315
8309
  if (!row) {
8316
- return c.json({ error: "Session not found" }, 404);
8310
+ return c.json({ error: "Chat not found" }, 404);
8317
8311
  }
8318
8312
  metadata2.softDelete(row.id);
8319
8313
  return c.body(null, 204);
8320
8314
  });
8321
- app2.post("/api/sessions/:id/restore", (c) => {
8322
- const sessionId = c.req.param("id");
8323
- const row = findArchiveSessionBySourceId(sessionId);
8315
+ app2.post("/api/chats/:id/restore", (c) => {
8316
+ const id = c.req.param("id");
8317
+ const row = findArchiveChatBySourceId(id);
8324
8318
  if (!row) {
8325
- return c.json({ error: "Session not found" }, 404);
8319
+ return c.json({ error: "Chat not found" }, 404);
8326
8320
  }
8327
8321
  metadata2.restore(row.id);
8328
8322
  return c.body(null, 204);
8329
8323
  });
8330
- app2.patch("/api/sessions/:id/title", async (c) => {
8331
- const sessionId = c.req.param("id");
8332
- const row = findArchiveSessionBySourceId(sessionId);
8324
+ app2.patch("/api/chats/:id/title", async (c) => {
8325
+ const id = c.req.param("id");
8326
+ const row = findArchiveChatBySourceId(id);
8333
8327
  if (!row) {
8334
- return c.json({ error: "Session not found" }, 404);
8328
+ return c.json({ error: "Chat not found" }, 404);
8335
8329
  }
8336
8330
  let body;
8337
8331
  try {
@@ -8350,22 +8344,22 @@ function createApp({ archive: archive2, metadata: metadata2, webDistDir: webDist
8350
8344
  metadata2.setCustomTitle(row.id, trimmed.length > 0 ? trimmed : null);
8351
8345
  return c.body(null, 204);
8352
8346
  });
8353
- app2.get("/api/sessions/:id", (c) => {
8354
- const sessionId = c.req.param("id");
8355
- const row = findArchiveSessionBySourceId(sessionId);
8347
+ app2.get("/api/chats/:id", (c) => {
8348
+ const id = c.req.param("id");
8349
+ const row = findArchiveChatBySourceId(id);
8356
8350
  if (!row) {
8357
- return c.json({ error: "Session not found" }, 404);
8351
+ return c.json({ error: "Chat not found" }, 404);
8358
8352
  }
8359
- const visibility = loadSessionVisibility(metadata2, {
8353
+ const visibility = loadChatVisibility(metadata2, {
8360
8354
  includeTrashed: c.req.query("includeTrashed") === "true"
8361
8355
  });
8362
8356
  if (!visibility.isVisible(row.id)) {
8363
- return c.json({ error: "Session not found" }, 404);
8357
+ return c.json({ error: "Chat not found" }, 404);
8364
8358
  }
8365
8359
  const rows = archive2.db.select().from(messages).where(
8366
8360
  and(
8367
8361
  eq(messages.agent, CLAUDE_CODE_AGENT),
8368
- eq(messages.sessionId, sessionId)
8362
+ eq(messages.sourceId, id)
8369
8363
  )
8370
8364
  ).orderBy(asc(messages.ts)).all();
8371
8365
  const messages2 = rows.map((m) => ({
@@ -8381,7 +8375,7 @@ function createApp({ archive: archive2, metadata: metadata2, webDistDir: webDist
8381
8375
  const firstUser = archive3.db.select({ text: messages.text }).from(messages).where(
8382
8376
  and(
8383
8377
  eq(messages.agent, CLAUDE_CODE_AGENT),
8384
- eq(messages.sessionId, row.sourceSessionId),
8378
+ eq(messages.sourceId, row.sourceId),
8385
8379
  eq(messages.role, "user")
8386
8380
  )
8387
8381
  ).orderBy(asc(messages.ts)).limit(1).get();
@@ -8601,18 +8595,18 @@ function migrate(db, config) {
8601
8595
  db.dialect.migrate(migrations, db.session, config);
8602
8596
  }
8603
8597
 
8604
- // src/archive/short-code.ts
8598
+ // src/archive/chat-id.ts
8605
8599
  import crypto4 from "crypto";
8606
8600
  var CROCKFORD_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
8607
- var SHORT_CODE_LENGTH = 6;
8601
+ var CHAT_ID_LENGTH = 6;
8608
8602
  var MAX_RETRIES = 5;
8609
- function generateShortCode({
8603
+ function generateChatId({
8610
8604
  isTaken,
8611
8605
  randomIndex = () => crypto4.randomInt(CROCKFORD_ALPHABET.length)
8612
8606
  }) {
8613
8607
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
8614
8608
  let code = "";
8615
- for (let i = 0; i < SHORT_CODE_LENGTH; i++) {
8609
+ for (let i = 0; i < CHAT_ID_LENGTH; i++) {
8616
8610
  code += CROCKFORD_ALPHABET[randomIndex()];
8617
8611
  }
8618
8612
  if (!isTaken(code)) {
@@ -8620,7 +8614,7 @@ function generateShortCode({
8620
8614
  }
8621
8615
  }
8622
8616
  throw new Error(
8623
- `Failed to generate unique short_code after ${MAX_RETRIES + 1} attempts`
8617
+ `Failed to generate unique chat_id after ${MAX_RETRIES + 1} attempts`
8624
8618
  );
8625
8619
  }
8626
8620
 
@@ -8668,9 +8662,9 @@ function createArchiveRepository({
8668
8662
  getAppliedMigrations() {
8669
8663
  return db.select().from(schemaVersion).all().map((r) => ({ version: r.version, appliedAt: r.appliedAt }));
8670
8664
  },
8671
- generateShortCode() {
8672
- return generateShortCode({
8673
- isTaken: (candidate) => db.select({ id: sessions.id }).from(sessions).where(eq(sessions.shortCode, candidate)).get() !== void 0
8665
+ generateChatId() {
8666
+ return generateChatId({
8667
+ isTaken: (candidate) => db.select({ id: chats.id }).from(chats).where(eq(chats.chatId, candidate)).get() !== void 0
8674
8668
  });
8675
8669
  },
8676
8670
  close() {
@@ -8686,7 +8680,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
8686
8680
  import Database2 from "better-sqlite3";
8687
8681
 
8688
8682
  // src/metadata/schema.ts
8689
- var sessionsMeta = sqliteTable("sessions_meta", {
8683
+ var chatsMeta = sqliteTable("chats_meta", {
8690
8684
  id: text("id").primaryKey(),
8691
8685
  isDeleted: integer("is_deleted", { mode: "boolean" }).notNull().default(false),
8692
8686
  customTitle: text("custom_title"),
@@ -8711,71 +8705,71 @@ var CLAUDE_CODE_AGENT2 = "claude-code";
8711
8705
  function createMetadataRepository({
8712
8706
  dataDir: dataDir2,
8713
8707
  lookupInternalId,
8714
- ensureSession: ensureSession2
8708
+ ensureChat: ensureChat2
8715
8709
  }) {
8716
8710
  fs3.mkdirSync(dataDir2, { recursive: true });
8717
8711
  const sqlite = new Database2(path2.join(dataDir2, "data.db"));
8718
8712
  const db = drizzle(sqlite);
8719
8713
  migrate(db, { migrationsFolder: resolveMigrationsFolder2() });
8720
8714
  if (lookupInternalId) {
8721
- rekeyLegacyRows(sqlite, db, lookupInternalId, ensureSession2);
8715
+ rekeyLegacyRows(sqlite, db, lookupInternalId, ensureChat2);
8722
8716
  }
8723
8717
  return {
8724
8718
  softDelete(internalId) {
8725
8719
  const now = /* @__PURE__ */ new Date();
8726
- db.insert(sessionsMeta).values({
8720
+ db.insert(chatsMeta).values({
8727
8721
  id: internalId,
8728
8722
  isDeleted: true,
8729
8723
  createdAt: now,
8730
8724
  updatedAt: now
8731
8725
  }).onConflictDoUpdate({
8732
- target: sessionsMeta.id,
8726
+ target: chatsMeta.id,
8733
8727
  set: { isDeleted: true, updatedAt: now }
8734
8728
  }).run();
8735
8729
  },
8736
8730
  restore(internalId) {
8737
8731
  const now = /* @__PURE__ */ new Date();
8738
- db.update(sessionsMeta).set({ isDeleted: false, updatedAt: now }).where(eq(sessionsMeta.id, internalId)).run();
8732
+ db.update(chatsMeta).set({ isDeleted: false, updatedAt: now }).where(eq(chatsMeta.id, internalId)).run();
8739
8733
  },
8740
8734
  isDeleted(internalId) {
8741
- const row = db.select({ isDeleted: sessionsMeta.isDeleted }).from(sessionsMeta).where(eq(sessionsMeta.id, internalId)).get();
8735
+ const row = db.select({ isDeleted: chatsMeta.isDeleted }).from(chatsMeta).where(eq(chatsMeta.id, internalId)).get();
8742
8736
  return row?.isDeleted ?? false;
8743
8737
  },
8744
8738
  listDeletedIds() {
8745
- const rows = db.select({ id: sessionsMeta.id }).from(sessionsMeta).where(eq(sessionsMeta.isDeleted, true)).all();
8739
+ const rows = db.select({ id: chatsMeta.id }).from(chatsMeta).where(eq(chatsMeta.isDeleted, true)).all();
8746
8740
  return rows.map((r) => r.id);
8747
8741
  },
8748
8742
  getCustomTitle(internalId) {
8749
- const row = db.select({ customTitle: sessionsMeta.customTitle }).from(sessionsMeta).where(eq(sessionsMeta.id, internalId)).get();
8743
+ const row = db.select({ customTitle: chatsMeta.customTitle }).from(chatsMeta).where(eq(chatsMeta.id, internalId)).get();
8750
8744
  return row?.customTitle ?? null;
8751
8745
  },
8752
8746
  setCustomTitle(internalId, title) {
8753
8747
  const now = /* @__PURE__ */ new Date();
8754
- db.insert(sessionsMeta).values({
8748
+ db.insert(chatsMeta).values({
8755
8749
  id: internalId,
8756
8750
  customTitle: title,
8757
8751
  createdAt: now,
8758
8752
  updatedAt: now
8759
8753
  }).onConflictDoUpdate({
8760
- target: sessionsMeta.id,
8754
+ target: chatsMeta.id,
8761
8755
  set: { customTitle: title, updatedAt: now }
8762
8756
  }).run();
8763
8757
  }
8764
8758
  };
8765
8759
  }
8766
8760
  var REKEY_USER_VERSION = 4;
8767
- function rekeyLegacyRows(sqlite, db, lookupInternalId, ensureSession2) {
8761
+ function rekeyLegacyRows(sqlite, db, lookupInternalId, ensureChat2) {
8768
8762
  const current = sqlite.pragma("user_version", { simple: true });
8769
8763
  if (current >= REKEY_USER_VERSION) return;
8770
- const rows = db.select({ id: sessionsMeta.id }).from(sessionsMeta).all();
8764
+ const rows = db.select({ id: chatsMeta.id }).from(chatsMeta).all();
8771
8765
  for (const row of rows) {
8772
8766
  let target = lookupInternalId(CLAUDE_CODE_AGENT2, row.id);
8773
8767
  if (target === null) {
8774
- if (!ensureSession2) continue;
8775
- target = ensureSession2(CLAUDE_CODE_AGENT2, row.id);
8768
+ if (!ensureChat2) continue;
8769
+ target = ensureChat2(CLAUDE_CODE_AGENT2, row.id);
8776
8770
  }
8777
8771
  if (target === row.id) continue;
8778
- db.update(sessionsMeta).set({ id: target, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessionsMeta.id, row.id)).run();
8772
+ db.update(chatsMeta).set({ id: target, updatedAt: /* @__PURE__ */ new Date() }).where(eq(chatsMeta.id, row.id)).run();
8779
8773
  }
8780
8774
  sqlite.pragma(`user_version = ${REKEY_USER_VERSION}`);
8781
8775
  }
@@ -8795,13 +8789,13 @@ async function runIngestion(opts) {
8795
8789
  for await (const ref of plugin.discover(opts.env)) {
8796
8790
  result.scanned += 1;
8797
8791
  const stat4 = safeStat(ref.sourcePath);
8798
- const prior = opts.archive.db.select().from(sessionScanState).where(
8792
+ const prior = opts.archive.db.select().from(chatScanState).where(
8799
8793
  and(
8800
- eq(sessionScanState.agent, plugin.id),
8801
- eq(sessionScanState.sessionId, ref.sessionId)
8794
+ eq(chatScanState.agent, plugin.id),
8795
+ eq(chatScanState.sourceId, ref.sourceId)
8802
8796
  )
8803
8797
  ).get();
8804
- ensureSession(opts.archive, plugin.id, ref.sessionId, now(), ref.project);
8798
+ ensureChat(opts.archive, plugin.id, ref.sourceId, now(), ref.project);
8805
8799
  if (stat4 && prior && prior.lastMtimeMs === stat4.mtimeMs && prior.lastSizeBytes === stat4.size) {
8806
8800
  result.skippedByMtime += 1;
8807
8801
  continue;
@@ -8812,7 +8806,7 @@ async function runIngestion(opts) {
8812
8806
  const existing = opts.archive.db.select({ id: rawMessages.id }).from(rawMessages).where(
8813
8807
  and(
8814
8808
  eq(rawMessages.agent, plugin.id),
8815
- eq(rawMessages.sessionId, ref.sessionId),
8809
+ eq(rawMessages.sourceId, ref.sourceId),
8816
8810
  eq(rawMessages.payloadHash, payloadHash)
8817
8811
  )
8818
8812
  ).get();
@@ -8822,7 +8816,7 @@ async function runIngestion(opts) {
8822
8816
  } else {
8823
8817
  const inserted = opts.archive.db.insert(rawMessages).values({
8824
8818
  agent: plugin.id,
8825
- sessionId: ref.sessionId,
8819
+ sourceId: ref.sourceId,
8826
8820
  sourcePath: raw2.sourcePath,
8827
8821
  sourceLocator: raw2.sourceLocator,
8828
8822
  rawPayload: payloadJson,
@@ -8837,7 +8831,7 @@ async function runIngestion(opts) {
8837
8831
  const upserted = upsertCanonical(
8838
8832
  opts.archive,
8839
8833
  plugin.id,
8840
- ref.sessionId,
8834
+ ref.sourceId,
8841
8835
  canonical,
8842
8836
  rawId
8843
8837
  );
@@ -8847,7 +8841,7 @@ async function runIngestion(opts) {
8847
8841
  recordScanState(
8848
8842
  opts.archive,
8849
8843
  plugin.id,
8850
- ref.sessionId,
8844
+ ref.sourceId,
8851
8845
  ref.sourcePath,
8852
8846
  stat4,
8853
8847
  now()
@@ -8864,24 +8858,21 @@ function safeStat(p) {
8864
8858
  return null;
8865
8859
  }
8866
8860
  }
8867
- function recordScanState(archive2, agent, sessionId, sourcePath, stat4, scannedAt) {
8868
- const existing = archive2.db.select({ id: sessionScanState.id }).from(sessionScanState).where(
8869
- and(
8870
- eq(sessionScanState.agent, agent),
8871
- eq(sessionScanState.sessionId, sessionId)
8872
- )
8861
+ function recordScanState(archive2, agent, sourceId, sourcePath, stat4, scannedAt) {
8862
+ const existing = archive2.db.select({ id: chatScanState.id }).from(chatScanState).where(
8863
+ and(eq(chatScanState.agent, agent), eq(chatScanState.sourceId, sourceId))
8873
8864
  ).get();
8874
8865
  if (existing) {
8875
- archive2.db.update(sessionScanState).set({
8866
+ archive2.db.update(chatScanState).set({
8876
8867
  sourcePath,
8877
8868
  lastMtimeMs: stat4.mtimeMs,
8878
8869
  lastSizeBytes: stat4.size,
8879
8870
  lastScannedAt: scannedAt
8880
- }).where(eq(sessionScanState.id, existing.id)).run();
8871
+ }).where(eq(chatScanState.id, existing.id)).run();
8881
8872
  } else {
8882
- archive2.db.insert(sessionScanState).values({
8873
+ archive2.db.insert(chatScanState).values({
8883
8874
  agent,
8884
- sessionId,
8875
+ sourceId,
8885
8876
  sourcePath,
8886
8877
  lastMtimeMs: stat4.mtimeMs,
8887
8878
  lastSizeBytes: stat4.size,
@@ -8889,36 +8880,31 @@ function recordScanState(archive2, agent, sessionId, sourcePath, stat4, scannedA
8889
8880
  }).run();
8890
8881
  }
8891
8882
  }
8892
- function ensureSession(archive2, agent, sourceSessionId, firstSeenAt, project) {
8893
- const existing = archive2.db.select().from(sessions).where(
8894
- and(
8895
- eq(sessions.agent, agent),
8896
- eq(sessions.sourceSessionId, sourceSessionId)
8897
- )
8898
- ).get();
8883
+ function ensureChat(archive2, agent, sourceId, firstSeenAt, project) {
8884
+ const existing = archive2.db.select().from(chats).where(and(eq(chats.agent, agent), eq(chats.sourceId, sourceId))).get();
8899
8885
  if (existing) {
8900
8886
  if (project && existing.project !== project) {
8901
- archive2.db.update(sessions).set({ project }).where(eq(sessions.id, existing.id)).run();
8887
+ archive2.db.update(chats).set({ project }).where(eq(chats.id, existing.id)).run();
8902
8888
  }
8903
8889
  return existing.id;
8904
8890
  }
8905
8891
  const id = crypto6.randomUUID();
8906
- const shortCode = archive2.generateShortCode();
8907
- archive2.db.insert(sessions).values({
8892
+ const chatId = archive2.generateChatId();
8893
+ archive2.db.insert(chats).values({
8908
8894
  id,
8909
- shortCode,
8895
+ chatId,
8910
8896
  agent,
8911
- sourceSessionId,
8897
+ sourceId,
8912
8898
  firstSeenAt,
8913
8899
  project: project ?? null
8914
8900
  }).run();
8915
8901
  return id;
8916
8902
  }
8917
- function upsertCanonical(archive2, agent, sessionId, msg, rawId) {
8903
+ function upsertCanonical(archive2, agent, sourceId, msg, rawId) {
8918
8904
  const existing = archive2.db.select().from(messages).where(
8919
8905
  and(
8920
8906
  eq(messages.agent, agent),
8921
- eq(messages.sessionId, sessionId),
8907
+ eq(messages.sourceId, sourceId),
8922
8908
  eq(messages.messageId, msg.messageId)
8923
8909
  )
8924
8910
  ).get();
@@ -8926,7 +8912,7 @@ function upsertCanonical(archive2, agent, sessionId, msg, rawId) {
8926
8912
  if (!existing) {
8927
8913
  archive2.db.insert(messages).values({
8928
8914
  agent,
8929
- sessionId,
8915
+ sourceId,
8930
8916
  messageId: msg.messageId,
8931
8917
  role: msg.role,
8932
8918
  ts,
@@ -10741,7 +10727,7 @@ function startWatcher(opts) {
10741
10727
  try {
10742
10728
  opts.archive.db.insert(ingestionEvents).values({
10743
10729
  agent: binding.plugin.id,
10744
- sessionId: binding.ref.sessionId,
10730
+ sourceId: binding.ref.sourceId,
10745
10731
  sourcePath: removedPath,
10746
10732
  eventType: "unlink_observed",
10747
10733
  detail: { path: removedPath },
@@ -10794,10 +10780,10 @@ var ClaudeCodePlugin = class {
10794
10780
  for (const file of files) {
10795
10781
  if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
10796
10782
  const sourcePath = path3.join(projectPath, file.name);
10797
- const sessionId = file.name.replace(/\.jsonl$/, "");
10783
+ const sourceId = file.name.replace(/\.jsonl$/, "");
10798
10784
  const cwd = await readCwdFromJsonl(sourcePath);
10799
10785
  yield {
10800
- sessionId,
10786
+ sourceId,
10801
10787
  sourcePath,
10802
10788
  watchPaths: [sourcePath],
10803
10789
  project: cwd ? path3.basename(cwd) : void 0
@@ -10814,7 +10800,7 @@ var ClaudeCodePlugin = class {
10814
10800
  lineNo += 1;
10815
10801
  if (!line) continue;
10816
10802
  yield {
10817
- sessionId: ref.sessionId,
10803
+ sourceId: ref.sourceId,
10818
10804
  sourcePath: ref.sourcePath,
10819
10805
  sourceLocator: `L${lineNo}`,
10820
10806
  payload: JSON.parse(line)
@@ -10917,6 +10903,7 @@ function parseCliArgs(argv, env = {}) {
10917
10903
  if (argv.includes("--help") || argv.includes("-h")) {
10918
10904
  return { kind: "help" };
10919
10905
  }
10906
+ const open2 = !env.CHAT_LOGBOOK_NO_OPEN;
10920
10907
  const portIdx = argv.findIndex((a) => a === "--port" || a === "-p");
10921
10908
  if (portIdx !== -1) {
10922
10909
  const raw2 = argv[portIdx + 1];
@@ -10939,12 +10926,12 @@ function parseCliArgs(argv, env = {}) {
10939
10926
  message: `Invalid port "${raw2}": must be between 1 and 65535`
10940
10927
  };
10941
10928
  }
10942
- return { kind: "run", port: port2 };
10929
+ return { kind: "run", port: port2, open: open2 };
10943
10930
  }
10944
10931
  if (env.PORT) {
10945
- return { kind: "run", port: Number(env.PORT) };
10932
+ return { kind: "run", port: Number(env.PORT), open: open2 };
10946
10933
  }
10947
- return { kind: "run", port: DEFAULT_PORT };
10934
+ return { kind: "run", port: DEFAULT_PORT, open: open2 };
10948
10935
  }
10949
10936
 
10950
10937
  // src/cli/help.ts
@@ -11011,7 +10998,7 @@ function openBrowser(url) {
11011
10998
  var server = serve({ fetch: app.fetch, port }, (info) => {
11012
10999
  const url = `http://localhost:${info.port}`;
11013
11000
  console.log(`chat-logbook is running at \x1B[36m${url}\x1B[0m`);
11014
- openBrowser(url);
11001
+ if (action.open) openBrowser(url);
11015
11002
  });
11016
11003
  server.on("error", (err) => {
11017
11004
  if (err.code === "EADDRINUSE") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat-logbook",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "A local-first conversation manager for Claude Code",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",