@cookielab.io/klovi 3.4.0 → 3.5.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/dist/cli.js CHANGED
@@ -38,6 +38,69 @@ function epochSecondsToIso(epochSeconds) {
38
38
  }
39
39
  var MS_PER_SECOND = 1000;
40
40
 
41
+ // ../../packages/plugin-core/src/jsonl-stream.ts
42
+ import { FileSystem } from "@effect/platform";
43
+ import { Effect, Ref, Stream } from "effect";
44
+ function parseAndVisit(line, lineIndex, visitor, onMalformed) {
45
+ if (!line.trim()) {
46
+ return;
47
+ }
48
+ const lineNumber = lineIndex + 1;
49
+ try {
50
+ const parsed = JSON.parse(line);
51
+ return visitor({ parsed, line, lineIndex, lineNumber });
52
+ } catch (error) {
53
+ onMalformed?.(line, lineNumber, error);
54
+ return;
55
+ }
56
+ }
57
+ function processLine(line, state) {
58
+ return Effect.gen(function* () {
59
+ if (yield* Ref.get(state.bailedRef)) {
60
+ return;
61
+ }
62
+ const lineIndex = yield* Ref.getAndUpdate(state.indexRef, (n) => n + 1);
63
+ const result = parseAndVisit(line, lineIndex, state.visitor, state.onMalformed);
64
+ if (result === false) {
65
+ yield* Ref.set(state.bailedRef, true);
66
+ }
67
+ });
68
+ }
69
+ function streamJsonlHead(filePath, visitor, options = {}) {
70
+ return Effect.gen(function* () {
71
+ const fs = yield* FileSystem.FileSystem;
72
+ const indexRef = yield* Ref.make(0);
73
+ const bailedRef = yield* Ref.make(false);
74
+ const chunkSize = options.chunkSize ?? DEFAULT_HEAD_CHUNK_SIZE;
75
+ const baseStream = fs.stream(filePath, { chunkSize });
76
+ const linesStream = baseStream.pipe(Stream.decodeText("utf-8"), Stream.splitLines);
77
+ const cappedStream = options.maxLines === undefined ? linesStream : linesStream.pipe(Stream.take(options.maxLines));
78
+ const state = {
79
+ visitor,
80
+ indexRef,
81
+ bailedRef,
82
+ onMalformed: options.onMalformed
83
+ };
84
+ yield* cappedStream.pipe(Stream.takeUntilEffect(() => Ref.get(bailedRef)), Stream.runForEach((line) => processLine(line, state)));
85
+ });
86
+ }
87
+ function streamJsonl(filePath, visitor, options = {}) {
88
+ return Effect.gen(function* () {
89
+ const fs = yield* FileSystem.FileSystem;
90
+ const indexRef = yield* Ref.make(0);
91
+ const chunkSize = options.chunkSize ?? DEFAULT_FULL_CHUNK_SIZE;
92
+ yield* fs.stream(filePath, { chunkSize }).pipe(Stream.decodeText("utf-8"), Stream.splitLines, Stream.runForEach((line) => Effect.gen(function* () {
93
+ const lineIndex = yield* Ref.getAndUpdate(indexRef, (n) => n + 1);
94
+ parseAndVisit(line, lineIndex, visitor, options.onMalformed);
95
+ })));
96
+ });
97
+ }
98
+ var KILOBYTE_BYTES = 1024, DEFAULT_HEAD_CHUNK_KILOBYTES = 8, DEFAULT_HEAD_CHUNK_SIZE, DEFAULT_FULL_CHUNK_KILOBYTES = 64, DEFAULT_FULL_CHUNK_SIZE;
99
+ var init_jsonl_stream = __esm(() => {
100
+ DEFAULT_HEAD_CHUNK_SIZE = DEFAULT_HEAD_CHUNK_KILOBYTES * KILOBYTE_BYTES;
101
+ DEFAULT_FULL_CHUNK_SIZE = DEFAULT_FULL_CHUNK_KILOBYTES * KILOBYTE_BYTES;
102
+ });
103
+
41
104
  // ../../packages/plugin-core/src/plugin-config.ts
42
105
  import { Context } from "effect";
43
106
  var PluginConfig;
@@ -56,8 +119,8 @@ var init_plugin_errors = __esm(() => {
56
119
 
57
120
  // ../../packages/plugin-core/src/resolve-worktree.ts
58
121
  import { join } from "node:path";
59
- import { FileSystem } from "@effect/platform";
60
- import { Effect } from "effect";
122
+ import { FileSystem as FileSystem2 } from "@effect/platform";
123
+ import { Effect as Effect2 } from "effect";
61
124
  function stripT3CodeSuffix(path) {
62
125
  if (!T3CODE_SUFFIX_REGEX.test(path)) {
63
126
  return null;
@@ -68,14 +131,14 @@ function stripT3CodeSuffix(path) {
68
131
  return { path: parentPath, projectName };
69
132
  }
70
133
  function resolveGitWorktree(worktreePath) {
71
- return Effect.gen(function* () {
72
- const fs = yield* FileSystem.FileSystem;
134
+ return Effect2.gen(function* () {
135
+ const fs = yield* FileSystem2.FileSystem;
73
136
  const dotGitPath = join(worktreePath, ".git");
74
- const info = yield* fs.stat(dotGitPath).pipe(Effect.catchAll(() => Effect.succeed(null)));
137
+ const info = yield* fs.stat(dotGitPath).pipe(Effect2.catchAll(() => Effect2.succeed(null)));
75
138
  if (!info || info.type !== "File") {
76
139
  return worktreePath;
77
140
  }
78
- const content = yield* fs.readFileString(dotGitPath).pipe(Effect.catchAll(() => Effect.succeed("")));
141
+ const content = yield* fs.readFileString(dotGitPath).pipe(Effect2.catchAll(() => Effect2.succeed("")));
79
142
  if (!content) {
80
143
  return worktreePath;
81
144
  }
@@ -132,7 +195,7 @@ function buildNameMap(projects, t3codeProjects) {
132
195
  return nameToResolvedPaths;
133
196
  }
134
197
  function resolveEntry(entry, nameMap) {
135
- return Effect.gen(function* () {
198
+ return Effect2.gen(function* () {
136
199
  const candidates = nameMap.get(entry.projectName);
137
200
  if (!candidates || candidates.length === 0) {
138
201
  return;
@@ -151,7 +214,7 @@ function resolveEntry(entry, nameMap) {
151
214
  });
152
215
  }
153
216
  function resolveT3CodePaths(projects) {
154
- return Effect.gen(function* () {
217
+ return Effect2.gen(function* () {
155
218
  const t3codeEntries = collectT3CodeEntries(projects);
156
219
  if (t3codeEntries.length === 0) {
157
220
  return;
@@ -186,7 +249,7 @@ function parseSessionId(sessionId) {
186
249
  var SESSION_ID_SEPARATOR = "::";
187
250
 
188
251
  // ../../packages/plugin-core/src/plugin-registry.ts
189
- import { Effect as Effect2, Layer } from "effect";
252
+ import { Effect as Effect3, Layer } from "effect";
190
253
  function encodeResolvedPath(resolvedPath) {
191
254
  if (resolvedPath.startsWith("/")) {
192
255
  return resolvedPath.replace(/\//gu, "-");
@@ -225,22 +288,21 @@ class PluginRegistry {
225
288
  return [...this.plugins.values()].map((entry) => entry.plugin);
226
289
  }
227
290
  discoverPluginStates(includeSessions) {
228
- return Effect2.gen(this, function* () {
229
- const states = [];
230
- for (const entry of this.plugins.values()) {
231
- const discoveredIndex = includeSessions && entry.plugin.discoverIndex ? yield* entry.plugin.discoverIndex.pipe(Effect2.provide(entry.configLayer), Effect2.catchAll(() => Effect2.succeed(undefined))) : undefined;
232
- const projects = discoveredIndex?.projects ?? (yield* entry.plugin.discoverProjects.pipe(Effect2.provide(entry.configLayer), Effect2.catchAll(() => Effect2.succeed([]))));
233
- states.push({
291
+ return Effect3.gen(this, function* () {
292
+ const entries = [...this.plugins.values()];
293
+ return yield* Effect3.forEach(entries, (entry) => Effect3.gen(function* () {
294
+ const discoveredIndex = includeSessions && entry.plugin.discoverIndex ? yield* entry.plugin.discoverIndex.pipe(Effect3.provide(entry.configLayer), Effect3.catchAll(() => Effect3.succeed(undefined))) : undefined;
295
+ const projects = discoveredIndex?.projects ?? (yield* entry.plugin.discoverProjects.pipe(Effect3.provide(entry.configLayer), Effect3.catchAll(() => Effect3.succeed([]))));
296
+ return {
234
297
  entry,
235
298
  projects,
236
299
  ...discoveredIndex ? { sessionsByNativeId: discoveredIndex.sessionsByNativeId } : {}
237
- });
238
- }
239
- return states;
300
+ };
301
+ }), { concurrency: "unbounded" });
240
302
  });
241
303
  }
242
304
  mergeProjects(allProjects) {
243
- return Effect2.gen(function* () {
305
+ return Effect3.gen(function* () {
244
306
  yield* resolveT3CodePaths(allProjects);
245
307
  const projectsByPath = new Map;
246
308
  for (const project of allProjects) {
@@ -280,18 +342,18 @@ class PluginRegistry {
280
342
  loadSourceSessions(state, source) {
281
343
  const discoveredSessions = state.sessionsByNativeId?.get(source.nativeId);
282
344
  if (discoveredSessions) {
283
- return Effect2.succeed(this.encodeSessions(source.pluginId, discoveredSessions));
345
+ return Effect3.succeed(this.encodeSessions(source.pluginId, discoveredSessions));
284
346
  }
285
- return state.entry.plugin.listSessions(source.nativeId).pipe(Effect2.provide(state.entry.configLayer), Effect2.catchAll(() => Effect2.succeed([])), Effect2.map((sessions) => this.encodeSessions(source.pluginId, sessions)));
347
+ return state.entry.plugin.listSessions(source.nativeId).pipe(Effect3.provide(state.entry.configLayer), Effect3.catchAll(() => Effect3.succeed([])), Effect3.map((sessions) => this.encodeSessions(source.pluginId, sessions)));
286
348
  }
287
349
  discoverAllProjects() {
288
- return Effect2.gen(this, function* () {
350
+ return Effect3.gen(this, function* () {
289
351
  const states = yield* this.discoverPluginStates(false);
290
352
  return yield* this.mergeProjects(states.flatMap((state) => state.projects));
291
353
  });
292
354
  }
293
355
  discoverAllProjectsWithSessions() {
294
- return Effect2.gen(this, function* () {
356
+ return Effect3.gen(this, function* () {
295
357
  const states = yield* this.discoverPluginStates(true);
296
358
  const mergedProjects = yield* this.mergeProjects(states.flatMap((state) => state.projects));
297
359
  const statesByPluginId = new Map(states.map((state) => [state.entry.plugin.id, state]));
@@ -315,14 +377,14 @@ class PluginRegistry {
315
377
  });
316
378
  }
317
379
  listAllSessions(project) {
318
- return Effect2.gen(this, function* () {
380
+ return Effect3.gen(this, function* () {
319
381
  const allSessions = [];
320
382
  for (const source of project.sources) {
321
383
  const entry = this.plugins.get(source.pluginId);
322
384
  if (!entry) {
323
385
  continue;
324
386
  }
325
- allSessions.push(...yield* entry.plugin.listSessions(source.nativeId).pipe(Effect2.provide(entry.configLayer), Effect2.catchAll(() => Effect2.succeed([])), Effect2.map((sessions) => this.encodeSessions(source.pluginId, sessions))));
387
+ allSessions.push(...yield* entry.plugin.listSessions(source.nativeId).pipe(Effect3.provide(entry.configLayer), Effect3.catchAll(() => Effect3.succeed([])), Effect3.map((sessions) => this.encodeSessions(source.pluginId, sessions))));
326
388
  }
327
389
  sortByIsoDesc(allSessions, (session) => session.timestamp);
328
390
  return allSessions;
@@ -353,6 +415,7 @@ var init_sqlite_service = __esm(() => {
353
415
 
354
416
  // ../../packages/plugin-core/src/index.ts
355
417
  var init_src = __esm(() => {
418
+ init_jsonl_stream();
356
419
  init_plugin_config();
357
420
  init_plugin_errors();
358
421
  init_plugin_registry();
@@ -362,18 +425,18 @@ var init_src = __esm(() => {
362
425
 
363
426
  // ../../packages/plugin-opencode/src/db.ts
364
427
  import { join as join2 } from "node:path";
365
- import { FileSystem as FileSystem2 } from "@effect/platform";
366
- import { Effect as Effect3 } from "effect";
428
+ import { FileSystem as FileSystem3 } from "@effect/platform";
429
+ import { Effect as Effect4 } from "effect";
367
430
  function getOpenCodeDbPath() {
368
- return Effect3.gen(function* () {
431
+ return Effect4.gen(function* () {
369
432
  const config = yield* PluginConfig;
370
433
  return join2(config.dataDir, "opencode.db");
371
434
  });
372
435
  }
373
436
  function openOpenCodeDb() {
374
- return Effect3.gen(function* () {
437
+ return Effect4.gen(function* () {
375
438
  const dbPath = yield* getOpenCodeDbPath();
376
- const fs = yield* FileSystem2.FileSystem;
439
+ const fs = yield* FileSystem3.FileSystem;
377
440
  const exists = yield* fs.exists(dbPath);
378
441
  if (!exists) {
379
442
  return null;
@@ -396,7 +459,7 @@ function tryParseJson(value) {
396
459
  }
397
460
 
398
461
  // ../../packages/plugin-opencode/src/discovery.ts
399
- import { Effect as Effect4 } from "effect";
462
+ import { Effect as Effect5 } from "effect";
400
463
  function getColumns(db, tableName) {
401
464
  const rows = db.query(`PRAGMA table_info(${tableName})`).all();
402
465
  return new Set(rows.map((r) => r.name));
@@ -417,7 +480,7 @@ function inspectSchema(db) {
417
480
  };
418
481
  }
419
482
  function discoverOpenCodeProjects() {
420
- return Effect4.gen(function* () {
483
+ return Effect5.gen(function* () {
421
484
  const db = yield* openOpenCodeDb();
422
485
  if (!db) {
423
486
  return [];
@@ -508,7 +571,7 @@ function sessionRowToSummary(db, row) {
508
571
  };
509
572
  }
510
573
  function listOpenCodeSessions(nativeId) {
511
- return Effect4.gen(function* () {
574
+ return Effect5.gen(function* () {
512
575
  const db = yield* openOpenCodeDb();
513
576
  if (!db) {
514
577
  return [];
@@ -575,7 +638,7 @@ var init_discovery = __esm(() => {
575
638
  });
576
639
 
577
640
  // ../../packages/plugin-opencode/src/parser.ts
578
- import { Effect as Effect5 } from "effect";
641
+ import { Effect as Effect6 } from "effect";
579
642
  function partToContentBlock(partData, nextToolUseId) {
580
643
  switch (partData.type) {
581
644
  case "text": {
@@ -784,7 +847,7 @@ function loadSessionFromDb(db, nativeId, sessionId) {
784
847
  return { sessionId, project, turns, pluginId: "opencode" };
785
848
  }
786
849
  function loadOpenCodeSession(nativeId, sessionId) {
787
- return Effect5.gen(function* () {
850
+ return Effect6.gen(function* () {
788
851
  const db = yield* openOpenCodeDb();
789
852
  if (!db) {
790
853
  return emptySession(nativeId, sessionId);
@@ -819,12 +882,12 @@ var init_config = __esm(() => {
819
882
  });
820
883
 
821
884
  // ../../packages/plugin-opencode/src/runtime/bun-sqlite.ts
822
- import { Effect as Effect6, Layer as Layer3 } from "effect";
885
+ import { Effect as Effect7, Layer as Layer3 } from "effect";
823
886
  var bunSqliteClient, BunSqliteLayer;
824
887
  var init_bun_sqlite = __esm(() => {
825
888
  init_src();
826
889
  bunSqliteClient = {
827
- open: (dbPath, options) => Effect6.try({
890
+ open: (dbPath, options) => Effect7.try({
828
891
  try: () => {
829
892
  const sqlite = __require("bun:sqlite");
830
893
  return new sqlite.Database(dbPath, {
@@ -832,18 +895,18 @@ var init_bun_sqlite = __esm(() => {
832
895
  });
833
896
  },
834
897
  catch: () => null
835
- }).pipe(Effect6.catchAll(() => Effect6.succeed(null)))
898
+ }).pipe(Effect7.catchAll(() => Effect7.succeed(null)))
836
899
  };
837
900
  BunSqliteLayer = Layer3.succeed(SqliteClientTag, bunSqliteClient);
838
901
  });
839
902
 
840
903
  // ../../packages/plugin-opencode/src/runtime/node-sqlite.ts
841
- import { Effect as Effect7, Layer as Layer4 } from "effect";
904
+ import { Effect as Effect8, Layer as Layer4 } from "effect";
842
905
  var nodeSqliteClient, NodeSqliteLayer;
843
906
  var init_node_sqlite = __esm(() => {
844
907
  init_src();
845
908
  nodeSqliteClient = {
846
- open: (dbPath, options) => Effect7.tryPromise({
909
+ open: (dbPath, options) => Effect8.tryPromise({
847
910
  try: async () => {
848
911
  const sqlite = await import("node:sqlite");
849
912
  const db = new sqlite.DatabaseSync(dbPath, {
@@ -864,15 +927,15 @@ var init_node_sqlite = __esm(() => {
864
927
  };
865
928
  },
866
929
  catch: () => null
867
- }).pipe(Effect7.catchAll(() => Effect7.succeed(null)))
930
+ }).pipe(Effect8.catchAll(() => Effect8.succeed(null)))
868
931
  };
869
932
  NodeSqliteLayer = Layer4.succeed(SqliteClientTag, nodeSqliteClient);
870
933
  });
871
934
 
872
935
  // ../../packages/plugin-opencode/src/index.ts
873
936
  import { join as join4 } from "node:path";
874
- import { FileSystem as FileSystem3 } from "@effect/platform";
875
- import { Effect as Effect8 } from "effect";
937
+ import { FileSystem as FileSystem4 } from "@effect/platform";
938
+ import { Effect as Effect9 } from "effect";
876
939
  var openCodePlugin;
877
940
  var init_src2 = __esm(() => {
878
941
  init_src();
@@ -885,24 +948,24 @@ var init_src2 = __esm(() => {
885
948
  id: "opencode",
886
949
  displayName: "OpenCode",
887
950
  getDefaultDataDir: () => null,
888
- isDataAvailable: Effect8.gen(function* () {
951
+ isDataAvailable: Effect9.gen(function* () {
889
952
  const config = yield* PluginConfig;
890
- const fs = yield* FileSystem3.FileSystem;
891
- return yield* fs.exists(join4(config.dataDir, "opencode.db")).pipe(Effect8.catchAll(() => Effect8.succeed(false)));
953
+ const fs = yield* FileSystem4.FileSystem;
954
+ return yield* fs.exists(join4(config.dataDir, "opencode.db")).pipe(Effect9.catchAll(() => Effect9.succeed(false)));
892
955
  }),
893
- discoverProjects: discoverOpenCodeProjects().pipe(Effect8.catchAll((err) => Effect8.fail(new PluginError({
956
+ discoverProjects: discoverOpenCodeProjects().pipe(Effect9.catchAll((err) => Effect9.fail(new PluginError({
894
957
  pluginId: "opencode",
895
958
  operation: "discoverProjects",
896
959
  message: String(err),
897
960
  cause: err
898
961
  })))),
899
- listSessions: (nativeId) => listOpenCodeSessions(nativeId).pipe(Effect8.catchAll((err) => Effect8.fail(new PluginError({
962
+ listSessions: (nativeId) => listOpenCodeSessions(nativeId).pipe(Effect9.catchAll((err) => Effect9.fail(new PluginError({
900
963
  pluginId: "opencode",
901
964
  operation: "listSessions",
902
965
  message: String(err),
903
966
  cause: err
904
967
  })))),
905
- loadSession: (nativeId, sessionId) => loadOpenCodeSession(nativeId, sessionId).pipe(Effect8.catchAll((err) => Effect8.fail(new PluginError({
968
+ loadSession: (nativeId, sessionId) => loadOpenCodeSession(nativeId, sessionId).pipe(Effect9.catchAll((err) => Effect9.fail(new PluginError({
906
969
  pluginId: "opencode",
907
970
  operation: "loadSession",
908
971
  message: String(err),
@@ -932,7 +995,7 @@ import { execFile } from "node:child_process";
932
995
  // ../../packages/server/src/effect/bootstrap.ts
933
996
  import { join as join15 } from "node:path";
934
997
  import { HttpServer } from "@effect/platform";
935
- import { Cause, Effect as Effect32, Fiber, Layer as Layer8 } from "effect";
998
+ import { Cause, Effect as Effect33, Fiber, Layer as Layer8 } from "effect";
936
999
 
937
1000
  // ../../packages/server/src/effect/platform-bun.ts
938
1001
  init_src2();
@@ -948,20 +1011,20 @@ class ServerConfig extends Context3.Tag("@klovi/ServerConfig")() {
948
1011
  }
949
1012
 
950
1013
  // ../../packages/server/src/effect/server-services.ts
951
- import { Context as Context4, Effect as Effect31, Layer as Layer6 } from "effect";
1014
+ import { Context as Context4, Effect as Effect32, Layer as Layer6 } from "effect";
952
1015
 
953
1016
  // ../../packages/server/src/services/auto-discover.ts
954
1017
  init_src();
955
- import { Effect as Effect24 } from "effect";
1018
+ import { Effect as Effect25 } from "effect";
956
1019
 
957
1020
  // ../../packages/plugin-claude-code/src/index.ts
958
1021
  init_src();
959
- import { Effect as Effect12 } from "effect";
1022
+ import { Effect as Effect13 } from "effect";
960
1023
 
961
1024
  // ../../packages/plugin-claude-code/src/discovery.ts
962
1025
  init_src();
963
1026
  import { join as join6 } from "node:path";
964
- import { Effect as Effect10 } from "effect";
1027
+ import { Effect as Effect11 } from "effect";
965
1028
 
966
1029
  // ../../packages/plugin-claude-code/src/command-message.ts
967
1030
  var COMMAND_ARGS_REGEX = /<command-args>(?<content>[\s\S]*?)<\/command-args>/u;
@@ -999,62 +1062,44 @@ function parseCommandMessage(text) {
999
1062
  // ../../packages/plugin-claude-code/src/shared/discovery-utils.ts
1000
1063
  init_src();
1001
1064
  import { join as join5 } from "node:path";
1002
- import { FileSystem as FileSystem4 } from "@effect/platform";
1003
- import { Effect as Effect9 } from "effect";
1065
+ import { FileSystem as FileSystem5 } from "@effect/platform";
1066
+ import { Effect as Effect10 } from "effect";
1004
1067
  var WINDOWS_DRIVE_LETTER_REGEX = /^[A-Za-z]\//u;
1068
+ var STAT_CONCURRENCY = 32;
1005
1069
  function readDirEntriesSafe(dir) {
1006
- return Effect9.gen(function* () {
1007
- const fs = yield* FileSystem4.FileSystem;
1008
- const names = yield* fs.readDirectory(dir).pipe(Effect9.catchAll(() => Effect9.succeed([])));
1009
- const entries = [];
1010
- for (const name of names) {
1011
- const info = yield* fs.stat(join5(dir, name)).pipe(Effect9.catchAll(() => Effect9.succeed(null)));
1012
- if (info) {
1013
- entries.push({ name, isDirectory: info.type === "Directory" });
1014
- }
1015
- }
1016
- return entries;
1070
+ return Effect10.gen(function* () {
1071
+ const fs = yield* FileSystem5.FileSystem;
1072
+ const names = yield* fs.readDirectory(dir).pipe(Effect10.catchAll(() => Effect10.succeed([])));
1073
+ const entries = yield* Effect10.forEach(names, (name) => Effect10.gen(function* () {
1074
+ const info = yield* fs.stat(join5(dir, name)).pipe(Effect10.catchAll(() => Effect10.succeed(null)));
1075
+ return info ? { name, isDirectory: info.type === "Directory" } : null;
1076
+ }), { concurrency: STAT_CONCURRENCY });
1077
+ return entries.filter((entry) => entry !== null);
1017
1078
  });
1018
1079
  }
1019
1080
  function listFilesBySuffix(dir, suffix) {
1020
- return Effect9.gen(function* () {
1021
- const fs = yield* FileSystem4.FileSystem;
1022
- const names = yield* fs.readDirectory(dir).pipe(Effect9.catchAll(() => Effect9.succeed([])));
1081
+ return Effect10.gen(function* () {
1082
+ const fs = yield* FileSystem5.FileSystem;
1083
+ const names = yield* fs.readDirectory(dir).pipe(Effect10.catchAll(() => Effect10.succeed([])));
1023
1084
  return names.filter((name) => name.endsWith(suffix));
1024
1085
  });
1025
1086
  }
1026
1087
  function listFilesWithMtime(dir, suffix) {
1027
- return Effect9.gen(function* () {
1028
- const fs = yield* FileSystem4.FileSystem;
1088
+ return Effect10.gen(function* () {
1089
+ const fs = yield* FileSystem5.FileSystem;
1029
1090
  const names = yield* listFilesBySuffix(dir, suffix);
1030
- const results = [];
1031
- for (const fileName of names) {
1032
- const info = yield* fs.stat(join5(dir, fileName)).pipe(Effect9.catchAll(() => Effect9.succeed(null)));
1033
- if (info?.mtime._tag === "Some") {
1034
- results.push({ fileName, mtime: info.mtime.value.toISOString() });
1035
- }
1036
- }
1091
+ const candidates = yield* Effect10.forEach(names, (fileName) => Effect10.gen(function* () {
1092
+ const info = yield* fs.stat(join5(dir, fileName)).pipe(Effect10.catchAll(() => Effect10.succeed(null)));
1093
+ return info?.mtime._tag === "Some" ? { fileName, mtime: info.mtime.value.toISOString() } : null;
1094
+ }), { concurrency: STAT_CONCURRENCY });
1095
+ const results = candidates.filter((r) => r !== null);
1037
1096
  sortByIsoDesc(results, (item) => item.mtime);
1038
1097
  return results;
1039
1098
  });
1040
1099
  }
1041
- function readTextPrefix(filePath, maxBytes) {
1042
- return Effect9.gen(function* () {
1043
- const fs = yield* FileSystem4.FileSystem;
1044
- const bytes = yield* fs.readFile(filePath);
1045
- const slice = bytes.subarray(0, maxBytes);
1046
- return new TextDecoder().decode(slice);
1047
- });
1048
- }
1049
- function readFileText(filePath) {
1050
- return Effect9.gen(function* () {
1051
- const fs = yield* FileSystem4.FileSystem;
1052
- return yield* fs.readFileString(filePath);
1053
- });
1054
- }
1055
1100
  function fileExists(filePath) {
1056
- return Effect9.gen(function* () {
1057
- const fs = yield* FileSystem4.FileSystem;
1101
+ return Effect10.gen(function* () {
1102
+ const fs = yield* FileSystem5.FileSystem;
1058
1103
  return yield* fs.exists(filePath);
1059
1104
  });
1060
1105
  }
@@ -1069,98 +1114,65 @@ function decodeEncodedPath(encoded) {
1069
1114
  return encoded.replace(/-/gu, "/");
1070
1115
  }
1071
1116
 
1072
- // ../../packages/plugin-claude-code/src/shared/jsonl-utils.ts
1073
- function iterateJsonl(text, visitor, options = {}) {
1074
- const lines = text.split(`
1075
- `);
1076
- const start = Math.max(0, options.startAt ?? 0);
1077
- const end = options.maxLines === undefined ? lines.length : Math.min(lines.length, start + options.maxLines);
1078
- for (let i = start;i < end; i++) {
1079
- const line = lines[i];
1080
- if (!line?.trim()) {
1081
- continue;
1082
- }
1083
- try {
1084
- const parsed = JSON.parse(line);
1085
- const shouldContinue = visitor({
1086
- parsed,
1087
- line,
1088
- lineIndex: i,
1089
- lineNumber: i + 1
1090
- });
1091
- if (shouldContinue === false) {
1092
- break;
1093
- }
1094
- } catch (error) {
1095
- options.onMalformed?.(line, i + 1, error);
1096
- }
1097
- }
1098
- }
1099
-
1100
1117
  // ../../packages/plugin-claude-code/src/discovery.ts
1101
- var BYTES_PER_KB = 1024;
1102
- var CWD_SCAN_KB = 64;
1103
- var CWD_SCAN_BYTES = CWD_SCAN_KB * BYTES_PER_KB;
1104
- var SESSION_META_SCAN_BYTES = BYTES_PER_KB * BYTES_PER_KB;
1105
1118
  var BRACKETED_TEXT_REGEX = /^\[.+\]$/u;
1119
+ var PROJECT_DIR_CONCURRENCY = 16;
1120
+ var SESSION_FILE_CONCURRENCY = 16;
1106
1121
  function inspectProjectSessions(projectDir, sessionFiles) {
1107
- return Effect10.gen(function* () {
1122
+ return Effect11.gen(function* () {
1108
1123
  const lastActivity = sessionFiles[0]?.mtime || "";
1109
1124
  let resolvedPath = "";
1110
1125
  for (const sessionFile of sessionFiles) {
1111
1126
  const filePath = join6(projectDir, sessionFile.fileName);
1112
- if (!resolvedPath) {
1113
- resolvedPath = yield* extractCwd(filePath);
1127
+ resolvedPath = yield* extractCwd(filePath);
1128
+ if (resolvedPath) {
1129
+ break;
1114
1130
  }
1115
1131
  }
1116
1132
  return { lastActivity, resolvedPath };
1117
1133
  });
1118
1134
  }
1119
1135
  function discoverClaudeProjects() {
1120
- return Effect10.gen(function* () {
1136
+ return Effect11.gen(function* () {
1121
1137
  const config = yield* PluginConfig;
1122
1138
  const projectsDir = join6(config.dataDir, "projects");
1123
1139
  const entries = yield* readDirEntriesSafe(projectsDir);
1124
- const projects = [];
1125
- for (const entry of entries) {
1126
- if (!entry.isDirectory) {
1127
- continue;
1128
- }
1140
+ const directories = entries.filter((entry) => entry.isDirectory);
1141
+ const projects = yield* Effect11.forEach(directories, (entry) => Effect11.gen(function* () {
1129
1142
  const projectDir = join6(projectsDir, entry.name);
1130
1143
  const sessionFiles = yield* listFilesWithMtime(projectDir, ".jsonl");
1131
1144
  if (sessionFiles.length === 0) {
1132
- continue;
1145
+ return null;
1133
1146
  }
1134
1147
  const projectInfo = yield* inspectProjectSessions(projectDir, sessionFiles);
1135
1148
  const resolvedPath = projectInfo.resolvedPath || decodeEncodedPath(entry.name);
1136
- projects.push({
1149
+ return {
1137
1150
  pluginId: "claude-code",
1138
1151
  nativeId: entry.name,
1139
1152
  resolvedPath,
1140
1153
  displayName: resolvedPath,
1141
1154
  sessionCount: sessionFiles.length,
1142
1155
  lastActivity: projectInfo.lastActivity
1143
- });
1144
- }
1145
- sortByIsoDesc(projects, (project) => project.lastActivity);
1146
- return projects;
1156
+ };
1157
+ }), { concurrency: PROJECT_DIR_CONCURRENCY });
1158
+ const filtered = projects.filter((p) => p !== null);
1159
+ sortByIsoDesc(filtered, (project) => project.lastActivity);
1160
+ return filtered;
1147
1161
  });
1148
1162
  }
1149
1163
  var PLAN_PREFIX = "Implement the following plan";
1150
1164
  function listClaudeSessions(nativeId) {
1151
- return Effect10.gen(function* () {
1165
+ return Effect11.gen(function* () {
1152
1166
  const config = yield* PluginConfig;
1153
1167
  const projectDir = join6(config.dataDir, "projects", nativeId);
1154
1168
  const files = yield* listFilesBySuffix(projectDir, ".jsonl");
1155
- const sessions = [];
1156
- for (const file of files) {
1169
+ const candidates = yield* Effect11.forEach(files, (file) => Effect11.gen(function* () {
1157
1170
  const filePath = join6(projectDir, file);
1158
1171
  const sessionId = file.replace(".jsonl", "");
1159
1172
  const meta = yield* extractSessionMeta(filePath);
1160
- if (meta) {
1161
- sessions.push({ sessionId, pluginId: "claude-code", ...meta });
1162
- }
1163
- }
1173
+ return meta ? { sessionId, pluginId: "claude-code", ...meta } : null;
1174
+ }), { concurrency: SESSION_FILE_CONCURRENCY });
1175
+ const sessions = candidates.filter((s) => s !== null);
1164
1176
  classifySessionTypes(sessions);
1165
1177
  sortByIsoDesc(sessions, (session) => session.timestamp);
1166
1178
  return sessions;
@@ -1183,20 +1195,16 @@ function classifySessionTypes(sessions) {
1183
1195
  }
1184
1196
  }
1185
1197
  function extractCwd(filePath) {
1186
- return Effect10.gen(function* () {
1187
- const text = yield* readTextPrefix(filePath, CWD_SCAN_BYTES).pipe(Effect10.catchAll(() => Effect10.succeed("")));
1188
- if (!text) {
1189
- return "";
1190
- }
1198
+ return Effect11.gen(function* () {
1191
1199
  let cwd = "";
1192
- iterateJsonl(text, ({ parsed }) => {
1200
+ yield* streamJsonlHead(filePath, ({ parsed }) => {
1193
1201
  const obj = parsed;
1194
1202
  if (obj.cwd) {
1195
1203
  ({ cwd } = obj);
1196
1204
  return false;
1197
1205
  }
1198
1206
  return;
1199
- }, { maxLines: 20 });
1207
+ }, { maxLines: 20 }).pipe(Effect11.catchAll(() => Effect11.void));
1200
1208
  return cwd;
1201
1209
  });
1202
1210
  }
@@ -1241,11 +1249,7 @@ function processMetaLine(obj, meta) {
1241
1249
  }
1242
1250
  }
1243
1251
  function extractSessionMeta(filePath) {
1244
- return Effect10.gen(function* () {
1245
- const text = yield* readTextPrefix(filePath, SESSION_META_SCAN_BYTES).pipe(Effect10.catchAll(() => Effect10.succeed("")));
1246
- if (!text) {
1247
- return null;
1248
- }
1252
+ return Effect11.gen(function* () {
1249
1253
  const meta = {
1250
1254
  timestamp: "",
1251
1255
  slug: "",
@@ -1253,7 +1257,7 @@ function extractSessionMeta(filePath) {
1253
1257
  model: "",
1254
1258
  gitBranch: ""
1255
1259
  };
1256
- iterateJsonl(text, ({ parsed }) => {
1260
+ yield* streamJsonlHead(filePath, ({ parsed }) => {
1257
1261
  const obj = parsed;
1258
1262
  processMetaLine(obj, meta);
1259
1263
  if (isMetaComplete(meta)) {
@@ -1263,7 +1267,7 @@ function extractSessionMeta(filePath) {
1263
1267
  }, {
1264
1268
  maxLines: 50,
1265
1269
  onMalformed: () => {}
1266
- });
1270
+ }).pipe(Effect11.catchAll(() => Effect11.void));
1267
1271
  if (!(meta.timestamp && meta.firstMessage)) {
1268
1272
  return null;
1269
1273
  }
@@ -1280,10 +1284,10 @@ function extractSessionMeta(filePath) {
1280
1284
  // ../../packages/plugin-claude-code/src/parser.ts
1281
1285
  init_src();
1282
1286
  import { join as join7 } from "node:path";
1283
- import { Effect as Effect11 } from "effect";
1287
+ import { Effect as Effect12 } from "effect";
1284
1288
  var MAX_RAW_LINE_LENGTH = 500;
1285
1289
  function loadClaudeSession(nativeId, sessionId) {
1286
- return Effect11.gen(function* () {
1290
+ return Effect12.gen(function* () {
1287
1291
  const config = yield* PluginConfig;
1288
1292
  const filePath = join7(config.dataDir, "projects", nativeId, `${sessionId}.jsonl`);
1289
1293
  const { rawLines, parseErrors } = yield* readJsonlLines(filePath);
@@ -1315,10 +1319,10 @@ function loadClaudeSession(nativeId, sessionId) {
1315
1319
  });
1316
1320
  }
1317
1321
  function parseSubAgentSession(sessionId, encodedPath, agentId) {
1318
- return Effect11.gen(function* () {
1322
+ return Effect12.gen(function* () {
1319
1323
  const config = yield* PluginConfig;
1320
1324
  const filePath = join7(config.dataDir, "projects", encodedPath, sessionId, "subagents", `agent-${agentId}.jsonl`);
1321
- const parsed = yield* readJsonlLines(filePath).pipe(Effect11.catchAll(() => Effect11.succeed({ rawLines: [], parseErrors: [] })));
1325
+ const parsed = yield* readJsonlLines(filePath).pipe(Effect12.catchAll(() => Effect12.succeed({ rawLines: [], parseErrors: [] })));
1322
1326
  const subAgentMap = extractSubAgentMap(parsed.rawLines);
1323
1327
  const turns = buildTurns(parsed.rawLines, parsed.parseErrors);
1324
1328
  for (const turn of turns) {
@@ -1404,11 +1408,10 @@ function findImplSessionId(slug, sessions, currentSessionId) {
1404
1408
  return match?.sessionId;
1405
1409
  }
1406
1410
  function readJsonlLines(filePath) {
1407
- return Effect11.gen(function* () {
1408
- const text = yield* readFileText(filePath);
1411
+ return Effect12.gen(function* () {
1409
1412
  const rawLines = [];
1410
1413
  const parseErrors = [];
1411
- iterateJsonl(text, ({ parsed }) => {
1414
+ yield* streamJsonl(filePath, ({ parsed }) => {
1412
1415
  rawLines.push(parsed);
1413
1416
  }, {
1414
1417
  onMalformed: (line, lineNumber, error) => {
@@ -1713,20 +1716,20 @@ var claudeCodePlugin = {
1713
1716
  id: "claude-code",
1714
1717
  displayName: "Claude Code",
1715
1718
  getDefaultDataDir: () => null,
1716
- isDataAvailable: Effect12.gen(function* () {
1719
+ isDataAvailable: Effect13.gen(function* () {
1717
1720
  const config = yield* PluginConfig;
1718
- return yield* fileExists(config.dataDir).pipe(Effect12.catchAll(() => Effect12.succeed(false)));
1721
+ return yield* fileExists(config.dataDir).pipe(Effect13.catchAll(() => Effect13.succeed(false)));
1719
1722
  }),
1720
1723
  discoverProjects: discoverClaudeProjects(),
1721
1724
  listSessions: (nativeId) => listClaudeSessions(nativeId),
1722
- loadSession: (nativeId, sessionId) => loadClaudeSession(nativeId, sessionId).pipe(Effect12.map((r) => r.session), Effect12.catchAll((err) => Effect12.fail(new PluginError({
1725
+ loadSession: (nativeId, sessionId) => loadClaudeSession(nativeId, sessionId).pipe(Effect13.map((r) => r.session), Effect13.catchAll((err) => Effect13.fail(new PluginError({
1723
1726
  pluginId: "claude-code",
1724
1727
  operation: "loadSession",
1725
1728
  message: String(err),
1726
1729
  cause: err
1727
1730
  })))),
1728
- loadSessionDetail: (nativeId, sessionId) => Effect12.gen(function* () {
1729
- const [{ session, slug }, sessions] = yield* Effect12.all([
1731
+ loadSessionDetail: (nativeId, sessionId) => Effect13.gen(function* () {
1732
+ const [{ session, slug }, sessions] = yield* Effect13.all([
1730
1733
  loadClaudeSession(nativeId, sessionId),
1731
1734
  listClaudeSessions(nativeId)
1732
1735
  ]);
@@ -1735,7 +1738,7 @@ var claudeCodePlugin = {
1735
1738
  planSessionId: findPlanSessionId(session.turns, slug, sessions, sessionId),
1736
1739
  implSessionId: findImplSessionId(slug, sessions, sessionId)
1737
1740
  };
1738
- }).pipe(Effect12.catchAll((err) => Effect12.fail(new PluginError({
1741
+ }).pipe(Effect13.catchAll((err) => Effect13.fail(new PluginError({
1739
1742
  pluginId: "claude-code",
1740
1743
  operation: "loadSessionDetail",
1741
1744
  message: String(err),
@@ -1747,74 +1750,17 @@ var claudeCodePlugin = {
1747
1750
 
1748
1751
  // ../../packages/plugin-codex/src/index.ts
1749
1752
  init_src();
1750
- import { Effect as Effect17 } from "effect";
1753
+ import { Effect as Effect18 } from "effect";
1751
1754
 
1752
1755
  // ../../packages/plugin-codex/src/discovery.ts
1753
1756
  init_src();
1754
- import { Effect as Effect15 } from "effect";
1757
+ import { Effect as Effect16 } from "effect";
1755
1758
 
1756
1759
  // ../../packages/plugin-codex/src/session-index.ts
1757
1760
  init_src();
1758
1761
  import { join as join9 } from "node:path";
1759
1762
  import { FileSystem as FileSystem6 } from "@effect/platform";
1760
1763
  import { Effect as Effect14 } from "effect";
1761
-
1762
- // ../../packages/plugin-codex/src/shared/discovery-utils.ts
1763
- import { FileSystem as FileSystem5 } from "@effect/platform";
1764
- import { Effect as Effect13 } from "effect";
1765
- function readTextPrefix2(filePath, maxBytes) {
1766
- return Effect13.gen(function* () {
1767
- const fs = yield* FileSystem5.FileSystem;
1768
- const bytes = yield* fs.readFile(filePath);
1769
- const slice = bytes.subarray(0, maxBytes);
1770
- return new TextDecoder().decode(slice);
1771
- });
1772
- }
1773
- function readFileText2(filePath) {
1774
- return Effect13.gen(function* () {
1775
- const fs = yield* FileSystem5.FileSystem;
1776
- return yield* fs.readFileString(filePath);
1777
- });
1778
- }
1779
- function fileExists2(filePath) {
1780
- return Effect13.gen(function* () {
1781
- const fs = yield* FileSystem5.FileSystem;
1782
- return yield* fs.exists(filePath);
1783
- });
1784
- }
1785
-
1786
- // ../../packages/plugin-codex/src/shared/jsonl-utils.ts
1787
- function iterateJsonl2(text, visitor, options = {}) {
1788
- const lines = text.split(`
1789
- `);
1790
- const start = Math.max(0, options.startAt ?? 0);
1791
- const end = options.maxLines === undefined ? lines.length : Math.min(lines.length, start + options.maxLines);
1792
- for (let i = start;i < end; i++) {
1793
- const line = lines[i];
1794
- if (!line?.trim()) {
1795
- continue;
1796
- }
1797
- try {
1798
- const parsed = JSON.parse(line);
1799
- const shouldContinue = visitor({
1800
- parsed,
1801
- line,
1802
- lineIndex: i,
1803
- lineNumber: i + 1
1804
- });
1805
- if (shouldContinue === false) {
1806
- break;
1807
- }
1808
- } catch (error) {
1809
- options.onMalformed?.(line, i + 1, error);
1810
- }
1811
- }
1812
- }
1813
-
1814
- // ../../packages/plugin-codex/src/session-index.ts
1815
- var BYTES_PER_KB2 = 1024;
1816
- var FIRST_LINE_SCAN_KB = 512;
1817
- var FIRST_LINE_SCAN_BYTES = FIRST_LINE_SCAN_KB * BYTES_PER_KB2;
1818
1764
  var MS_PER_SECOND2 = 1000;
1819
1765
  function isCodexSessionMeta(obj) {
1820
1766
  return typeof obj === "object" && obj !== null && "uuid" in obj && "cwd" in obj && "timestamps" in obj && typeof obj.uuid === "string" && typeof obj.cwd === "string";
@@ -1854,50 +1800,45 @@ function extractTurnContextModel(parsed) {
1854
1800
  }
1855
1801
  return typeof event.payload?.model === "string" ? event.payload.model : null;
1856
1802
  }
1857
- function inferModelFromPrefix(prefixText) {
1858
- let model = null;
1859
- iterateJsonl2(prefixText, ({ parsed }) => {
1860
- const extracted = extractTurnContextModel(parsed);
1861
- if (isKnownModel(extracted)) {
1862
- model = extracted;
1863
- return false;
1864
- }
1865
- return;
1866
- }, { startAt: 1, maxLines: 256 });
1867
- return model;
1803
+ function streamInferredModel(filePath) {
1804
+ return Effect14.gen(function* () {
1805
+ let model = null;
1806
+ yield* streamJsonlHead(filePath, ({ parsed, lineIndex }) => {
1807
+ if (lineIndex === 0) {
1808
+ return;
1809
+ }
1810
+ const extracted = extractTurnContextModel(parsed);
1811
+ if (isKnownModel(extracted)) {
1812
+ model = extracted;
1813
+ return false;
1814
+ }
1815
+ return;
1816
+ }, { maxLines: 256 }).pipe(Effect14.catchAll(() => Effect14.void));
1817
+ return model;
1818
+ });
1868
1819
  }
1869
1820
  function parseSessionMeta(filePath, fileMtimeEpoch) {
1870
1821
  return Effect14.gen(function* () {
1871
- const prefix = yield* readTextPrefix2(filePath, FIRST_LINE_SCAN_BYTES).pipe(Effect14.catchAll(() => Effect14.succeed("")));
1872
- if (!prefix) {
1822
+ const captured = { value: null };
1823
+ yield* streamJsonlHead(filePath, ({ parsed }) => {
1824
+ captured.value = normalizeSessionMeta(parsed, fileMtimeEpoch);
1825
+ return false;
1826
+ }, { maxLines: 1 }).pipe(Effect14.catchAll(() => Effect14.void));
1827
+ const firstLineMeta = captured.value;
1828
+ if (!firstLineMeta) {
1873
1829
  return null;
1874
1830
  }
1875
- const firstNewline = prefix.indexOf(`
1876
- `);
1877
- const firstLine = firstNewline === -1 ? prefix : prefix.slice(0, firstNewline);
1878
- const trimmedFirstLine = firstLine.trim();
1879
- if (!trimmedFirstLine) {
1880
- return null;
1831
+ if (isKnownModel(firstLineMeta.model)) {
1832
+ return firstLineMeta;
1881
1833
  }
1882
- try {
1883
- const parsed = JSON.parse(trimmedFirstLine);
1884
- const meta = normalizeSessionMeta(parsed, fileMtimeEpoch);
1885
- if (!meta) {
1886
- return null;
1887
- }
1888
- if (isKnownModel(meta.model)) {
1889
- return meta;
1890
- }
1891
- const inferred = inferModelFromPrefix(prefix);
1892
- if (isKnownModel(inferred)) {
1893
- return { ...meta, model: inferred };
1894
- }
1895
- if (isKnownModel(meta.provider_id)) {
1896
- return { ...meta, model: meta.provider_id };
1897
- }
1898
- return meta;
1899
- } catch {}
1900
- return null;
1834
+ const inferred = yield* streamInferredModel(filePath);
1835
+ if (isKnownModel(inferred)) {
1836
+ return { ...firstLineMeta, model: inferred };
1837
+ }
1838
+ if (isKnownModel(firstLineMeta.provider_id)) {
1839
+ return { ...firstLineMeta, model: firstLineMeta.provider_id };
1840
+ }
1841
+ return firstLineMeta;
1901
1842
  });
1902
1843
  }
1903
1844
  function walkJsonlFiles(dir, visit) {
@@ -1983,12 +1924,54 @@ function findCodexSessionFileById(sessionId) {
1983
1924
  });
1984
1925
  }
1985
1926
 
1927
+ // ../../packages/plugin-codex/src/shared/discovery-utils.ts
1928
+ import { FileSystem as FileSystem7 } from "@effect/platform";
1929
+ import { Effect as Effect15 } from "effect";
1930
+ function readFileText(filePath) {
1931
+ return Effect15.gen(function* () {
1932
+ const fs = yield* FileSystem7.FileSystem;
1933
+ return yield* fs.readFileString(filePath);
1934
+ });
1935
+ }
1936
+ function fileExists2(filePath) {
1937
+ return Effect15.gen(function* () {
1938
+ const fs = yield* FileSystem7.FileSystem;
1939
+ return yield* fs.exists(filePath);
1940
+ });
1941
+ }
1942
+
1943
+ // ../../packages/plugin-codex/src/shared/jsonl-utils.ts
1944
+ function iterateJsonl(text, visitor, options = {}) {
1945
+ const lines = text.split(`
1946
+ `);
1947
+ const start = Math.max(0, options.startAt ?? 0);
1948
+ const end = options.maxLines === undefined ? lines.length : Math.min(lines.length, start + options.maxLines);
1949
+ for (let i = start;i < end; i++) {
1950
+ const line = lines[i];
1951
+ if (!line?.trim()) {
1952
+ continue;
1953
+ }
1954
+ try {
1955
+ const parsed = JSON.parse(line);
1956
+ const shouldContinue = visitor({
1957
+ parsed,
1958
+ line,
1959
+ lineIndex: i,
1960
+ lineNumber: i + 1
1961
+ });
1962
+ if (shouldContinue === false) {
1963
+ break;
1964
+ }
1965
+ } catch (error) {
1966
+ options.onMalformed?.(line, i + 1, error);
1967
+ }
1968
+ }
1969
+ }
1970
+
1986
1971
  // ../../packages/plugin-codex/src/discovery.ts
1987
- var BYTES_PER_KB3 = 1024;
1988
- var SESSION_TITLE_SCAN_KB = 256;
1989
- var SESSION_TITLE_SCAN_BYTES = SESSION_TITLE_SCAN_KB * BYTES_PER_KB3;
1972
+ var SESSION_FILE_CONCURRENCY2 = 16;
1990
1973
  function discoverCodexProjects() {
1991
- return Effect15.gen(function* () {
1974
+ return Effect16.gen(function* () {
1992
1975
  const sessions = yield* scanCodexSessions();
1993
1976
  const byCwd = new Map;
1994
1977
  for (const session of sessions) {
@@ -2020,9 +2003,41 @@ function discoverCodexProjects() {
2020
2003
  return projects;
2021
2004
  });
2022
2005
  }
2023
- function extractFirstUserMessage(text) {
2006
+ function visitForFirstUserMessage(parsed, captured) {
2007
+ const event = parsed;
2008
+ if (event.type === "item.completed" && event.item?.type === "agent_message" && event.item.text) {
2009
+ const maxPreviewLength = 200;
2010
+ captured.value = event.item.text.slice(0, maxPreviewLength);
2011
+ return true;
2012
+ }
2013
+ if (event.type === "event_msg" && event.payload?.type === "user_message") {
2014
+ const payloadText = event.payload.message || event.payload.text;
2015
+ if (typeof payloadText === "string" && payloadText) {
2016
+ const maxMsgLength = 200;
2017
+ captured.value = payloadText.slice(0, maxMsgLength);
2018
+ return true;
2019
+ }
2020
+ }
2021
+ return false;
2022
+ }
2023
+ function streamFirstUserMessage(filePath) {
2024
+ return Effect16.gen(function* () {
2025
+ const captured = { value: null };
2026
+ yield* streamJsonlHead(filePath, ({ parsed, lineIndex }) => {
2027
+ if (lineIndex === 0) {
2028
+ return;
2029
+ }
2030
+ if (visitForFirstUserMessage(parsed, captured)) {
2031
+ return false;
2032
+ }
2033
+ return;
2034
+ }, { maxLines: 200 }).pipe(Effect16.catchAll(() => Effect16.void));
2035
+ return captured.value;
2036
+ });
2037
+ }
2038
+ function extractFirstUserMessageFromText(text) {
2024
2039
  let message = null;
2025
- iterateJsonl2(text, ({ parsed }) => {
2040
+ iterateJsonl(text, ({ parsed }) => {
2026
2041
  const event = parsed;
2027
2042
  if (event.type === "item.completed" && event.item?.type === "agent_message" && event.item.text) {
2028
2043
  const maxPreviewLength = 200;
@@ -2042,23 +2057,21 @@ function extractFirstUserMessage(text) {
2042
2057
  return message;
2043
2058
  }
2044
2059
  function listCodexSessions(nativeId) {
2045
- return Effect15.gen(function* () {
2060
+ return Effect16.gen(function* () {
2046
2061
  const allSessions = yield* scanCodexSessions();
2047
2062
  const matching = allSessions.filter((s) => s.meta.cwd === nativeId);
2048
- const sessions = [];
2049
- for (const s of matching) {
2063
+ const sessions = yield* Effect16.forEach(matching, (s) => Effect16.gen(function* () {
2050
2064
  let firstMessage = s.meta.name ?? "";
2051
2065
  if (!firstMessage) {
2052
- const prefix = yield* readTextPrefix2(s.filePath, SESSION_TITLE_SCAN_BYTES).pipe(Effect15.catchAll(() => Effect15.succeed("")));
2053
- firstMessage = extractFirstUserMessage(prefix) ?? "";
2066
+ firstMessage = (yield* streamFirstUserMessage(s.filePath)) ?? "";
2054
2067
  if (!firstMessage) {
2055
- const fullText = yield* readFileText2(s.filePath).pipe(Effect15.catchAll(() => Effect15.succeed("")));
2056
- firstMessage = extractFirstUserMessage(fullText) ?? "";
2068
+ const fullText = yield* readFileText(s.filePath).pipe(Effect16.catchAll(() => Effect16.succeed("")));
2069
+ firstMessage = extractFirstUserMessageFromText(fullText) ?? "";
2057
2070
  }
2058
2071
  firstMessage ||= "Codex session";
2059
2072
  }
2060
2073
  const timestamp = epochSecondsToIso(s.meta.timestamps.created);
2061
- sessions.push({
2074
+ return {
2062
2075
  sessionId: s.meta.uuid,
2063
2076
  timestamp,
2064
2077
  slug: s.meta.uuid,
@@ -2066,8 +2079,8 @@ function listCodexSessions(nativeId) {
2066
2079
  model: s.meta.model || "unknown",
2067
2080
  gitBranch: "",
2068
2081
  pluginId: "codex-cli"
2069
- });
2070
- }
2082
+ };
2083
+ }), { concurrency: SESSION_FILE_CONCURRENCY2 });
2071
2084
  sortByIsoDesc(sessions, (session) => session.timestamp);
2072
2085
  return sessions;
2073
2086
  });
@@ -2075,7 +2088,7 @@ function listCodexSessions(nativeId) {
2075
2088
 
2076
2089
  // ../../packages/plugin-codex/src/parser.ts
2077
2090
  init_src();
2078
- import { Effect as Effect16 } from "effect";
2091
+ import { Effect as Effect17 } from "effect";
2079
2092
  function isKnownModel2(model) {
2080
2093
  return typeof model === "string" && model.length > 0 && model !== "unknown";
2081
2094
  }
@@ -2413,7 +2426,7 @@ function buildCodexTurns(events, model, timestamp) {
2413
2426
  return state.turns;
2414
2427
  }
2415
2428
  function loadCodexSession(_nativeId, sessionId) {
2416
- return Effect16.gen(function* () {
2429
+ return Effect17.gen(function* () {
2417
2430
  const filePath = yield* findCodexSessionFileById(sessionId);
2418
2431
  if (!filePath) {
2419
2432
  return {
@@ -2423,11 +2436,11 @@ function loadCodexSession(_nativeId, sessionId) {
2423
2436
  pluginId: "codex-cli"
2424
2437
  };
2425
2438
  }
2426
- const text = yield* readFileText2(filePath);
2439
+ const text = yield* readFileText(filePath);
2427
2440
  let meta = null;
2428
2441
  const events = [];
2429
2442
  let turnContextModel = null;
2430
- iterateJsonl2(text, ({ parsed, lineIndex }) => {
2443
+ iterateJsonl(text, ({ parsed, lineIndex }) => {
2431
2444
  if (lineIndex === 0) {
2432
2445
  const normalized = normalizeSessionMeta(parsed);
2433
2446
  if (normalized) {
@@ -2467,13 +2480,13 @@ var codexCliPlugin = {
2467
2480
  id: "codex-cli",
2468
2481
  displayName: "Codex",
2469
2482
  getDefaultDataDir: () => null,
2470
- isDataAvailable: Effect17.gen(function* () {
2483
+ isDataAvailable: Effect18.gen(function* () {
2471
2484
  const config = yield* PluginConfig;
2472
- return yield* fileExists2(config.dataDir).pipe(Effect17.catchAll(() => Effect17.succeed(false)));
2485
+ return yield* fileExists2(config.dataDir).pipe(Effect18.catchAll(() => Effect18.succeed(false)));
2473
2486
  }),
2474
2487
  discoverProjects: discoverCodexProjects(),
2475
2488
  listSessions: (nativeId) => listCodexSessions(nativeId),
2476
- loadSession: (nativeId, sessionId) => loadCodexSession(nativeId, sessionId).pipe(Effect17.catchAll((err) => Effect17.fail(new PluginError({
2489
+ loadSession: (nativeId, sessionId) => loadCodexSession(nativeId, sessionId).pipe(Effect18.catchAll((err) => Effect18.fail(new PluginError({
2477
2490
  pluginId: "codex-cli",
2478
2491
  operation: "loadSession",
2479
2492
  message: String(err),
@@ -2484,8 +2497,8 @@ var codexCliPlugin = {
2484
2497
 
2485
2498
  // ../../packages/plugin-cursor/src/index.ts
2486
2499
  init_src();
2487
- import { FileSystem as FileSystem9 } from "@effect/platform";
2488
- import { Effect as Effect23 } from "effect";
2500
+ import { FileSystem as FileSystem10 } from "@effect/platform";
2501
+ import { Effect as Effect24 } from "effect";
2489
2502
 
2490
2503
  // ../../packages/plugin-cursor/src/config.ts
2491
2504
  import { posix, win32 } from "node:path";
@@ -2543,16 +2556,16 @@ var DEFAULT_CURSOR_DIR = getDefaultCursorDir();
2543
2556
  init_src();
2544
2557
  import { join as join12 } from "node:path";
2545
2558
  import { fileURLToPath } from "node:url";
2546
- import { Effect as Effect21 } from "effect";
2559
+ import { Effect as Effect22 } from "effect";
2547
2560
 
2548
2561
  // ../../packages/plugin-cursor/src/db.ts
2549
2562
  init_src();
2550
- import { FileSystem as FileSystem7 } from "@effect/platform";
2551
- import { Effect as Effect18 } from "effect";
2563
+ import { FileSystem as FileSystem8 } from "@effect/platform";
2564
+ import { Effect as Effect19 } from "effect";
2552
2565
  function openCursorDbIfExists(dbPath) {
2553
- return Effect18.gen(function* () {
2554
- const fs = yield* FileSystem7.FileSystem;
2555
- const exists = yield* fs.exists(dbPath).pipe(Effect18.catchAll(() => Effect18.succeed(false)));
2566
+ return Effect19.gen(function* () {
2567
+ const fs = yield* FileSystem8.FileSystem;
2568
+ const exists = yield* fs.exists(dbPath).pipe(Effect19.catchAll(() => Effect19.succeed(false)));
2556
2569
  if (!exists) {
2557
2570
  return null;
2558
2571
  }
@@ -2564,25 +2577,25 @@ function openCursorGlobalDb() {
2564
2577
  return openCursorDbIfExists(getCursorGlobalDbPath());
2565
2578
  }
2566
2579
  function getCursorWorkspaceStorageDirEffect() {
2567
- return Effect18.succeed(getCursorWorkspaceStorageDir());
2580
+ return Effect19.succeed(getCursorWorkspaceStorageDir());
2568
2581
  }
2569
2582
 
2570
2583
  // ../../packages/plugin-cursor/src/plans.ts
2571
2584
  import { basename } from "node:path";
2572
- import { Effect as Effect20 } from "effect";
2585
+ import { Effect as Effect21 } from "effect";
2573
2586
 
2574
2587
  // ../../packages/plugin-cursor/src/shared/discovery-utils.ts
2575
2588
  init_src();
2576
2589
  import { join as join11 } from "node:path";
2577
- import { FileSystem as FileSystem8 } from "@effect/platform";
2578
- import { Effect as Effect19 } from "effect";
2590
+ import { FileSystem as FileSystem9 } from "@effect/platform";
2591
+ import { Effect as Effect20 } from "effect";
2579
2592
  function readDirEntriesSafe2(dir) {
2580
- return Effect19.gen(function* () {
2581
- const fs = yield* FileSystem8.FileSystem;
2582
- const names = yield* fs.readDirectory(dir).pipe(Effect19.catchAll(() => Effect19.succeed([])));
2593
+ return Effect20.gen(function* () {
2594
+ const fs = yield* FileSystem9.FileSystem;
2595
+ const names = yield* fs.readDirectory(dir).pipe(Effect20.catchAll(() => Effect20.succeed([])));
2583
2596
  const entries = [];
2584
2597
  for (const name of names) {
2585
- const info = yield* fs.stat(join11(dir, name)).pipe(Effect19.catchAll(() => Effect19.succeed(null)));
2598
+ const info = yield* fs.stat(join11(dir, name)).pipe(Effect20.catchAll(() => Effect20.succeed(null)));
2586
2599
  if (info) {
2587
2600
  entries.push({ name, isDirectory: info.type === "Directory" });
2588
2601
  }
@@ -2590,28 +2603,28 @@ function readDirEntriesSafe2(dir) {
2590
2603
  return entries;
2591
2604
  });
2592
2605
  }
2593
- function readFileText3(filePath) {
2594
- return Effect19.gen(function* () {
2595
- const fs = yield* FileSystem8.FileSystem;
2606
+ function readFileText2(filePath) {
2607
+ return Effect20.gen(function* () {
2608
+ const fs = yield* FileSystem9.FileSystem;
2596
2609
  return yield* fs.readFileString(filePath);
2597
2610
  });
2598
2611
  }
2599
2612
  function fileExists3(filePath) {
2600
- return Effect19.gen(function* () {
2601
- const fs = yield* FileSystem8.FileSystem;
2602
- return yield* fs.exists(filePath).pipe(Effect19.catchAll(() => Effect19.succeed(false)));
2613
+ return Effect20.gen(function* () {
2614
+ const fs = yield* FileSystem9.FileSystem;
2615
+ return yield* fs.exists(filePath).pipe(Effect20.catchAll(() => Effect20.succeed(false)));
2603
2616
  });
2604
2617
  }
2605
2618
  function listFilesWithMtime2(dir, suffix) {
2606
- return Effect19.gen(function* () {
2607
- const fs = yield* FileSystem8.FileSystem;
2608
- const names = yield* fs.readDirectory(dir).pipe(Effect19.catchAll(() => Effect19.succeed([])));
2619
+ return Effect20.gen(function* () {
2620
+ const fs = yield* FileSystem9.FileSystem;
2621
+ const names = yield* fs.readDirectory(dir).pipe(Effect20.catchAll(() => Effect20.succeed([])));
2609
2622
  const results = [];
2610
2623
  for (const name of names) {
2611
2624
  if (!name.endsWith(suffix)) {
2612
2625
  continue;
2613
2626
  }
2614
- const info = yield* fs.stat(join11(dir, name)).pipe(Effect19.catchAll(() => Effect19.succeed(null)));
2627
+ const info = yield* fs.stat(join11(dir, name)).pipe(Effect20.catchAll(() => Effect20.succeed(null)));
2615
2628
  if (info?.mtime._tag === "Some") {
2616
2629
  results.push({ fileName: name, mtime: info.mtime.value.toISOString() });
2617
2630
  }
@@ -2678,7 +2691,7 @@ function getPlanFallbackName(filePath) {
2678
2691
  return basename(filePath).replace(PLAN_FILE_SUFFIX_REGEX, "");
2679
2692
  }
2680
2693
  function readPlanDisplayName(filePath) {
2681
- return readFileText3(filePath).pipe(Effect20.map((text) => {
2694
+ return readFileText2(filePath).pipe(Effect21.map((text) => {
2682
2695
  const parsed = parsePlanFrontmatter(text);
2683
2696
  return parsed.name || extractFirstHeading(parsed.body) || getPlanFallbackName(filePath);
2684
2697
  }));
@@ -2692,7 +2705,7 @@ function makeSystemTurn(text, timestamp) {
2692
2705
  };
2693
2706
  }
2694
2707
  function loadCursorPlanSession(plan) {
2695
- return readFileText3(plan.filePath).pipe(Effect20.map((text) => {
2708
+ return readFileText2(plan.filePath).pipe(Effect21.map((text) => {
2696
2709
  const parsed = parsePlanFrontmatter(text);
2697
2710
  return {
2698
2711
  sessionId: plan.rawSessionId,
@@ -2713,7 +2726,7 @@ function tryParseJson2(value) {
2713
2726
  }
2714
2727
 
2715
2728
  // ../../packages/plugin-cursor/src/shared/jsonl-utils.ts
2716
- function iterateJsonl3(text, visitor, options = {}) {
2729
+ function iterateJsonl2(text, visitor, options = {}) {
2717
2730
  const lines = text.split(`
2718
2731
  `);
2719
2732
  const start = Math.max(0, options.startAt ?? 0);
@@ -2900,7 +2913,7 @@ function parseProjectPathFromWorkspaceJson(content) {
2900
2913
  return fileUrlToPath(parsed.folder);
2901
2914
  }
2902
2915
  function discoverWorkspaceDescriptors(targetProjectPath) {
2903
- return Effect21.gen(function* () {
2916
+ return Effect22.gen(function* () {
2904
2917
  const workspaceStorageDir = yield* getCursorWorkspaceStorageDirEffect();
2905
2918
  const workspaceEntries = yield* readDirEntriesSafe2(workspaceStorageDir);
2906
2919
  const workspaceDescriptors = [];
@@ -2915,7 +2928,7 @@ function discoverWorkspaceDescriptors(targetProjectPath) {
2915
2928
  if (!exists) {
2916
2929
  continue;
2917
2930
  }
2918
- const projectPath = yield* readFileText3(workspaceJsonPath).pipe(Effect21.map(parseProjectPathFromWorkspaceJson), Effect21.catchAll(() => Effect21.succeed(null)));
2931
+ const projectPath = yield* readFileText2(workspaceJsonPath).pipe(Effect22.map(parseProjectPathFromWorkspaceJson), Effect22.catchAll(() => Effect22.succeed(null)));
2919
2932
  if (!projectPath || targetProjectPath && projectPath !== targetProjectPath) {
2920
2933
  continue;
2921
2934
  }
@@ -2948,7 +2961,7 @@ function collectComposerSessions({
2948
2961
  composersById,
2949
2962
  options
2950
2963
  }) {
2951
- return Effect21.gen(function* () {
2964
+ return Effect22.gen(function* () {
2952
2965
  for (const descriptor of workspaceDescriptors) {
2953
2966
  const workspaceDb = yield* openCursorDbIfExists(descriptor.workspaceDbPath);
2954
2967
  if (!workspaceDb) {
@@ -2978,7 +2991,7 @@ function collectComposerSessions({
2978
2991
  }
2979
2992
  function readFirstTranscriptUserMessage(text) {
2980
2993
  let firstMessage = "";
2981
- iterateJsonl3(text, ({ parsed }) => {
2994
+ iterateJsonl2(text, ({ parsed }) => {
2982
2995
  const line = parsed;
2983
2996
  if (line.role !== "user" || !Array.isArray(line.message?.content)) {
2984
2997
  return;
@@ -2995,7 +3008,7 @@ function readFirstTranscriptUserMessage(text) {
2995
3008
  return firstMessage;
2996
3009
  }
2997
3010
  function listTranscriptFiles(agentTranscriptsDir) {
2998
- return Effect21.gen(function* () {
3011
+ return Effect22.gen(function* () {
2999
3012
  const agentDirs = yield* readDirEntriesSafe2(agentTranscriptsDir);
3000
3013
  const files = [];
3001
3014
  for (const entry of agentDirs) {
@@ -3036,7 +3049,7 @@ function createBackgroundAgentSummary(projectPath, transcript, firstMessage) {
3036
3049
  };
3037
3050
  }
3038
3051
  function discoverBackgroundAgentsForProject(projectPath, options = { readPreviewText: true }) {
3039
- return Effect21.gen(function* () {
3052
+ return Effect22.gen(function* () {
3040
3053
  const config = yield* PluginConfig;
3041
3054
  const agentTranscriptsDir = join12(config.dataDir, "projects", encodeCursorProjectPath(projectPath), "agent-transcripts");
3042
3055
  const agentsById = new Map;
@@ -3048,7 +3061,7 @@ function discoverBackgroundAgentsForProject(projectPath, options = { readPreview
3048
3061
  for (const transcript of transcripts) {
3049
3062
  let firstMessage = "Cursor background agent";
3050
3063
  if (options.readPreviewText) {
3051
- const text = yield* readFileText3(transcript.filePath).pipe(Effect21.catchAll(() => Effect21.succeed("")));
3064
+ const text = yield* readFileText2(transcript.filePath).pipe(Effect22.catchAll(() => Effect22.succeed("")));
3052
3065
  firstMessage = readFirstTranscriptUserMessage(text) || firstMessage;
3053
3066
  }
3054
3067
  agentsById.set(transcript.agentId, createBackgroundAgentSummary(projectPath, transcript, firstMessage));
@@ -3057,7 +3070,7 @@ function discoverBackgroundAgentsForProject(projectPath, options = { readPreview
3057
3070
  });
3058
3071
  }
3059
3072
  function discoverBackgroundAgents(projectPaths, options = { readPreviewText: true }) {
3060
- return Effect21.gen(function* () {
3073
+ return Effect22.gen(function* () {
3061
3074
  const agentsById = new Map;
3062
3075
  for (const projectPath of new Set(projectPaths)) {
3063
3076
  const projectAgents = yield* discoverBackgroundAgentsForProject(projectPath, options);
@@ -3100,7 +3113,7 @@ function createPlanSummary(entry, projectPath, filePath, displayName) {
3100
3113
  };
3101
3114
  }
3102
3115
  function discoverMappedPlans(globalDb, composersById, agentsById, options = { loadDisplayName: true }) {
3103
- return Effect21.gen(function* () {
3116
+ return Effect22.gen(function* () {
3104
3117
  const plansById = new Map;
3105
3118
  if (!globalDb) {
3106
3119
  return plansById;
@@ -3121,7 +3134,7 @@ function discoverMappedPlans(globalDb, composersById, agentsById, options = { lo
3121
3134
  if (!exists) {
3122
3135
  continue;
3123
3136
  }
3124
- const displayName = options.loadDisplayName ? yield* readPlanDisplayName(filePath).pipe(Effect21.catchAll(() => Effect21.succeed(""))) : "";
3137
+ const displayName = options.loadDisplayName ? yield* readPlanDisplayName(filePath).pipe(Effect22.catchAll(() => Effect22.succeed(""))) : "";
3125
3138
  const summary = createPlanSummary(entry, projectPath, filePath, displayName || entry.name?.trim() || "Cursor plan");
3126
3139
  if (!summary) {
3127
3140
  continue;
@@ -3154,7 +3167,7 @@ function buildProjects(sessionsByProject) {
3154
3167
  return projects;
3155
3168
  }
3156
3169
  function buildCursorProjectIndex(nativeId) {
3157
- return Effect21.gen(function* () {
3170
+ return Effect22.gen(function* () {
3158
3171
  const globalDb = yield* openCursorGlobalDb();
3159
3172
  const sessionsByProject = new Map;
3160
3173
  const composersById = new Map;
@@ -3184,7 +3197,7 @@ function buildCursorProjectIndex(nativeId) {
3184
3197
  });
3185
3198
  }
3186
3199
  function buildCursorIndex() {
3187
- return Effect21.gen(function* () {
3200
+ return Effect22.gen(function* () {
3188
3201
  const globalDb = yield* openCursorGlobalDb();
3189
3202
  const sessionsByProject = new Map;
3190
3203
  const composersById = new Map;
@@ -3227,7 +3240,7 @@ function toSessionSummary(session) {
3227
3240
  };
3228
3241
  }
3229
3242
  function buildCursorDiscoveryIndex() {
3230
- return buildCursorIndex().pipe(Effect21.map((index) => ({
3243
+ return buildCursorIndex().pipe(Effect22.map((index) => ({
3231
3244
  projects: index.projects,
3232
3245
  sessionsByNativeId: new Map([...index.sessionsByProject.entries()].map(([projectPath, sessions]) => [
3233
3246
  projectPath,
@@ -3236,7 +3249,7 @@ function buildCursorDiscoveryIndex() {
3236
3249
  })));
3237
3250
  }
3238
3251
  function discoverCursorProjects() {
3239
- return Effect21.gen(function* () {
3252
+ return Effect22.gen(function* () {
3240
3253
  const globalDb = yield* openCursorGlobalDb();
3241
3254
  const sessionsByProject = new Map;
3242
3255
  const composersById = new Map;
@@ -3263,7 +3276,7 @@ function discoverCursorProjects() {
3263
3276
  });
3264
3277
  }
3265
3278
  function listCursorSessions(nativeId) {
3266
- return buildCursorProjectIndex(nativeId).pipe(Effect21.map((index) => {
3279
+ return buildCursorProjectIndex(nativeId).pipe(Effect22.map((index) => {
3267
3280
  const sessions = index.sessionsByProject.get(nativeId) ?? [];
3268
3281
  sortByIsoDesc(sessions, (session) => session.timestamp);
3269
3282
  return sessions.map(toSessionSummary);
@@ -3271,7 +3284,7 @@ function listCursorSessions(nativeId) {
3271
3284
  }
3272
3285
 
3273
3286
  // ../../packages/plugin-cursor/src/parser.ts
3274
- import { Effect as Effect22 } from "effect";
3287
+ import { Effect as Effect23 } from "effect";
3275
3288
  var COMPOSER_PREFIX_REGEX = /^composer:/u;
3276
3289
  var AGENT_PREFIX_REGEX = /^agent:/u;
3277
3290
  var PLAN_PREFIX_REGEX = /^plan:/u;
@@ -3410,7 +3423,7 @@ function partialCursorSessionNotice(timestamp) {
3410
3423
  Klovi could not reconstruct the full Composer transcript from Cursor state. Showing recovered content only.`);
3411
3424
  }
3412
3425
  function loadCursorComposerSession(summary) {
3413
- return Effect22.gen(function* () {
3426
+ return Effect23.gen(function* () {
3414
3427
  const globalDb = yield* openCursorGlobalDb();
3415
3428
  const baseTimestampMs = summary.createdAtMs;
3416
3429
  try {
@@ -3460,11 +3473,11 @@ function loadCursorComposerSession(summary) {
3460
3473
  });
3461
3474
  }
3462
3475
  function loadCursorAgentSession(summary) {
3463
- return Effect22.gen(function* () {
3464
- const text = yield* readFileText3(summary.filePath);
3476
+ return Effect23.gen(function* () {
3477
+ const text = yield* readFileText2(summary.filePath);
3465
3478
  const turns = [];
3466
3479
  let turnIndex = 0;
3467
- iterateJsonl3(text, ({ parsed }) => {
3480
+ iterateJsonl2(text, ({ parsed }) => {
3468
3481
  const line = parsed;
3469
3482
  if (!Array.isArray(line.message?.content)) {
3470
3483
  return;
@@ -3496,11 +3509,11 @@ function loadCursorAgentSession(summary) {
3496
3509
  });
3497
3510
  }
3498
3511
  function loadCursorSession(nativeId, sessionId) {
3499
- return Effect22.gen(function* () {
3512
+ return Effect23.gen(function* () {
3500
3513
  const index = yield* buildCursorProjectIndex(nativeId);
3501
3514
  const session = index.composersById.get(sessionId.replace(COMPOSER_PREFIX_REGEX, "")) ?? index.agentsById.get(sessionId.replace(AGENT_PREFIX_REGEX, "")) ?? index.plansById.get(sessionId.replace(PLAN_PREFIX_REGEX, ""));
3502
3515
  if (!session || session.projectPath !== nativeId) {
3503
- return yield* Effect22.fail(new Error(`Cursor session not found: ${sessionId}`));
3516
+ return yield* Effect23.fail(new Error(`Cursor session not found: ${sessionId}`));
3504
3517
  }
3505
3518
  if (session.kind === "composer") {
3506
3519
  return yield* loadCursorComposerSession(session);
@@ -3516,38 +3529,38 @@ var cursorPlugin = {
3516
3529
  id: "cursor",
3517
3530
  displayName: "Cursor",
3518
3531
  getDefaultDataDir: () => null,
3519
- isDataAvailable: Effect23.gen(function* () {
3532
+ isDataAvailable: Effect24.gen(function* () {
3520
3533
  const config = yield* PluginConfig;
3521
- const fs = yield* FileSystem9.FileSystem;
3522
- const userDataExists = yield* fileExists3(config.dataDir).pipe(Effect23.catchAll(() => Effect23.succeed(false)));
3534
+ const fs = yield* FileSystem10.FileSystem;
3535
+ const userDataExists = yield* fileExists3(config.dataDir).pipe(Effect24.catchAll(() => Effect24.succeed(false)));
3523
3536
  if (userDataExists) {
3524
3537
  return true;
3525
3538
  }
3526
- const globalDbExists = yield* fs.exists(getCursorGlobalDbPath()).pipe(Effect23.catchAll(() => Effect23.succeed(false)));
3539
+ const globalDbExists = yield* fs.exists(getCursorGlobalDbPath()).pipe(Effect24.catchAll(() => Effect24.succeed(false)));
3527
3540
  if (globalDbExists) {
3528
3541
  return true;
3529
3542
  }
3530
- return yield* fs.exists(getCursorWorkspaceStorageDir()).pipe(Effect23.catchAll(() => Effect23.succeed(false)));
3543
+ return yield* fs.exists(getCursorWorkspaceStorageDir()).pipe(Effect24.catchAll(() => Effect24.succeed(false)));
3531
3544
  }),
3532
- discoverProjects: discoverCursorProjects().pipe(Effect23.catchAll((err) => Effect23.fail(new PluginError({
3545
+ discoverProjects: discoverCursorProjects().pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
3533
3546
  pluginId: "cursor",
3534
3547
  operation: "discoverProjects",
3535
3548
  message: String(err),
3536
3549
  cause: err
3537
3550
  })))),
3538
- discoverIndex: buildCursorDiscoveryIndex().pipe(Effect23.catchAll((err) => Effect23.fail(new PluginError({
3551
+ discoverIndex: buildCursorDiscoveryIndex().pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
3539
3552
  pluginId: "cursor",
3540
3553
  operation: "discoverIndex",
3541
3554
  message: String(err),
3542
3555
  cause: err
3543
3556
  })))),
3544
- listSessions: (nativeId) => listCursorSessions(nativeId).pipe(Effect23.catchAll((err) => Effect23.fail(new PluginError({
3557
+ listSessions: (nativeId) => listCursorSessions(nativeId).pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
3545
3558
  pluginId: "cursor",
3546
3559
  operation: "listSessions",
3547
3560
  message: String(err),
3548
3561
  cause: err
3549
3562
  })))),
3550
- loadSession: (nativeId, sessionId) => loadCursorSession(nativeId, sessionId).pipe(Effect23.catchAll((err) => Effect23.fail(new PluginError({
3563
+ loadSession: (nativeId, sessionId) => loadCursorSession(nativeId, sessionId).pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
3551
3564
  pluginId: "cursor",
3552
3565
  operation: "loadSession",
3553
3566
  message: String(err),
@@ -3588,7 +3601,7 @@ class PluginRegistry2 extends PluginRegistry {
3588
3601
 
3589
3602
  // ../../packages/server/src/services/auto-discover.ts
3590
3603
  function createRegistry(settings) {
3591
- return Effect24.gen(function* () {
3604
+ return Effect25.gen(function* () {
3592
3605
  const registry = new PluginRegistry2;
3593
3606
  for (const { plugin, defaultDir, defaultEnabled } of BUILTIN_PLUGIN_DESCRIPTORS) {
3594
3607
  const pluginSettings = settings?.plugins[plugin.id];
@@ -3598,7 +3611,7 @@ function createRegistry(settings) {
3598
3611
  }
3599
3612
  const dataDir = pluginSettings?.dataDir ?? defaultDir;
3600
3613
  const configLayer = makePluginConfigLayer({ dataDir });
3601
- const available = yield* plugin.isDataAvailable.pipe(Effect24.provide(configLayer), Effect24.catchAll(() => Effect24.succeed(false)));
3614
+ const available = yield* plugin.isDataAvailable.pipe(Effect25.provide(configLayer), Effect25.catchAll(() => Effect25.succeed(false)));
3602
3615
  if (available) {
3603
3616
  registry.register(plugin, { dataDir });
3604
3617
  }
@@ -3608,12 +3621,12 @@ function createRegistry(settings) {
3608
3621
  }
3609
3622
 
3610
3623
  // ../../packages/server/src/services/onboarding-service.ts
3611
- import { Effect as Effect26 } from "effect";
3624
+ import { Effect as Effect27 } from "effect";
3612
3625
 
3613
3626
  // ../../packages/server/src/services/settings.ts
3614
3627
  import { dirname, join as join13 } from "node:path";
3615
- import { FileSystem as FileSystem10 } from "@effect/platform";
3616
- import { Effect as Effect25 } from "effect";
3628
+ import { FileSystem as FileSystem11 } from "@effect/platform";
3629
+ import { Effect as Effect26 } from "effect";
3617
3630
 
3618
3631
  // ../../packages/server/src/services/errors.ts
3619
3632
  import { Data as Data2 } from "effect";
@@ -3658,16 +3671,16 @@ function getDefaultSettings() {
3658
3671
  };
3659
3672
  }
3660
3673
  function loadSettings(path) {
3661
- return Effect25.gen(function* () {
3662
- const fs = yield* FileSystem10.FileSystem;
3663
- const content = yield* fs.readFileString(path).pipe(Effect25.catchAll(() => Effect25.succeed(null)));
3674
+ return Effect26.gen(function* () {
3675
+ const fs = yield* FileSystem11.FileSystem;
3676
+ const content = yield* fs.readFileString(path).pipe(Effect26.catchAll(() => Effect26.succeed(null)));
3664
3677
  if (content === null) {
3665
3678
  return getDefaultSettings();
3666
3679
  }
3667
- const parsed = yield* Effect25.try({
3680
+ const parsed = yield* Effect26.try({
3668
3681
  try: () => JSON.parse(content),
3669
3682
  catch: () => null
3670
- }).pipe(Effect25.catchAll(() => Effect25.succeed(null)));
3683
+ }).pipe(Effect26.catchAll(() => Effect26.succeed(null)));
3671
3684
  if (parsed === null || parsed["version"] !== 1 || typeof parsed["plugins"] !== "object") {
3672
3685
  return getDefaultSettings();
3673
3686
  }
@@ -3675,37 +3688,37 @@ function loadSettings(path) {
3675
3688
  });
3676
3689
  }
3677
3690
  function saveSettings(path, settings) {
3678
- return Effect25.gen(function* () {
3679
- const fs = yield* FileSystem10.FileSystem;
3691
+ return Effect26.gen(function* () {
3692
+ const fs = yield* FileSystem11.FileSystem;
3680
3693
  const dir = dirname(path);
3681
- yield* fs.makeDirectory(dir, { recursive: true }).pipe(Effect25.mapError((cause) => new SettingsWriteError({ path, cause })));
3694
+ yield* fs.makeDirectory(dir, { recursive: true }).pipe(Effect26.mapError((cause) => new SettingsWriteError({ path, cause })));
3682
3695
  const tmpPath = join13(dir, `.settings-${Date.now()}.tmp`);
3683
- yield* fs.writeFileString(tmpPath, JSON.stringify(settings, null, 2)).pipe(Effect25.mapError((cause) => new SettingsWriteError({ path, cause })));
3684
- yield* fs.rename(tmpPath, path).pipe(Effect25.mapError((cause) => new SettingsWriteError({ path, cause })));
3696
+ yield* fs.writeFileString(tmpPath, JSON.stringify(settings, null, 2)).pipe(Effect26.mapError((cause) => new SettingsWriteError({ path, cause })));
3697
+ yield* fs.rename(tmpPath, path).pipe(Effect26.mapError((cause) => new SettingsWriteError({ path, cause })));
3685
3698
  });
3686
3699
  }
3687
3700
  function settingsFileExists(path) {
3688
- return Effect25.gen(function* () {
3689
- const fs = yield* FileSystem10.FileSystem;
3690
- return yield* fs.exists(path).pipe(Effect25.catchAll(() => Effect25.succeed(false)));
3701
+ return Effect26.gen(function* () {
3702
+ const fs = yield* FileSystem11.FileSystem;
3703
+ return yield* fs.exists(path).pipe(Effect26.catchAll(() => Effect26.succeed(false)));
3691
3704
  });
3692
3705
  }
3693
3706
  function deleteSettingsFile(path) {
3694
- return Effect25.gen(function* () {
3695
- const fs = yield* FileSystem10.FileSystem;
3696
- yield* fs.remove(path).pipe(Effect25.catchAll(() => Effect25.void));
3707
+ return Effect26.gen(function* () {
3708
+ const fs = yield* FileSystem11.FileSystem;
3709
+ yield* fs.remove(path).pipe(Effect26.catchAll(() => Effect26.void));
3697
3710
  });
3698
3711
  }
3699
3712
 
3700
3713
  // ../../packages/server/src/services/onboarding-service.ts
3701
3714
  function isFirstLaunch(settingsPath) {
3702
- return Effect26.gen(function* () {
3715
+ return Effect27.gen(function* () {
3703
3716
  const exists = yield* settingsFileExists(settingsPath);
3704
3717
  return { firstLaunch: !exists };
3705
3718
  });
3706
3719
  }
3707
3720
  function completeOnboarding(settingsPath) {
3708
- return Effect26.gen(function* () {
3721
+ return Effect27.gen(function* () {
3709
3722
  const { firstLaunch } = yield* isFirstLaunch(settingsPath);
3710
3723
  if (firstLaunch) {
3711
3724
  yield* saveSettings(settingsPath, getDefaultSettings());
@@ -3714,7 +3727,7 @@ function completeOnboarding(settingsPath) {
3714
3727
  });
3715
3728
  }
3716
3729
  function resetSettings(settingsPath) {
3717
- return Effect26.gen(function* () {
3730
+ return Effect27.gen(function* () {
3718
3731
  yield* deleteSettingsFile(settingsPath);
3719
3732
  return { ok: true };
3720
3733
  });
@@ -3722,60 +3735,81 @@ function resetSettings(settingsPath) {
3722
3735
 
3723
3736
  // ../../packages/server/src/services/sessions-service.ts
3724
3737
  init_src();
3725
- import { Effect as Effect27 } from "effect";
3738
+ import { Effect as Effect28 } from "effect";
3739
+ var DEFAULT_HEAD_SIZE = 100;
3726
3740
  function getProjects(registry) {
3727
- return Effect27.gen(function* () {
3741
+ return Effect28.gen(function* () {
3728
3742
  const projects = yield* registry.discoverAllProjects();
3729
3743
  return { projects };
3730
3744
  });
3731
3745
  }
3732
3746
  function getSessions(registry, params) {
3733
- return Effect27.gen(function* () {
3747
+ return Effect28.gen(function* () {
3734
3748
  const discovered = yield* registry.discoverAllProjectsWithSessions();
3735
- if (!discovered.projects.find((project) => project.encodedPath === params.encodedPath)) {
3749
+ if (!discovered.projects.some((project) => project.encodedPath === params.encodedPath)) {
3736
3750
  return { sessions: [] };
3737
3751
  }
3738
3752
  return { sessions: discovered.sessionsByEncodedPath.get(params.encodedPath) ?? [] };
3739
3753
  });
3740
3754
  }
3741
- function getSession(registry, params) {
3742
- return Effect27.gen(function* () {
3755
+ function loadSessionInternal(registry, params) {
3756
+ return Effect28.gen(function* () {
3743
3757
  const parsed = parseSessionId(params.sessionId);
3744
3758
  if (!(parsed.pluginId && parsed.rawSessionId)) {
3745
- return yield* Effect27.fail(new InvalidSessionIdError({ value: params.sessionId }));
3759
+ return yield* Effect28.fail(new InvalidSessionIdError({ value: params.sessionId }));
3746
3760
  }
3747
3761
  const { pluginId } = parsed;
3748
3762
  const { rawSessionId } = parsed;
3749
3763
  const projects = yield* registry.discoverAllProjects();
3750
3764
  const project = projects.find((p) => p.encodedPath === params.project);
3751
3765
  if (!project) {
3752
- return yield* Effect27.fail(new ProjectNotFoundError({ encodedPath: params.project }));
3766
+ return yield* Effect28.fail(new ProjectNotFoundError({ encodedPath: params.project }));
3753
3767
  }
3754
3768
  const source = project.sources.find((s) => s.pluginId === pluginId);
3755
3769
  if (!source) {
3756
- return yield* Effect27.fail(new PluginSourceNotFoundError({ pluginId, project: params.project }));
3770
+ return yield* Effect28.fail(new PluginSourceNotFoundError({ pluginId, project: params.project }));
3757
3771
  }
3758
3772
  const plugin = registry.getPlugin(pluginId);
3759
3773
  const pluginConfig = registry.getPluginConfig(pluginId);
3760
3774
  const configLayer = makePluginConfigLayer(pluginConfig);
3761
- const sessionDetail = plugin.loadSessionDetail ? yield* plugin.loadSessionDetail(source.nativeId, rawSessionId).pipe(Effect27.provide(configLayer), Effect27.catchAll(() => Effect27.succeed(undefined))) : undefined;
3762
- const session = sessionDetail?.session ?? (yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(Effect27.provide(configLayer), Effect27.catchAll(() => Effect27.die("loadSession failed"))));
3775
+ const sessionDetail = plugin.loadSessionDetail ? yield* plugin.loadSessionDetail(source.nativeId, rawSessionId).pipe(Effect28.provide(configLayer), Effect28.catchAll(() => Effect28.succeed(undefined))) : undefined;
3776
+ const session = sessionDetail?.session ?? (yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(Effect28.provide(configLayer), Effect28.catchAll(() => Effect28.die("loadSession failed"))));
3763
3777
  session.sessionId = encodeSessionId(pluginId, rawSessionId);
3764
3778
  session.pluginId = pluginId;
3765
3779
  session.planSessionId = sessionDetail?.planSessionId ? encodeSessionId(pluginId, sessionDetail.planSessionId) : undefined;
3766
3780
  session.implSessionId = sessionDetail?.implSessionId ? encodeSessionId(pluginId, sessionDetail.implSessionId) : undefined;
3767
- return { session };
3781
+ return { session, pluginId, rawSessionId };
3782
+ });
3783
+ }
3784
+ function getSession(registry, params) {
3785
+ return loadSessionInternal(registry, params).pipe(Effect28.map(({ session }) => ({ session })));
3786
+ }
3787
+ function getSessionHead(registry, params) {
3788
+ return Effect28.gen(function* () {
3789
+ const { session } = yield* loadSessionInternal(registry, params);
3790
+ const headSize = params.headSize ?? DEFAULT_HEAD_SIZE;
3791
+ const totalTurns = session.turns.length;
3792
+ const headSession = { ...session, turns: session.turns.slice(0, headSize) };
3793
+ return { session: headSession, totalTurns };
3794
+ });
3795
+ }
3796
+ function getSessionTail(registry, params) {
3797
+ return Effect28.gen(function* () {
3798
+ const { session } = yield* loadSessionInternal(registry, params);
3799
+ const fromTurn = Math.max(0, params.fromTurn);
3800
+ const totalTurns = session.turns.length;
3801
+ return { turns: fromTurn >= totalTurns ? [] : session.turns.slice(fromTurn) };
3768
3802
  });
3769
3803
  }
3770
3804
  function getSubAgent(registry, params) {
3771
- return Effect27.gen(function* () {
3805
+ return Effect28.gen(function* () {
3772
3806
  const parsed = parseSessionId(params.sessionId);
3773
3807
  if (!(parsed.pluginId && parsed.rawSessionId)) {
3774
- return yield* Effect27.fail(new InvalidSessionIdError({ value: params.sessionId }));
3808
+ return yield* Effect28.fail(new InvalidSessionIdError({ value: params.sessionId }));
3775
3809
  }
3776
3810
  const plugin = registry.getPlugin(parsed.pluginId);
3777
3811
  if (!plugin.loadSubAgentSession) {
3778
- return yield* Effect27.fail(new SubAgentNotSupportedError({ pluginId: parsed.pluginId }));
3812
+ return yield* Effect28.fail(new SubAgentNotSupportedError({ pluginId: parsed.pluginId }));
3779
3813
  }
3780
3814
  const pluginConfig = registry.getPluginConfig(parsed.pluginId);
3781
3815
  const configLayer = makePluginConfigLayer(pluginConfig);
@@ -3783,7 +3817,7 @@ function getSubAgent(registry, params) {
3783
3817
  sessionId: parsed.rawSessionId,
3784
3818
  project: params.project,
3785
3819
  agentId: params.agentId
3786
- }).pipe(Effect27.provide(configLayer), Effect27.catchAll(() => Effect27.die("loadSubAgentSession failed")));
3820
+ }).pipe(Effect28.provide(configLayer), Effect28.catchAll(() => Effect28.die("loadSubAgentSession failed")));
3787
3821
  return { session };
3788
3822
  });
3789
3823
  }
@@ -3792,7 +3826,7 @@ function projectNameFromPath(fullPath) {
3792
3826
  return parts.slice(-2).join("/");
3793
3827
  }
3794
3828
  function searchSessions(registry) {
3795
- return Effect27.gen(function* () {
3829
+ return Effect28.gen(function* () {
3796
3830
  const discovered = yield* registry.discoverAllProjectsWithSessions();
3797
3831
  const allSessions = [];
3798
3832
  for (const project of discovered.projects) {
@@ -3812,10 +3846,10 @@ function searchSessions(registry) {
3812
3846
  }
3813
3847
 
3814
3848
  // ../../packages/server/src/services/settings-service.ts
3815
- import { Effect as Effect28 } from "effect";
3849
+ import { Effect as Effect29 } from "effect";
3816
3850
  var DEFAULT_CHECK_INTERVAL_HOURS = 6;
3817
3851
  function buildPluginSettingsResponse(settingsPath) {
3818
- return Effect28.gen(function* () {
3852
+ return Effect29.gen(function* () {
3819
3853
  const settings = yield* loadSettings(settingsPath);
3820
3854
  const plugins = BUILTIN_PLUGIN_DESCRIPTORS.map(({ plugin, defaultDir, defaultEnabled }) => {
3821
3855
  const { id } = plugin;
@@ -3839,13 +3873,13 @@ function getPluginSettings(settingsPath) {
3839
3873
  return buildPluginSettingsResponse(settingsPath);
3840
3874
  }
3841
3875
  function getGeneralSettings(settingsPath) {
3842
- return Effect28.gen(function* () {
3876
+ return Effect29.gen(function* () {
3843
3877
  const settings = yield* loadSettings(settingsPath);
3844
3878
  return { showSecurityWarning: settings.general?.showSecurityWarning ?? true };
3845
3879
  });
3846
3880
  }
3847
3881
  function updateGeneralSettings(settingsPath, params) {
3848
- return Effect28.gen(function* () {
3882
+ return Effect29.gen(function* () {
3849
3883
  const settings = yield* loadSettings(settingsPath);
3850
3884
  if (!settings.general) {
3851
3885
  settings.general = {};
@@ -3858,9 +3892,9 @@ function updateGeneralSettings(settingsPath, params) {
3858
3892
  });
3859
3893
  }
3860
3894
  function updatePluginSetting(settingsPath, params) {
3861
- return Effect28.gen(function* () {
3895
+ return Effect29.gen(function* () {
3862
3896
  if (!BUILTIN_PLUGIN_ID_SET.has(params.pluginId)) {
3863
- return yield* Effect28.fail(new UnknownPluginError({ pluginId: params.pluginId }));
3897
+ return yield* Effect29.fail(new UnknownPluginError({ pluginId: params.pluginId }));
3864
3898
  }
3865
3899
  const settings = yield* loadSettings(settingsPath);
3866
3900
  const descriptor = BUILTIN_PLUGIN_DESCRIPTORS.find(({ plugin }) => plugin.id === params.pluginId);
@@ -3880,7 +3914,7 @@ function updatePluginSetting(settingsPath, params) {
3880
3914
  });
3881
3915
  }
3882
3916
  function getUpdateSettings(settingsPath) {
3883
- return Effect28.gen(function* () {
3917
+ return Effect29.gen(function* () {
3884
3918
  const settings = yield* loadSettings(settingsPath);
3885
3919
  return {
3886
3920
  channel: settings.updates?.channel ?? "stable",
@@ -3890,7 +3924,7 @@ function getUpdateSettings(settingsPath) {
3890
3924
  });
3891
3925
  }
3892
3926
  function updateUpdateSettings(settingsPath, params) {
3893
- return Effect28.gen(function* () {
3927
+ return Effect29.gen(function* () {
3894
3928
  const settings = yield* loadSettings(settingsPath);
3895
3929
  if (!settings.updates) {
3896
3930
  settings.updates = { channel: "stable", checkIntervalHours: 6, autoDownload: true };
@@ -3916,12 +3950,12 @@ function updateUpdateSettings(settingsPath, params) {
3916
3950
 
3917
3951
  // ../../packages/server/src/services/stats-service.ts
3918
3952
  import { dirname as dirname2, join as join14 } from "node:path";
3919
- import { FileSystem as FileSystem11 } from "@effect/platform";
3920
- import { Effect as Effect30 } from "effect";
3953
+ import { FileSystem as FileSystem12 } from "@effect/platform";
3954
+ import { Effect as Effect31 } from "effect";
3921
3955
 
3922
3956
  // ../../packages/server/src/services/stats.ts
3923
3957
  init_src();
3924
- import { Effect as Effect29 } from "effect";
3958
+ import { Effect as Effect30 } from "effect";
3925
3959
  function emptyStats(projects = 0) {
3926
3960
  return {
3927
3961
  projects,
@@ -3981,7 +4015,7 @@ function countVisibleMessages(turns) {
3981
4015
  return turns.filter((turn) => turn.kind !== "parse_error").length;
3982
4016
  }
3983
4017
  function collectSessionsWithProjects(registry, stats) {
3984
- return Effect29.gen(function* () {
4018
+ return Effect30.gen(function* () {
3985
4019
  const discovered = yield* registry.discoverAllProjectsWithSessions();
3986
4020
  stats.projects = discovered.projects.length;
3987
4021
  const sessionsWithProject = [];
@@ -4001,7 +4035,7 @@ function applyRecentSessionStats(stats, sessionsWithProject) {
4001
4035
  stats.thisWeekSessions = recent.thisWeekSessions;
4002
4036
  }
4003
4037
  function loadSessionForStats(registry, project, session) {
4004
- return Effect29.gen(function* () {
4038
+ return Effect30.gen(function* () {
4005
4039
  if (!session.pluginId) {
4006
4040
  return null;
4007
4041
  }
@@ -4013,7 +4047,7 @@ function loadSessionForStats(registry, project, session) {
4013
4047
  const pluginConfig = registry.getPluginConfig(session.pluginId);
4014
4048
  const { rawSessionId } = parseSessionId(session.sessionId);
4015
4049
  const configLayer = makePluginConfigLayer(pluginConfig);
4016
- const loaded = yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(Effect29.provide(configLayer), Effect29.catchAll(() => Effect29.succeed(null)));
4050
+ const loaded = yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(Effect30.provide(configLayer), Effect30.catchAll(() => Effect30.succeed(null)));
4017
4051
  return loaded?.turns ?? null;
4018
4052
  });
4019
4053
  }
@@ -4045,11 +4079,11 @@ function applyTurnStats(stats, turns, fallbackModel) {
4045
4079
  }
4046
4080
  }
4047
4081
  function computeStats(registry) {
4048
- return Effect29.gen(function* () {
4082
+ return Effect30.gen(function* () {
4049
4083
  const stats = emptyStats();
4050
4084
  const sessionsWithProject = yield* collectSessionsWithProjects(registry, stats);
4051
4085
  applyRecentSessionStats(stats, sessionsWithProject);
4052
- const turnsPerSession = yield* Effect29.forEach(sessionsWithProject, (item) => loadSessionForStats(registry, item.project, item.session).pipe(Effect29.map((turns) => ({ item, turns }))), { concurrency: "unbounded" });
4086
+ const turnsPerSession = yield* Effect30.forEach(sessionsWithProject, (item) => loadSessionForStats(registry, item.project, item.session).pipe(Effect30.map((turns) => ({ item, turns }))), { concurrency: "unbounded" });
4053
4087
  for (const { item, turns } of turnsPerSession) {
4054
4088
  if (!turns) {
4055
4089
  continue;
@@ -4120,27 +4154,27 @@ function isStatsCacheFile(value) {
4120
4154
  return candidate["version"] === 1 && typeof candidate["cachedAt"] === "string" && isDashboardStats(candidate["stats"]);
4121
4155
  }
4122
4156
  function loadStatsCache(settingsPath) {
4123
- return Effect30.gen(function* () {
4124
- const fs = yield* FileSystem11.FileSystem;
4157
+ return Effect31.gen(function* () {
4158
+ const fs = yield* FileSystem12.FileSystem;
4125
4159
  const cachePath = getStatsCachePath(settingsPath);
4126
- const raw = yield* fs.readFileString(cachePath).pipe(Effect30.catchAll(() => Effect30.succeed(null)));
4160
+ const raw = yield* fs.readFileString(cachePath).pipe(Effect31.catchAll(() => Effect31.succeed(null)));
4127
4161
  if (raw === null) {
4128
4162
  return null;
4129
4163
  }
4130
- const parsed = yield* Effect30.try({
4164
+ const parsed = yield* Effect31.try({
4131
4165
  try: () => JSON.parse(raw),
4132
4166
  catch: () => null
4133
- }).pipe(Effect30.catchAll(() => Effect30.succeed(null)));
4167
+ }).pipe(Effect31.catchAll(() => Effect31.succeed(null)));
4134
4168
  return parsed !== null && isStatsCacheFile(parsed) ? parsed : null;
4135
4169
  });
4136
4170
  }
4137
4171
  function persistStatsCache(settingsPath, stats, expectedEpoch) {
4138
- return Effect30.gen(function* () {
4139
- const fs = yield* FileSystem11.FileSystem;
4172
+ return Effect31.gen(function* () {
4173
+ const fs = yield* FileSystem12.FileSystem;
4140
4174
  const cachePath = getStatsCachePath(settingsPath);
4141
4175
  const cacheDir = dirname2(cachePath);
4142
4176
  const cachedAt = new Date().toISOString();
4143
- yield* fs.makeDirectory(cacheDir, { recursive: true }).pipe(Effect30.catchAll(() => Effect30.void));
4177
+ yield* fs.makeDirectory(cacheDir, { recursive: true }).pipe(Effect31.catchAll(() => Effect31.void));
4144
4178
  if (getRefreshEpoch(cachePath) !== expectedEpoch) {
4145
4179
  return null;
4146
4180
  }
@@ -4150,15 +4184,15 @@ function persistStatsCache(settingsPath, stats, expectedEpoch) {
4150
4184
  cachedAt,
4151
4185
  stats
4152
4186
  };
4153
- const wroteTempFile = yield* fs.writeFileString(tmpPath, JSON.stringify(payload, null, 2)).pipe(Effect30.map(() => true), Effect30.catchAll(() => Effect30.succeed(false)));
4187
+ const wroteTempFile = yield* fs.writeFileString(tmpPath, JSON.stringify(payload, null, 2)).pipe(Effect31.map(() => true), Effect31.catchAll(() => Effect31.succeed(false)));
4154
4188
  if (!wroteTempFile) {
4155
4189
  return null;
4156
4190
  }
4157
4191
  if (getRefreshEpoch(cachePath) !== expectedEpoch) {
4158
- yield* fs.remove(tmpPath).pipe(Effect30.catchAll(() => Effect30.void));
4192
+ yield* fs.remove(tmpPath).pipe(Effect31.catchAll(() => Effect31.void));
4159
4193
  return null;
4160
4194
  }
4161
- const renamed = yield* fs.rename(tmpPath, cachePath).pipe(Effect30.map(() => true), Effect30.catchAll(() => fs.remove(tmpPath).pipe(Effect30.catchAll(() => Effect30.void), Effect30.map(() => false))));
4195
+ const renamed = yield* fs.rename(tmpPath, cachePath).pipe(Effect31.map(() => true), Effect31.catchAll(() => fs.remove(tmpPath).pipe(Effect31.catchAll(() => Effect31.void), Effect31.map(() => false))));
4162
4196
  if (!renamed) {
4163
4197
  return null;
4164
4198
  }
@@ -4166,38 +4200,38 @@ function persistStatsCache(settingsPath, stats, expectedEpoch) {
4166
4200
  });
4167
4201
  }
4168
4202
  function computeAndPersistStats(settingsPath, registry, expectedEpoch) {
4169
- return Effect30.gen(function* () {
4203
+ return Effect31.gen(function* () {
4170
4204
  const stats = yield* scanStats(registry);
4171
4205
  const cachedAt = yield* persistStatsCache(settingsPath, stats, expectedEpoch);
4172
4206
  return { stats, ...cachedAt ? { cachedAt } : {} };
4173
4207
  });
4174
4208
  }
4175
4209
  function refreshStats(settingsPath, registry) {
4176
- return Effect30.gen(function* () {
4210
+ return Effect31.gen(function* () {
4177
4211
  const cachePath = getStatsCachePath(settingsPath);
4178
4212
  const expectedEpoch = getRefreshEpoch(cachePath);
4179
4213
  refreshingCachePaths.add(cachePath);
4180
4214
  const result = yield* computeAndPersistStats(settingsPath, registry, expectedEpoch);
4181
4215
  return { ...result, refreshing: false };
4182
- }).pipe(Effect30.ensuring(Effect30.sync(() => {
4216
+ }).pipe(Effect31.ensuring(Effect31.sync(() => {
4183
4217
  refreshingCachePaths.delete(getStatsCachePath(settingsPath));
4184
4218
  })));
4185
4219
  }
4186
4220
  function scheduleRefresh(settingsPath, registry) {
4187
- return Effect30.gen(function* () {
4221
+ return Effect31.gen(function* () {
4188
4222
  const cachePath = getStatsCachePath(settingsPath);
4189
4223
  if (refreshingCachePaths.has(cachePath)) {
4190
4224
  return;
4191
4225
  }
4192
4226
  const expectedEpoch = getRefreshEpoch(cachePath);
4193
4227
  refreshingCachePaths.add(cachePath);
4194
- yield* computeAndPersistStats(settingsPath, registry, expectedEpoch).pipe(Effect30.catchAllCause(() => Effect30.void), Effect30.ensuring(Effect30.sync(() => {
4228
+ yield* computeAndPersistStats(settingsPath, registry, expectedEpoch).pipe(Effect31.catchAllCause(() => Effect31.void), Effect31.ensuring(Effect31.sync(() => {
4195
4229
  refreshingCachePaths.delete(cachePath);
4196
- })), Effect30.forkDaemon);
4230
+ })), Effect31.forkDaemon);
4197
4231
  });
4198
4232
  }
4199
4233
  function getStats(settingsPath, registry) {
4200
- return Effect30.gen(function* () {
4234
+ return Effect31.gen(function* () {
4201
4235
  const cachePath = getStatsCachePath(settingsPath);
4202
4236
  const cached = yield* loadStatsCache(settingsPath);
4203
4237
  if (!cached) {
@@ -4216,11 +4250,11 @@ function getStats(settingsPath, registry) {
4216
4250
  });
4217
4251
  }
4218
4252
  function invalidateStatsCache(settingsPath) {
4219
- return Effect30.gen(function* () {
4220
- const fs = yield* FileSystem11.FileSystem;
4253
+ return Effect31.gen(function* () {
4254
+ const fs = yield* FileSystem12.FileSystem;
4221
4255
  const cachePath = getStatsCachePath(settingsPath);
4222
4256
  bumpRefreshEpoch(cachePath);
4223
- yield* fs.remove(cachePath).pipe(Effect30.catchAll(() => Effect30.void));
4257
+ yield* fs.remove(cachePath).pipe(Effect31.catchAll(() => Effect31.void));
4224
4258
  });
4225
4259
  }
4226
4260
 
@@ -4236,27 +4270,29 @@ function getVersion(state) {
4236
4270
  // ../../packages/server/src/effect/server-services.ts
4237
4271
  class KloviServices extends Context4.Tag("@klovi/KloviServices")() {
4238
4272
  }
4239
- var KloviServicesLive = Layer6.effect(KloviServices, Effect31.gen(function* () {
4273
+ var KloviServicesLive = Layer6.effect(KloviServices, Effect32.gen(function* () {
4240
4274
  const config = yield* ServerConfig;
4241
4275
  const { settingsPath } = config;
4242
4276
  const versionState = makeVersionState(config.version, config.commit);
4243
4277
  const settings = yield* loadSettings(settingsPath);
4244
4278
  let registry = yield* createRegistry(settings);
4245
- const refreshRegistry = () => Effect31.gen(function* () {
4279
+ const refreshRegistry = () => Effect32.gen(function* () {
4246
4280
  const freshSettings = yield* loadSettings(settingsPath);
4247
4281
  registry = yield* createRegistry(freshSettings);
4248
4282
  });
4249
4283
  return {
4250
4284
  acceptRisks: () => completeOnboarding(settingsPath),
4251
- getVersion: () => Effect31.succeed(getVersion(versionState)),
4285
+ getVersion: () => Effect32.succeed(getVersion(versionState)),
4252
4286
  getStats: () => getStats(settingsPath, registry),
4253
4287
  getProjects: () => getProjects(registry),
4254
4288
  getSessions: (params) => getSessions(registry, params),
4255
4289
  getSession: (params) => getSession(registry, params),
4290
+ getSessionHead: (params) => getSessionHead(registry, params),
4291
+ getSessionTail: (params) => getSessionTail(registry, params),
4256
4292
  getSubAgent: (params) => getSubAgent(registry, params),
4257
4293
  searchSessions: () => searchSessions(registry),
4258
4294
  getPluginSettings: () => getPluginSettings(settingsPath),
4259
- updatePluginSetting: (params) => Effect31.gen(function* () {
4295
+ updatePluginSetting: (params) => Effect32.gen(function* () {
4260
4296
  const result = yield* updatePluginSetting(settingsPath, params);
4261
4297
  yield* refreshRegistry();
4262
4298
  yield* invalidateStatsCache(settingsPath);
@@ -4265,7 +4301,7 @@ var KloviServicesLive = Layer6.effect(KloviServices, Effect31.gen(function* () {
4265
4301
  getGeneralSettings: () => getGeneralSettings(settingsPath),
4266
4302
  updateGeneralSettings: (params) => updateGeneralSettings(settingsPath, params),
4267
4303
  isFirstLaunch: () => isFirstLaunch(settingsPath),
4268
- resetSettings: () => Effect31.gen(function* () {
4304
+ resetSettings: () => Effect32.gen(function* () {
4269
4305
  const result = yield* resetSettings(settingsPath);
4270
4306
  yield* refreshRegistry();
4271
4307
  yield* invalidateStatsCache(settingsPath);
@@ -4317,28 +4353,28 @@ async function bootstrapServer(options, makeServe) {
4317
4353
  resolveAddress = resolve;
4318
4354
  rejectAddress = reject;
4319
4355
  });
4320
- const addressCapture = Layer8.effectDiscard(HttpServer.addressWith((address) => Effect32.gen(function* () {
4356
+ const addressCapture = Layer8.effectDiscard(HttpServer.addressWith((address) => Effect33.gen(function* () {
4321
4357
  const addr = address;
4322
4358
  const url2 = `http://${addr.hostname}:${addr.port}`;
4323
- yield* Effect32.log(`Klovi server listening on ${url2}`);
4359
+ yield* Effect33.log(`Klovi server listening on ${url2}`);
4324
4360
  resolveAddress(url2);
4325
4361
  })));
4326
4362
  const serveLayer = makeServe();
4327
4363
  const fullLayer = Layer8.merge(serveLayer, addressCapture).pipe(Layer8.provide(servicesLayer), Layer8.provide(configLayer), Layer8.provide(platformLayer));
4328
- const fiber = Effect32.runFork(Layer8.launch(fullLayer));
4329
- Effect32.runFork(Fiber.join(fiber).pipe(Effect32.catchAllCause((cause) => Effect32.sync(() => rejectAddress(Cause.squash(cause))))));
4364
+ const fiber = Effect33.runFork(Layer8.launch(fullLayer));
4365
+ Effect33.runFork(Fiber.join(fiber).pipe(Effect33.catchAllCause((cause) => Effect33.sync(() => rejectAddress(Cause.squash(cause))))));
4330
4366
  const url = await addressPromise;
4331
4367
  return {
4332
4368
  url,
4333
4369
  stop: () => {
4334
- Effect32.runFork(Fiber.interrupt(fiber));
4370
+ Effect33.runFork(Fiber.interrupt(fiber));
4335
4371
  }
4336
4372
  };
4337
4373
  }
4338
4374
 
4339
4375
  // ../../packages/server/src/effect/http-app.ts
4340
4376
  import { HttpRouter, HttpServer as HttpServer2, HttpServerRequest, HttpServerResponse } from "@effect/platform";
4341
- import { Effect as Effect33 } from "effect";
4377
+ import { Effect as Effect34 } from "effect";
4342
4378
 
4343
4379
  // ../../packages/server/src/rpc-error.ts
4344
4380
  class RPCError extends Error {
@@ -4379,7 +4415,7 @@ function mapDomainErrorToStatus(err) {
4379
4415
  const message = err instanceof Error ? err.message : "Internal server error";
4380
4416
  return { status: 500, message };
4381
4417
  }
4382
- var rpcHandler = Effect33.gen(function* () {
4418
+ var rpcHandler = Effect34.gen(function* () {
4383
4419
  const services = yield* KloviServices;
4384
4420
  const routeParams = yield* HttpRouter.params;
4385
4421
  const req = yield* HttpServerRequest.HttpServerRequest;
@@ -4389,7 +4425,7 @@ var rpcHandler = Effect33.gen(function* () {
4389
4425
  }
4390
4426
  if (!isRpcMethod(method, services)) {
4391
4427
  const httpNotFound = 404;
4392
- return yield* Effect33.fail(new RPCError(httpNotFound, `Unknown method: ${method}`));
4428
+ return yield* Effect34.fail(new RPCError(httpNotFound, `Unknown method: ${method}`));
4393
4429
  }
4394
4430
  let params = {};
4395
4431
  const bodyText = yield* req.text;
@@ -4403,39 +4439,39 @@ var rpcHandler = Effect33.gen(function* () {
4403
4439
  const handler = services[method];
4404
4440
  const value = yield* handler(params);
4405
4441
  return HttpServerResponse.unsafeJson(value);
4406
- }).pipe(Effect33.catchAll((err) => {
4442
+ }).pipe(Effect34.catchAll((err) => {
4407
4443
  const { status, message } = mapDomainErrorToStatus(err);
4408
- return Effect33.succeed(HttpServerResponse.unsafeJson({ error: message }, { status }));
4444
+ return Effect34.succeed(HttpServerResponse.unsafeJson({ error: message }, { status }));
4409
4445
  }));
4410
- var emptyMethodHandler = Effect33.succeed(HttpServerResponse.unsafeJson({ error: "Method name required" }, { status: 400 }));
4446
+ var emptyMethodHandler = Effect34.succeed(HttpServerResponse.unsafeJson({ error: "Method name required" }, { status: 400 }));
4411
4447
  var makeRpcRouter = () => HttpRouter.empty.pipe(HttpRouter.post("/api/rpc/", emptyMethodHandler), HttpRouter.post("/api/rpc/:method", rpcHandler));
4412
- var notFoundHandler = Effect33.succeed(HttpServerResponse.unsafeJson({ error: "Not found" }, { status: 404 }));
4413
- var makeHttpApp = () => makeRpcRouter().pipe(Effect33.catchTag("RouteNotFound", () => notFoundHandler));
4448
+ var notFoundHandler = Effect34.succeed(HttpServerResponse.unsafeJson({ error: "Not found" }, { status: 404 }));
4449
+ var makeHttpApp = () => makeRpcRouter().pipe(Effect34.catchTag("RouteNotFound", () => notFoundHandler));
4414
4450
  var makeServeLayer = () => makeHttpApp().pipe(HttpServer2.serve());
4415
4451
 
4416
4452
  // src/http-app.ts
4417
4453
  import { HttpServer as HttpServer3 } from "@effect/platform";
4418
- import { Effect as Effect35 } from "effect";
4454
+ import { Effect as Effect36 } from "effect";
4419
4455
 
4420
4456
  // src/static-handler.ts
4421
4457
  import { HttpServerRequest as HttpServerRequest2, HttpServerResponse as HttpServerResponse2 } from "@effect/platform";
4422
- import { Effect as Effect34 } from "effect";
4458
+ import { Effect as Effect35 } from "effect";
4423
4459
  var notFound = HttpServerResponse2.unsafeJson({ error: "Not found" }, { status: 404 });
4424
4460
  var isNavigationRequest = (pathname) => {
4425
4461
  const lastSegment2 = pathname.split("/").pop() ?? "";
4426
4462
  return !lastSegment2.includes(".");
4427
4463
  };
4428
- var makeStaticHandler = (staticDir) => Effect34.gen(function* () {
4464
+ var makeStaticHandler = (staticDir) => Effect35.gen(function* () {
4429
4465
  const req = yield* HttpServerRequest2.HttpServerRequest;
4430
4466
  const url = new URL(req.url, "http://localhost");
4431
4467
  const filePath = url.pathname === "/" ? "/index.html" : url.pathname;
4432
- return yield* HttpServerResponse2.file(`${staticDir}${filePath}`).pipe(Effect34.orElse(() => isNavigationRequest(url.pathname) ? HttpServerResponse2.file(`${staticDir}/index.html`).pipe(Effect34.orElse(() => Effect34.succeed(notFound))) : Effect34.succeed(notFound)));
4468
+ return yield* HttpServerResponse2.file(`${staticDir}${filePath}`).pipe(Effect35.orElse(() => isNavigationRequest(url.pathname) ? HttpServerResponse2.file(`${staticDir}/index.html`).pipe(Effect35.orElse(() => Effect35.succeed(notFound))) : Effect35.succeed(notFound)));
4433
4469
  });
4434
4470
 
4435
4471
  // src/http-app.ts
4436
4472
  var makePackageHttpApp = (staticDir) => {
4437
4473
  const router = makeRpcRouter();
4438
- return router.pipe(Effect35.catchTag("RouteNotFound", () => makeStaticHandler(staticDir)));
4474
+ return router.pipe(Effect36.catchTag("RouteNotFound", () => makeStaticHandler(staticDir)));
4439
4475
  };
4440
4476
  var makePackageServeLayer = (staticDir) => {
4441
4477
  if (!staticDir) {