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