codesesh 0.8.0 → 0.9.1

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.
@@ -31,18 +31,20 @@ import {
31
31
  import { join as join7, basename as basename5 } from "path";
32
32
  import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync5 } from "fs";
33
33
  import { join as join8, normalize } from "path";
34
+ import { existsSync as existsSync9, readFileSync as readFileSync7, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
35
+ import { basename as basename6, join as join9 } from "path";
34
36
  import { availableParallelism } from "os";
35
37
  import { Worker } from "worker_threads";
36
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
38
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
37
39
  import { spawnSync } from "child_process";
38
40
  import * as os from "os";
39
41
  import * as path from "path";
40
42
  import { resolve, sep } from "path";
41
- import { existsSync as existsSync10, rmSync, unlinkSync } from "fs";
42
- import { join as join9 } from "path";
43
+ import { existsSync as existsSync11, rmSync, unlinkSync } from "fs";
44
+ import { join as join10 } from "path";
43
45
  import { homedir as homedir4 } from "os";
44
46
  import { homedir as homedir5, platform as platform2 } from "os";
45
- import { join as join10 } from "path";
47
+ import { join as join11 } from "path";
46
48
  var registrations = [];
47
49
  function registerAgent(reg) {
48
50
  registrations.push(reg);
@@ -112,7 +114,8 @@ function resolveProviderRoots() {
112
114
  codexRoot: envPath("CODEX_HOME") ?? join(home, ".codex"),
113
115
  claudeRoot: envPath("CLAUDE_CONFIG_DIR") ?? join(home, ".claude"),
114
116
  kimiRoot: envPath("KIMI_SHARE_DIR") ?? join(home, ".kimi"),
115
- opencodeRoot: join(getDataHome(), "opencode")
117
+ opencodeRoot: join(getDataHome(), "opencode"),
118
+ piRoot: envPath("PI_HOME") ?? join(home, ".pi")
116
119
  };
117
120
  }
118
121
  function getCursorDataPath() {
@@ -701,7 +704,7 @@ function withEstimatedSessionCost(stats, model) {
701
704
  function estimateTokenCost(model, tokens) {
702
705
  return estimateCostForTokens(model, tokens)?.cost ?? null;
703
706
  }
704
- var HEAD_INDEX_VERSION = "claudecode-head-v1";
707
+ var HEAD_INDEX_VERSION = "claudecode-head-v2";
705
708
  function parseTimestampMs(data) {
706
709
  const raw = String(data["timestamp"] ?? "").trim();
707
710
  if (!raw) return 0;
@@ -711,6 +714,25 @@ function parseTimestampMs(data) {
711
714
  return 0;
712
715
  }
713
716
  }
717
+ function numericUsage(value) {
718
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
719
+ }
720
+ function extractClaudeUsage(data, msg) {
721
+ const usage = msg["usage"];
722
+ if (!usage || typeof usage !== "object") return null;
723
+ const u = usage;
724
+ const requestId = typeof data["requestId"] === "string" ? data["requestId"].trim() : "";
725
+ const uuid = typeof data["uuid"] === "string" ? data["uuid"].trim() : "";
726
+ const key = requestId || uuid;
727
+ if (!key) return null;
728
+ return {
729
+ key,
730
+ input: numericUsage(u["input_tokens"]),
731
+ output: numericUsage(u["output_tokens"]),
732
+ cacheRead: numericUsage(u["cache_read_input_tokens"]),
733
+ cacheCreate: numericUsage(u["cache_creation_input_tokens"])
734
+ };
735
+ }
714
736
  var ClaudeCodeAgent = class extends BaseAgent {
715
737
  name = "claudecode";
716
738
  displayName = "Claude Code";
@@ -820,6 +842,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
820
842
  const pendingToolCalls = /* @__PURE__ */ new Map();
821
843
  const ignoredToolCallIds = /* @__PURE__ */ new Set();
822
844
  const assistantUuidToToolCalls = /* @__PURE__ */ new Map();
845
+ const countedUsageKeys = /* @__PURE__ */ new Set();
823
846
  const assistantState = {
824
847
  currentIndex: null,
825
848
  latestTextIndex: null
@@ -837,6 +860,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
837
860
  pendingToolCalls,
838
861
  ignoredToolCallIds,
839
862
  assistantUuidToToolCalls,
863
+ countedUsageKeys,
840
864
  assistantState
841
865
  );
842
866
  } catch {
@@ -1054,6 +1078,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1054
1078
  let totalCacheCreateTokens = 0;
1055
1079
  let totalCost = 0;
1056
1080
  const modelUsageMap = {};
1081
+ const countedUsageKeys = /* @__PURE__ */ new Set();
1057
1082
  for (const line of lines) {
1058
1083
  try {
1059
1084
  const data = JSON.parse(line);
@@ -1074,12 +1099,13 @@ var ClaudeCodeAgent = class extends BaseAgent {
1074
1099
  if (typeof m === "string" && m.trim()) model = m.trim();
1075
1100
  }
1076
1101
  if (role === "assistant") {
1077
- const usage = msg["usage"];
1078
- if (usage && typeof usage === "object") {
1079
- const inputTokens = usage["input_tokens"] ?? 0;
1080
- const cacheRead = usage["cache_read_input_tokens"] ?? 0;
1081
- const cacheCreate = usage["cache_creation_input_tokens"] ?? 0;
1082
- const outputTokens = usage["output_tokens"] ?? 0;
1102
+ const usage = extractClaudeUsage(data, msg);
1103
+ if (usage && !countedUsageKeys.has(usage.key)) {
1104
+ countedUsageKeys.add(usage.key);
1105
+ const inputTokens = usage.input;
1106
+ const cacheRead = usage.cacheRead;
1107
+ const cacheCreate = usage.cacheCreate;
1108
+ const outputTokens = usage.output;
1083
1109
  totalInputTokens += inputTokens + cacheRead + cacheCreate;
1084
1110
  totalOutputTokens += outputTokens;
1085
1111
  totalCacheReadTokens += cacheRead;
@@ -1153,7 +1179,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1153
1179
  return null;
1154
1180
  }
1155
1181
  // --- Record conversion ---
1156
- convertRecord(data, messages, pendingToolCalls, ignoredToolCallIds, assistantUuidToToolCalls, assistantState) {
1182
+ convertRecord(data, messages, pendingToolCalls, ignoredToolCallIds, assistantUuidToToolCalls, countedUsageKeys, assistantState) {
1157
1183
  if (data["isMeta"] === true) return;
1158
1184
  const msgType = String(data["type"] ?? "");
1159
1185
  if (isInternalEventType(msgType)) return;
@@ -1164,6 +1190,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1164
1190
  pendingToolCalls,
1165
1191
  ignoredToolCallIds,
1166
1192
  assistantUuidToToolCalls,
1193
+ countedUsageKeys,
1167
1194
  assistantState
1168
1195
  );
1169
1196
  } else if (msgType === "user") {
@@ -1179,7 +1206,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1179
1206
  this.convertToolResultRecord(data, messages, assistantState);
1180
1207
  }
1181
1208
  }
1182
- convertAssistantRecord(data, messages, pendingToolCalls, ignoredToolCallIds, assistantUuidToToolCalls, assistantState) {
1209
+ convertAssistantRecord(data, messages, pendingToolCalls, ignoredToolCallIds, assistantUuidToToolCalls, countedUsageKeys, assistantState) {
1183
1210
  const msg = data["message"] ?? {};
1184
1211
  const timestampMs = parseTimestampMs(data);
1185
1212
  const rawContent = msg["content"] ?? [];
@@ -1197,7 +1224,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1197
1224
  if (text) {
1198
1225
  currentAssistantIndex = this.appendAssistantReasoning(
1199
1226
  messages,
1200
- { messageId: uuid, msg, timestampMs, text },
1227
+ { messageId: uuid, data, msg, timestampMs, text, countedUsageKeys },
1201
1228
  currentAssistantIndex
1202
1229
  );
1203
1230
  }
@@ -1208,7 +1235,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1208
1235
  if (text) {
1209
1236
  currentAssistantIndex = this.appendAssistantText(
1210
1237
  messages,
1211
- { messageId: uuid, msg, timestampMs, text },
1238
+ { messageId: uuid, data, msg, timestampMs, text, countedUsageKeys },
1212
1239
  currentAssistantIndex
1213
1240
  );
1214
1241
  latestAssistantTextIndex = currentAssistantIndex;
@@ -1225,10 +1252,12 @@ var ClaudeCodeAgent = class extends BaseAgent {
1225
1252
  const toolPart = this.buildToolPart(part, timestampMs);
1226
1253
  const [msgIndex, partIndex] = this.attachToolCallToLatestAssistant(messages, {
1227
1254
  messageId: uuid,
1255
+ data,
1228
1256
  msg,
1229
1257
  timestampMs,
1230
1258
  toolPart,
1231
- latestTextIndex: latestAssistantTextIndex
1259
+ latestTextIndex: latestAssistantTextIndex,
1260
+ countedUsageKeys
1232
1261
  });
1233
1262
  currentAssistantIndex = msgIndex;
1234
1263
  if (toolCallId) {
@@ -1350,21 +1379,19 @@ var ClaudeCodeAgent = class extends BaseAgent {
1350
1379
  time_created: timestampMs
1351
1380
  };
1352
1381
  }
1353
- applyAssistantMetadata(message, msg) {
1382
+ applyAssistantMetadata(message, data, msg, countedUsageKeys) {
1354
1383
  const model = msg["model"];
1355
1384
  if (model && typeof model === "string" && !message.model) {
1356
1385
  message.model = model;
1357
1386
  }
1358
- const usage = msg["usage"];
1359
- if (usage && typeof usage === "object" && !message.tokens) {
1360
- const u = usage;
1361
- const cacheRead = u["cache_read_input_tokens"] ?? 0;
1362
- const cacheCreate = u["cache_creation_input_tokens"] ?? 0;
1387
+ const usage = extractClaudeUsage(data, msg);
1388
+ if (usage && !message.tokens && !countedUsageKeys.has(usage.key)) {
1389
+ countedUsageKeys.add(usage.key);
1363
1390
  message.tokens = {
1364
- input: (u["input_tokens"] ?? 0) + cacheCreate + cacheRead,
1365
- output: u["output_tokens"] ?? 0,
1366
- cache_read: cacheRead,
1367
- cache_create: cacheCreate
1391
+ input: usage.input + usage.cacheCreate + usage.cacheRead,
1392
+ output: usage.output,
1393
+ cache_read: usage.cacheRead,
1394
+ cache_create: usage.cacheCreate
1368
1395
  };
1369
1396
  const cost = estimateTokenCost(message.model, message.tokens);
1370
1397
  if (cost !== null) {
@@ -1382,7 +1409,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1382
1409
  const hasTool = message2.parts.some((p) => p.type === "tool");
1383
1410
  if (!hasText && !hasTool) {
1384
1411
  this.appendPartIfNew(message2, part);
1385
- this.applyAssistantMetadata(message2, opts.msg);
1412
+ this.applyAssistantMetadata(message2, opts.data, opts.msg, opts.countedUsageKeys);
1386
1413
  return currentIndex;
1387
1414
  }
1388
1415
  }
@@ -1393,7 +1420,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1393
1420
  parts: [part],
1394
1421
  agent: "claude"
1395
1422
  });
1396
- this.applyAssistantMetadata(message, opts.msg);
1423
+ this.applyAssistantMetadata(message, opts.data, opts.msg, opts.countedUsageKeys);
1397
1424
  messages.push(message);
1398
1425
  return messages.length - 1;
1399
1426
  }
@@ -1404,7 +1431,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1404
1431
  const hasTool = message2.parts.some((p) => p.type === "tool");
1405
1432
  if (!hasTool) {
1406
1433
  this.appendPartIfNew(message2, part);
1407
- this.applyAssistantMetadata(message2, opts.msg);
1434
+ this.applyAssistantMetadata(message2, opts.data, opts.msg, opts.countedUsageKeys);
1408
1435
  return currentIndex;
1409
1436
  }
1410
1437
  }
@@ -1415,7 +1442,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1415
1442
  parts: [part],
1416
1443
  agent: "claude"
1417
1444
  });
1418
- this.applyAssistantMetadata(message, opts.msg);
1445
+ this.applyAssistantMetadata(message, opts.data, opts.msg, opts.countedUsageKeys);
1419
1446
  messages.push(message);
1420
1447
  return messages.length - 1;
1421
1448
  }
@@ -1423,7 +1450,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1423
1450
  if (opts.latestTextIndex !== null) {
1424
1451
  const message2 = messages[opts.latestTextIndex];
1425
1452
  message2.parts.push(opts.toolPart);
1426
- this.applyAssistantMetadata(message2, opts.msg);
1453
+ this.applyAssistantMetadata(message2, opts.data, opts.msg, opts.countedUsageKeys);
1427
1454
  return [opts.latestTextIndex, message2.parts.length - 1];
1428
1455
  }
1429
1456
  const message = this.buildMessage({
@@ -1434,7 +1461,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
1434
1461
  agent: "claude",
1435
1462
  mode: "tool"
1436
1463
  });
1437
- this.applyAssistantMetadata(message, opts.msg);
1464
+ this.applyAssistantMetadata(message, opts.data, opts.msg, opts.countedUsageKeys);
1438
1465
  messages.push(message);
1439
1466
  return [messages.length - 1, 0];
1440
1467
  }
@@ -4664,6 +4691,589 @@ var CursorAgent = class extends BaseAgent {
4664
4691
  }
4665
4692
  }
4666
4693
  };
4694
+ var HEAD_INDEX_VERSION3 = "pi-head-v1";
4695
+ var PARSER_VERSION2 = "pi-parser-v1";
4696
+ function parseTimestampMs3(value) {
4697
+ if (typeof value === "number") return Number.isFinite(value) ? value : 0;
4698
+ const text = String(value ?? "").trim();
4699
+ if (!text) return 0;
4700
+ const ts = Date.parse(text);
4701
+ return Number.isNaN(ts) ? 0 : ts;
4702
+ }
4703
+ function extractSessionIdFromFilename(filePath) {
4704
+ const stem = basename6(filePath, ".jsonl");
4705
+ const underscore = stem.indexOf("_");
4706
+ return underscore >= 0 ? stem.slice(underscore + 1) || stem : stem;
4707
+ }
4708
+ function isObject(value) {
4709
+ return typeof value === "object" && value !== null;
4710
+ }
4711
+ function contentToText(content) {
4712
+ if (typeof content === "string") return content;
4713
+ if (!Array.isArray(content)) return "";
4714
+ return content.map((item) => {
4715
+ if (!isObject(item)) return "";
4716
+ if (item["type"] === "text") return String(item["text"] ?? "");
4717
+ if (item["type"] === "image") return "[image]";
4718
+ return "";
4719
+ }).filter(Boolean).join("\n");
4720
+ }
4721
+ function normalizeTextParts(content, timestampMs) {
4722
+ const text = cleanInternalText(contentToText(content));
4723
+ return text ? [{ type: "text", text, time_created: timestampMs }] : [];
4724
+ }
4725
+ function buildMessage(params) {
4726
+ return {
4727
+ id: params.id,
4728
+ role: params.role,
4729
+ agent: params.agent,
4730
+ time_created: params.timestampMs,
4731
+ provider: params.provider,
4732
+ model: params.model,
4733
+ tokens: params.tokens,
4734
+ cost: params.cost,
4735
+ cost_source: params.costSource,
4736
+ parts: params.parts
4737
+ };
4738
+ }
4739
+ function getEntryTimestamp(entry) {
4740
+ return parseTimestampMs3(entry["timestamp"]);
4741
+ }
4742
+ function chooseLeafEntry(entries) {
4743
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
4744
+ if (typeof entries[index]?.["id"] === "string") return entries[index];
4745
+ }
4746
+ return null;
4747
+ }
4748
+ function buildCurrentPathEntries(entries) {
4749
+ const byId = /* @__PURE__ */ new Map();
4750
+ for (const entry of entries) {
4751
+ const id = entry["id"];
4752
+ if (typeof id === "string" && id) byId.set(id, entry);
4753
+ }
4754
+ const leaf = chooseLeafEntry(entries);
4755
+ if (!leaf) return [];
4756
+ const path2 = [];
4757
+ const seen = /* @__PURE__ */ new Set();
4758
+ let current = leaf;
4759
+ while (current) {
4760
+ const id = String(current["id"] ?? "");
4761
+ if (!id || seen.has(id)) break;
4762
+ seen.add(id);
4763
+ path2.push(current);
4764
+ const parentId = current["parentId"];
4765
+ current = typeof parentId === "string" ? byId.get(parentId) : void 0;
4766
+ }
4767
+ return path2.reverse();
4768
+ }
4769
+ var PiAgent = class extends BaseAgent {
4770
+ name = "pi";
4771
+ displayName = "Pi";
4772
+ basePath = null;
4773
+ sessionMetaMap = /* @__PURE__ */ new Map();
4774
+ findBasePath() {
4775
+ const roots = resolveProviderRoots();
4776
+ return firstExisting(join9(roots.piRoot, "agent", "sessions"), "data/pi");
4777
+ }
4778
+ isAvailable() {
4779
+ this.basePath = this.findBasePath();
4780
+ if (!this.basePath) return false;
4781
+ return this.listSessionFiles().length > 0;
4782
+ }
4783
+ scan(options) {
4784
+ if (!this.basePath) return [];
4785
+ const scanMarker = perf.start("pi:scan");
4786
+ const files = this.listSessionFiles(options);
4787
+ options?.onProgress?.({ total: files.length, processed: 0, sessions: 0 });
4788
+ const heads = [];
4789
+ let processed = 0;
4790
+ for (const file of files) {
4791
+ try {
4792
+ const head = getParsedSession(this.parseSessionHeadResult(file));
4793
+ if (head) {
4794
+ heads.push(head);
4795
+ this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file));
4796
+ }
4797
+ } catch {
4798
+ } finally {
4799
+ processed += 1;
4800
+ options?.onProgress?.({ total: files.length, processed, sessions: heads.length });
4801
+ }
4802
+ }
4803
+ perf.end(scanMarker);
4804
+ return heads;
4805
+ }
4806
+ listSessionSources() {
4807
+ if (!this.basePath) return [];
4808
+ return this.listSessionFiles().map((file) => ({
4809
+ sessionId: extractSessionIdFromFilename(file),
4810
+ sourcePath: file,
4811
+ fingerprint: this.sourceFingerprint(file)
4812
+ }));
4813
+ }
4814
+ scanSessionSource(sourcePath) {
4815
+ const head = getParsedSession(this.parseSessionHeadResult(sourcePath));
4816
+ if (head) {
4817
+ this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, sourcePath));
4818
+ }
4819
+ return head;
4820
+ }
4821
+ getSessionMetaMap() {
4822
+ return this.sessionMetaMap;
4823
+ }
4824
+ setSessionMetaMap(meta) {
4825
+ this.sessionMetaMap = meta;
4826
+ }
4827
+ checkForChanges(_sinceTimestamp, cachedSessions) {
4828
+ if (!this.basePath) return { hasChanges: false, timestamp: Date.now() };
4829
+ const currentFiles = this.listSessionFiles();
4830
+ const currentIds = new Set(currentFiles.map((file) => extractSessionIdFromFilename(file)));
4831
+ const cachedIds = new Set(cachedSessions.map((session) => session.id));
4832
+ const changedIds = /* @__PURE__ */ new Set();
4833
+ for (const session of cachedSessions) {
4834
+ if (!currentIds.has(session.id)) {
4835
+ changedIds.add(session.id);
4836
+ continue;
4837
+ }
4838
+ const meta = this.sessionMetaMap.get(session.id);
4839
+ if (!meta) {
4840
+ changedIds.add(session.id);
4841
+ continue;
4842
+ }
4843
+ try {
4844
+ if (this.hasMetaChanged(meta)) changedIds.add(session.id);
4845
+ } catch {
4846
+ changedIds.add(session.id);
4847
+ }
4848
+ }
4849
+ const hasAddedSessions = currentFiles.some(
4850
+ (file) => !cachedIds.has(extractSessionIdFromFilename(file))
4851
+ );
4852
+ return {
4853
+ hasChanges: changedIds.size > 0 || hasAddedSessions,
4854
+ changedIds: [...changedIds],
4855
+ timestamp: Date.now()
4856
+ };
4857
+ }
4858
+ incrementalScan(cachedSessions, changedIds) {
4859
+ if (!this.basePath) return cachedSessions;
4860
+ const sessionMap = new Map(cachedSessions.map((session) => [session.id, session]));
4861
+ const changedSet = new Set(changedIds);
4862
+ const currentFiles = this.listSessionFiles();
4863
+ const currentIds = new Set(currentFiles.map((file) => extractSessionIdFromFilename(file)));
4864
+ for (const session of cachedSessions) {
4865
+ if (!currentIds.has(session.id)) {
4866
+ sessionMap.delete(session.id);
4867
+ this.sessionMetaMap.delete(session.id);
4868
+ }
4869
+ }
4870
+ for (const file of currentFiles) {
4871
+ try {
4872
+ const sessionId = extractSessionIdFromFilename(file);
4873
+ if (!changedSet.has(sessionId) && sessionMap.has(sessionId)) continue;
4874
+ const head = getParsedSession(this.parseSessionHeadResult(file));
4875
+ if (head) {
4876
+ sessionMap.set(head.id, head);
4877
+ this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file));
4878
+ }
4879
+ } catch {
4880
+ }
4881
+ }
4882
+ return [...sessionMap.values()];
4883
+ }
4884
+ getSessionData(sessionId) {
4885
+ const meta = this.sessionMetaMap.get(sessionId);
4886
+ if (!meta) throw new Error(`Session not found: ${sessionId}`);
4887
+ if (!existsSync9(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
4888
+ const parsed2 = this.parsePiFile(meta.sourcePath);
4889
+ const state = this.convertEntries(parsed2.pathEntries);
4890
+ const cleanedMessages = cleanParsedMessages(state.messages);
4891
+ return {
4892
+ id: meta.id,
4893
+ title: meta.title,
4894
+ slug: `pi/${meta.id}`,
4895
+ directory: meta.directory,
4896
+ time_created: meta.createdAt,
4897
+ time_updated: meta.updatedAt,
4898
+ stats: {
4899
+ message_count: cleanedMessages.length,
4900
+ total_input_tokens: state.totalInputTokens,
4901
+ total_output_tokens: state.totalOutputTokens,
4902
+ total_cache_read_tokens: state.totalCacheReadTokens || void 0,
4903
+ total_cache_create_tokens: state.totalCacheCreateTokens || void 0,
4904
+ total_cost: state.totalCost,
4905
+ cost_source: state.totalCost > 0 ? "recorded" : void 0
4906
+ },
4907
+ messages: cleanedMessages
4908
+ };
4909
+ }
4910
+ listSessionFiles(options) {
4911
+ if (!this.basePath) return [];
4912
+ return this.walkJsonlFiles(this.basePath, options);
4913
+ }
4914
+ walkJsonlFiles(dir, options) {
4915
+ const files = [];
4916
+ try {
4917
+ for (const entry of readdirSync5(dir, { withFileTypes: true })) {
4918
+ const fullPath = join9(dir, entry.name);
4919
+ if (entry.isDirectory()) {
4920
+ files.push(...this.walkJsonlFiles(fullPath, options));
4921
+ continue;
4922
+ }
4923
+ if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
4924
+ if (!matchesScanWindow(statSync6(fullPath).mtimeMs, options)) continue;
4925
+ files.push(fullPath);
4926
+ }
4927
+ } catch {
4928
+ }
4929
+ return files;
4930
+ }
4931
+ buildSessionMeta(head, file) {
4932
+ return {
4933
+ id: head.id,
4934
+ title: head.title,
4935
+ sourcePath: file,
4936
+ sourceFingerprint: this.sourceFingerprint(file),
4937
+ sourceMtimeMs: statSync6(file).mtimeMs,
4938
+ headIndexVersion: HEAD_INDEX_VERSION3,
4939
+ parserVersion: PARSER_VERSION2,
4940
+ directory: head.directory,
4941
+ messageCount: head.stats.message_count,
4942
+ createdAt: head.time_created,
4943
+ updatedAt: head.time_updated ?? head.time_created
4944
+ };
4945
+ }
4946
+ hasMetaChanged(meta) {
4947
+ if (meta.headIndexVersion !== HEAD_INDEX_VERSION3) return true;
4948
+ if (meta.parserVersion !== PARSER_VERSION2) return true;
4949
+ return statSync6(meta.sourcePath).mtimeMs !== meta.sourceMtimeMs;
4950
+ }
4951
+ sourceFingerprint(file) {
4952
+ const stat = statSync6(file);
4953
+ return JSON.stringify([HEAD_INDEX_VERSION3, PARSER_VERSION2, stat.mtimeMs, stat.size]);
4954
+ }
4955
+ parseSessionHeadResult(filePath) {
4956
+ const parsed2 = this.parsePiFile(filePath);
4957
+ const state = this.convertEntries(parsed2.pathEntries);
4958
+ const messageCount = state.messages.length;
4959
+ if (messageCount === 0) return filteredSession("no visible messages");
4960
+ const modelUsage = Object.keys(state.modelUsage).length > 0 ? state.modelUsage : void 0;
4961
+ return parsedSession({
4962
+ id: parsed2.sessionId,
4963
+ slug: `pi/${parsed2.sessionId}`,
4964
+ title: parsed2.title,
4965
+ directory: parsed2.directory,
4966
+ time_created: parsed2.createdAt,
4967
+ time_updated: parsed2.updatedAt,
4968
+ stats: {
4969
+ message_count: messageCount,
4970
+ total_input_tokens: state.totalInputTokens,
4971
+ total_output_tokens: state.totalOutputTokens,
4972
+ total_cache_read_tokens: state.totalCacheReadTokens || void 0,
4973
+ total_cache_create_tokens: state.totalCacheCreateTokens || void 0,
4974
+ total_cost: state.totalCost,
4975
+ cost_source: state.totalCost > 0 ? "recorded" : void 0
4976
+ },
4977
+ model_usage: modelUsage
4978
+ });
4979
+ }
4980
+ parsePiFile(filePath) {
4981
+ const records = Array.from(parseJsonlLines(readFileSync7(filePath, "utf-8")));
4982
+ if (records.length === 0) throw new Error("empty file");
4983
+ const header = records.find((record) => record["type"] === "session");
4984
+ if (!header) throw new Error("missing session header");
4985
+ const entries = records.filter((record) => record["type"] !== "session");
4986
+ const pathEntries = buildCurrentPathEntries(entries);
4987
+ if (pathEntries.length === 0) throw new Error("empty session tree");
4988
+ const sessionId = String(header["id"] ?? extractSessionIdFromFilename(filePath)).trim();
4989
+ if (!sessionId) throw new Error("missing session id");
4990
+ const stat = statSync6(filePath);
4991
+ const directory = String(header["cwd"] ?? "").trim() || basename6(filePath, ".jsonl");
4992
+ const createdAt = parseTimestampMs3(header["timestamp"]) || stat.mtimeMs;
4993
+ const updatedAt = pathEntries.reduce(
4994
+ (max, entry) => Math.max(max, getEntryTimestamp(entry)),
4995
+ createdAt
4996
+ );
4997
+ const explicitTitle = this.extractSessionName(pathEntries);
4998
+ const messageTitle = this.extractTitle(pathEntries);
4999
+ const directoryTitle = basenameTitle(directory);
5000
+ return {
5001
+ sessionId,
5002
+ directory,
5003
+ createdAt,
5004
+ updatedAt,
5005
+ title: resolveSessionTitle(explicitTitle, messageTitle, directoryTitle),
5006
+ pathEntries
5007
+ };
5008
+ }
5009
+ extractSessionName(entries) {
5010
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
5011
+ const entry = entries[index];
5012
+ if (entry["type"] !== "session_info") continue;
5013
+ const name = normalizeTitleText(String(entry["name"] ?? ""));
5014
+ if (name) return name;
5015
+ }
5016
+ return null;
5017
+ }
5018
+ extractTitle(entries) {
5019
+ for (const entry of entries) {
5020
+ if (entry["type"] !== "message") continue;
5021
+ const message = entry["message"];
5022
+ if (!isObject(message) || message["role"] !== "user") continue;
5023
+ const title = normalizeTitleText(contentToText(message["content"]));
5024
+ if (title) return title;
5025
+ }
5026
+ return null;
5027
+ }
5028
+ convertEntries(entries) {
5029
+ const messages = [];
5030
+ const pendingToolCalls = /* @__PURE__ */ new Map();
5031
+ const modelUsage = {};
5032
+ let totalInputTokens = 0;
5033
+ let totalOutputTokens = 0;
5034
+ let totalCacheReadTokens = 0;
5035
+ let totalCacheCreateTokens = 0;
5036
+ let totalCost = 0;
5037
+ for (const entry of entries) {
5038
+ const timestampMs = getEntryTimestamp(entry);
5039
+ const type = String(entry["type"] ?? "");
5040
+ if (type === "message") {
5041
+ const message = entry["message"];
5042
+ if (!isObject(message)) continue;
5043
+ const result = this.convertAgentMessage(
5044
+ entry,
5045
+ message,
5046
+ timestampMs,
5047
+ pendingToolCalls,
5048
+ messages.length,
5049
+ messages
5050
+ );
5051
+ if (!result) continue;
5052
+ if (result.message) messages.push(result.message);
5053
+ totalInputTokens += result.inputTokens;
5054
+ totalOutputTokens += result.outputTokens;
5055
+ totalCacheReadTokens += result.cacheReadTokens;
5056
+ totalCacheCreateTokens += result.cacheCreateTokens;
5057
+ totalCost += result.cost;
5058
+ if (result.model && result.totalTokens > 0) {
5059
+ modelUsage[result.model] = (modelUsage[result.model] ?? 0) + result.totalTokens;
5060
+ }
5061
+ continue;
5062
+ }
5063
+ const summary = this.convertSummaryEntry(entry, timestampMs);
5064
+ if (summary) messages.push(summary);
5065
+ }
5066
+ return {
5067
+ messages,
5068
+ totalInputTokens,
5069
+ totalOutputTokens,
5070
+ totalCacheReadTokens,
5071
+ totalCacheCreateTokens,
5072
+ totalCost,
5073
+ modelUsage
5074
+ };
5075
+ }
5076
+ convertAgentMessage(entry, message, timestampMs, pendingToolCalls, nextMessageIndex, messages) {
5077
+ const id = String(entry["id"] ?? "");
5078
+ const role = String(message["role"] ?? "");
5079
+ if (role === "user") {
5080
+ const parts = normalizeTextParts(message["content"], timestampMs);
5081
+ if (parts.length === 0) return null;
5082
+ return this.emptyUsageResult(buildMessage({ id, role: "user", timestampMs, parts }));
5083
+ }
5084
+ if (role === "assistant") {
5085
+ const parts = this.normalizeAssistantParts(
5086
+ message["content"],
5087
+ timestampMs,
5088
+ pendingToolCalls,
5089
+ nextMessageIndex
5090
+ );
5091
+ if (parts.length === 0) return null;
5092
+ const usage = this.normalizeUsage(message["usage"]);
5093
+ const model = typeof message["model"] === "string" ? message["model"].trim() : null;
5094
+ const cost = usage.cost ?? estimateTokenCost(model, usage.tokens) ?? 0;
5095
+ return {
5096
+ message: buildMessage({
5097
+ id,
5098
+ role: "assistant",
5099
+ agent: "pi",
5100
+ timestampMs,
5101
+ parts,
5102
+ provider: typeof message["provider"] === "string" ? message["provider"] : null,
5103
+ model,
5104
+ tokens: usage.tokens,
5105
+ cost: cost || void 0,
5106
+ costSource: cost > 0 ? "recorded" : void 0
5107
+ }),
5108
+ inputTokens: usage.inputTokens,
5109
+ outputTokens: usage.outputTokens,
5110
+ cacheReadTokens: usage.cacheReadTokens,
5111
+ cacheCreateTokens: usage.cacheCreateTokens,
5112
+ totalTokens: usage.totalTokens,
5113
+ cost,
5114
+ model
5115
+ };
5116
+ }
5117
+ if (role === "toolResult") {
5118
+ this.attachToolResult(message, timestampMs, pendingToolCalls, messages);
5119
+ return this.emptyUsageResult();
5120
+ }
5121
+ if (role === "bashExecution") {
5122
+ return this.emptyUsageResult(this.convertBashExecution(id, message, timestampMs));
5123
+ }
5124
+ if (role === "custom" && message["display"] === true) {
5125
+ const parts = normalizeTextParts(message["content"], timestampMs);
5126
+ if (parts.length === 0) return null;
5127
+ return this.emptyUsageResult(buildMessage({ id, role: "user", timestampMs, parts }));
5128
+ }
5129
+ if (role === "branchSummary" || role === "compactionSummary") {
5130
+ const summary = String(message["summary"] ?? "").trim();
5131
+ if (!summary) return null;
5132
+ return this.emptyUsageResult(
5133
+ buildMessage({
5134
+ id,
5135
+ role: "assistant",
5136
+ agent: "pi",
5137
+ timestampMs,
5138
+ parts: [{ type: "text", text: summary, time_created: timestampMs }]
5139
+ })
5140
+ );
5141
+ }
5142
+ return null;
5143
+ }
5144
+ normalizeAssistantParts(content, timestampMs, pendingToolCalls, messageIndex) {
5145
+ if (!Array.isArray(content)) return [];
5146
+ const parts = [];
5147
+ for (const item of content) {
5148
+ if (!isObject(item)) continue;
5149
+ const type = item["type"];
5150
+ if (type === "text") {
5151
+ const text = cleanInternalText(String(item["text"] ?? ""));
5152
+ if (text) parts.push({ type: "text", text, time_created: timestampMs });
5153
+ continue;
5154
+ }
5155
+ if (type === "thinking") {
5156
+ const text = cleanInternalText(String(item["thinking"] ?? ""));
5157
+ if (text) parts.push({ type: "reasoning", text, time_created: timestampMs });
5158
+ continue;
5159
+ }
5160
+ if (type === "toolCall") {
5161
+ const callId = String(item["id"] ?? "").trim();
5162
+ const toolName = String(item["name"] ?? "").trim() || "tool";
5163
+ const toolPart = {
5164
+ type: "tool",
5165
+ tool: toolName,
5166
+ title: `Tool: ${toolName}`,
5167
+ callID: callId || void 0,
5168
+ time_created: timestampMs,
5169
+ state: {
5170
+ status: "running",
5171
+ input: item["arguments"] ?? {}
5172
+ }
5173
+ };
5174
+ parts.push(toolPart);
5175
+ if (callId) pendingToolCalls.set(callId, [messageIndex, parts.length - 1]);
5176
+ }
5177
+ }
5178
+ return parts;
5179
+ }
5180
+ attachToolResult(message, timestampMs, pendingToolCalls, messages) {
5181
+ const callId = String(message["toolCallId"] ?? "").trim();
5182
+ const output = normalizeTextParts(message["content"], timestampMs);
5183
+ const location = callId ? pendingToolCalls.get(callId) : void 0;
5184
+ if (!location) return;
5185
+ const [messageIndex, partIndex] = location;
5186
+ const target = messages[messageIndex]?.parts[partIndex];
5187
+ if (!target?.state) return;
5188
+ target.state.output = output;
5189
+ target.state.status = message["isError"] === true ? "error" : "completed";
5190
+ target.state.metadata = message["details"];
5191
+ pendingToolCalls.delete(callId);
5192
+ }
5193
+ convertBashExecution(id, message, timestampMs) {
5194
+ const command = String(message["command"] ?? "");
5195
+ const output = String(message["output"] ?? "");
5196
+ const isError = Number(message["exitCode"] ?? 0) !== 0 || message["cancelled"] === true;
5197
+ return buildMessage({
5198
+ id,
5199
+ role: "tool",
5200
+ timestampMs,
5201
+ parts: [
5202
+ {
5203
+ type: "tool",
5204
+ tool: "bash",
5205
+ title: "Tool: bash",
5206
+ time_created: timestampMs,
5207
+ state: {
5208
+ status: isError ? "error" : "completed",
5209
+ input: { command },
5210
+ output: output ? [{ type: "text", text: output, time_created: timestampMs }] : [],
5211
+ metadata: {
5212
+ exitCode: message["exitCode"],
5213
+ cancelled: message["cancelled"],
5214
+ truncated: message["truncated"],
5215
+ fullOutputPath: message["fullOutputPath"]
5216
+ }
5217
+ }
5218
+ }
5219
+ ]
5220
+ });
5221
+ }
5222
+ convertSummaryEntry(entry, timestampMs) {
5223
+ const type = entry["type"];
5224
+ if (type !== "compaction" && type !== "branch_summary" && type !== "custom_message") {
5225
+ return null;
5226
+ }
5227
+ if (type === "custom_message" && entry["display"] !== true) return null;
5228
+ const rawText = type === "custom_message" ? contentToText(entry["content"]) : String(entry["summary"] ?? "");
5229
+ const text = cleanInternalText(rawText);
5230
+ if (!text) return null;
5231
+ return buildMessage({
5232
+ id: String(entry["id"] ?? ""),
5233
+ role: type === "custom_message" ? "user" : "assistant",
5234
+ agent: type === "custom_message" ? void 0 : "pi",
5235
+ timestampMs,
5236
+ parts: [{ type: "text", text, time_created: timestampMs }]
5237
+ });
5238
+ }
5239
+ normalizeUsage(raw) {
5240
+ const usage = isObject(raw) ? raw : {};
5241
+ const inputTokens = Number(usage["input"] ?? 0);
5242
+ const outputTokens = Number(usage["output"] ?? 0);
5243
+ const cacheReadTokens = Number(usage["cacheRead"] ?? 0);
5244
+ const cacheCreateTokens = Number(usage["cacheWrite"] ?? 0);
5245
+ const totalTokens = Number(
5246
+ usage["totalTokens"] ?? inputTokens + outputTokens + cacheReadTokens + cacheCreateTokens
5247
+ );
5248
+ const cost = isObject(usage["cost"]) ? Number(usage["cost"]["total"] ?? 0) : null;
5249
+ return {
5250
+ inputTokens: inputTokens + cacheReadTokens + cacheCreateTokens,
5251
+ outputTokens,
5252
+ cacheReadTokens,
5253
+ cacheCreateTokens,
5254
+ totalTokens,
5255
+ cost: cost && Number.isFinite(cost) ? cost : null,
5256
+ tokens: {
5257
+ input: inputTokens + cacheReadTokens + cacheCreateTokens,
5258
+ output: outputTokens,
5259
+ cache_read: cacheReadTokens || void 0,
5260
+ cache_create: cacheCreateTokens || void 0
5261
+ }
5262
+ };
5263
+ }
5264
+ emptyUsageResult(message) {
5265
+ return {
5266
+ message,
5267
+ inputTokens: 0,
5268
+ outputTokens: 0,
5269
+ cacheReadTokens: 0,
5270
+ cacheCreateTokens: 0,
5271
+ totalTokens: 0,
5272
+ cost: 0,
5273
+ model: null
5274
+ };
5275
+ }
5276
+ };
4667
5277
  registerAgent({
4668
5278
  name: "claudecode",
4669
5279
  displayName: "Claude Code",
@@ -4688,6 +5298,12 @@ registerAgent({
4688
5298
  icon: "/icon/agent/codex.svg",
4689
5299
  create: () => new CodexAgent()
4690
5300
  });
5301
+ registerAgent({
5302
+ name: "pi",
5303
+ displayName: "Pi",
5304
+ icon: "/icon/agent/pi.svg",
5305
+ create: () => new PiAgent()
5306
+ });
4691
5307
  registerAgent({
4692
5308
  name: "cursor",
4693
5309
  displayName: "Cursor",
@@ -4702,11 +5318,11 @@ function fallbackDisplayName(input) {
4702
5318
  }
4703
5319
  var realFs = {
4704
5320
  exists(path2) {
4705
- return existsSync9(path2);
5321
+ return existsSync10(path2);
4706
5322
  },
4707
5323
  readText(path2) {
4708
5324
  try {
4709
- return readFileSync7(path2, "utf8");
5325
+ return readFileSync8(path2, "utf8");
4710
5326
  } catch {
4711
5327
  return null;
4712
5328
  }
@@ -5196,16 +5812,16 @@ var LEGACY_CACHE_FILENAME = "scan-cache.json";
5196
5812
  var SEARCH_INDEX_BULK_SYNC_THRESHOLD = 100;
5197
5813
  var ftsIntegrityCheckedPath = null;
5198
5814
  function getCacheDir2() {
5199
- return join9(homedir4(), ".cache", "codesesh");
5815
+ return join10(homedir4(), ".cache", "codesesh");
5200
5816
  }
5201
5817
  function getCachePath2() {
5202
- return join9(getCacheDir2(), CACHE_FILENAME);
5818
+ return join10(getCacheDir2(), CACHE_FILENAME);
5203
5819
  }
5204
5820
  function getLegacyCachePath() {
5205
- return join9(getCacheDir2(), LEGACY_CACHE_FILENAME);
5821
+ return join10(getCacheDir2(), LEGACY_CACHE_FILENAME);
5206
5822
  }
5207
5823
  function hasCacheStorage() {
5208
- return existsSync10(getCachePath2());
5824
+ return existsSync11(getCachePath2());
5209
5825
  }
5210
5826
  function withCacheDb(fn) {
5211
5827
  const cachePath = getCachePath2();
@@ -6644,7 +7260,7 @@ function buildSessionContentFromMessages(title, messages) {
6644
7260
  }
6645
7261
  function deleteLegacyCacheFile() {
6646
7262
  const legacyPath = getLegacyCachePath();
6647
- if (!existsSync10(legacyPath)) {
7263
+ if (!existsSync11(legacyPath)) {
6648
7264
  return;
6649
7265
  }
6650
7266
  try {
@@ -6971,7 +7587,7 @@ function clearCache() {
6971
7587
  const walPath = `${cachePath}-wal`;
6972
7588
  const shmPath = `${cachePath}-shm`;
6973
7589
  for (const filePath of [walPath, shmPath]) {
6974
- if (!existsSync10(filePath)) {
7590
+ if (!existsSync11(filePath)) {
6975
7591
  continue;
6976
7592
  }
6977
7593
  try {
@@ -8179,16 +8795,16 @@ function getStateDir() {
8179
8795
  }
8180
8796
  const p = platform2();
8181
8797
  if (p === "darwin") {
8182
- return join10(homedir5(), "Library", "Application Support", "codesesh");
8798
+ return join11(homedir5(), "Library", "Application Support", "codesesh");
8183
8799
  }
8184
8800
  if (p === "win32") {
8185
8801
  const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
8186
- return join10(appData ?? join10(homedir5(), "AppData", "Roaming"), "codesesh");
8802
+ return join11(appData ?? join11(homedir5(), "AppData", "Roaming"), "codesesh");
8187
8803
  }
8188
- return join10(process.env.XDG_DATA_HOME ?? join10(homedir5(), ".local", "share"), "codesesh");
8804
+ return join11(process.env.XDG_DATA_HOME ?? join11(homedir5(), ".local", "share"), "codesesh");
8189
8805
  }
8190
8806
  function getStateDbPath() {
8191
- return join10(getStateDir(), BOOKMARK_DB_FILENAME);
8807
+ return join11(getStateDir(), BOOKMARK_DB_FILENAME);
8192
8808
  }
8193
8809
  function useMemoryStateStore() {
8194
8810
  return process.env.CODESESH_STATE_STORE === MEMORY_STATE_STORE;
@@ -8556,4 +9172,4 @@ export {
8556
9172
  importBookmarks,
8557
9173
  deleteBookmark
8558
9174
  };
8559
- //# sourceMappingURL=chunk-XANARSZG.js.map
9175
+ //# sourceMappingURL=chunk-5UOLRSFH.js.map