wave-agent-sdk 0.18.7 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/builtin/skills/settings/ENV.md +0 -1
  2. package/builtin/skills/settings/HOOKS.md +10 -0
  3. package/builtin/skills/settings/MODELS.md +55 -10
  4. package/builtin/skills/settings/SKILL.md +8 -4
  5. package/builtin/skills/settings/SKILLS.md +6 -4
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +12 -5
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/managers/aiManager.d.ts.map +1 -1
  12. package/dist/managers/aiManager.js +20 -4
  13. package/dist/managers/mcpManager.d.ts.map +1 -1
  14. package/dist/managers/mcpManager.js +7 -2
  15. package/dist/managers/messageManager.d.ts +3 -5
  16. package/dist/managers/messageManager.d.ts.map +1 -1
  17. package/dist/managers/messageManager.js +17 -77
  18. package/dist/managers/skillManager.d.ts +1 -0
  19. package/dist/managers/skillManager.d.ts.map +1 -1
  20. package/dist/managers/skillManager.js +11 -2
  21. package/dist/prompts/index.d.ts +3 -2
  22. package/dist/prompts/index.d.ts.map +1 -1
  23. package/dist/prompts/index.js +5 -1
  24. package/dist/services/aiService.d.ts.map +1 -1
  25. package/dist/services/aiService.js +16 -47
  26. package/dist/services/configurationService.d.ts.map +1 -1
  27. package/dist/services/configurationService.js +25 -19
  28. package/dist/services/hook.d.ts.map +1 -1
  29. package/dist/services/hook.js +6 -0
  30. package/dist/services/initializationService.d.ts.map +1 -1
  31. package/dist/services/initializationService.js +1 -1
  32. package/dist/services/interactionService.js +1 -1
  33. package/dist/services/reversionService.d.ts +5 -1
  34. package/dist/services/reversionService.d.ts.map +1 -1
  35. package/dist/services/reversionService.js +50 -22
  36. package/dist/services/session.d.ts +4 -13
  37. package/dist/services/session.d.ts.map +1 -1
  38. package/dist/services/session.js +59 -215
  39. package/dist/telemetry/sessionTracing.d.ts +14 -4
  40. package/dist/telemetry/sessionTracing.d.ts.map +1 -1
  41. package/dist/telemetry/sessionTracing.js +134 -8
  42. package/dist/tools/bashTool.d.ts.map +1 -1
  43. package/dist/tools/bashTool.js +16 -1
  44. package/dist/types/config.d.ts +11 -2
  45. package/dist/types/config.d.ts.map +1 -1
  46. package/dist/types/hooks.d.ts +1 -0
  47. package/dist/types/hooks.d.ts.map +1 -1
  48. package/dist/types/messaging.d.ts +0 -1
  49. package/dist/types/messaging.d.ts.map +1 -1
  50. package/dist/types/skills.d.ts +1 -0
  51. package/dist/types/skills.d.ts.map +1 -1
  52. package/dist/types/telemetry.d.ts +14 -1
  53. package/dist/types/telemetry.d.ts.map +1 -1
  54. package/dist/utils/atomicWrite.d.ts +15 -0
  55. package/dist/utils/atomicWrite.d.ts.map +1 -0
  56. package/dist/utils/atomicWrite.js +59 -0
  57. package/dist/utils/cacheControlUtils.d.ts +3 -15
  58. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  59. package/dist/utils/cacheControlUtils.js +4 -35
  60. package/dist/utils/containerSetup.d.ts.map +1 -1
  61. package/dist/utils/containerSetup.js +5 -1
  62. package/dist/utils/convertMessagesForAPI.d.ts +9 -1
  63. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  64. package/dist/utils/convertMessagesForAPI.js +74 -55
  65. package/dist/utils/mcpUtils.d.ts.map +1 -1
  66. package/dist/utils/mcpUtils.js +2 -1
  67. package/dist/utils/modelCapabilities.d.ts +22 -0
  68. package/dist/utils/modelCapabilities.d.ts.map +1 -0
  69. package/dist/utils/modelCapabilities.js +24 -0
  70. package/dist/utils/skillParser.d.ts.map +1 -1
  71. package/dist/utils/skillParser.js +3 -1
  72. package/package.json +1 -1
  73. package/src/agent.ts +11 -5
  74. package/src/index.ts +1 -0
  75. package/src/managers/aiManager.ts +22 -2
  76. package/src/managers/mcpManager.ts +8 -2
  77. package/src/managers/messageManager.ts +22 -104
  78. package/src/managers/skillManager.ts +16 -2
  79. package/src/prompts/index.ts +6 -1
  80. package/src/services/aiService.ts +15 -85
  81. package/src/services/configurationService.ts +27 -41
  82. package/src/services/hook.ts +7 -0
  83. package/src/services/initializationService.ts +1 -3
  84. package/src/services/interactionService.ts +1 -1
  85. package/src/services/reversionService.ts +50 -27
  86. package/src/services/session.ts +63 -262
  87. package/src/telemetry/sessionTracing.ts +167 -12
  88. package/src/tools/bashTool.ts +23 -1
  89. package/src/types/config.ts +12 -2
  90. package/src/types/hooks.ts +1 -0
  91. package/src/types/messaging.ts +0 -1
  92. package/src/types/skills.ts +1 -0
  93. package/src/types/telemetry.ts +17 -1
  94. package/src/utils/atomicWrite.ts +61 -0
  95. package/src/utils/cacheControlUtils.ts +5 -38
  96. package/src/utils/containerSetup.ts +5 -1
  97. package/src/utils/convertMessagesForAPI.ts +87 -57
  98. package/src/utils/mcpUtils.ts +2 -1
  99. package/src/utils/modelCapabilities.ts +30 -0
  100. package/src/utils/skillParser.ts +3 -1
@@ -17,8 +17,6 @@
17
17
  import type { Message } from "../types/index.js";
18
18
  export interface SessionData {
19
19
  id: string;
20
- rootSessionId?: string;
21
- parentSessionId?: string;
22
20
  messages: Message[];
23
21
  metadata: {
24
22
  workdir: string;
@@ -28,8 +26,6 @@ export interface SessionData {
28
26
  }
29
27
  export interface SessionMetadata {
30
28
  id: string;
31
- rootSessionId?: string;
32
- parentSessionId?: string;
33
29
  sessionType: "main" | "subagent";
34
30
  subagentType?: string;
35
31
  workdir: string;
@@ -38,13 +34,6 @@ export interface SessionMetadata {
38
34
  latestTotalTokens: number;
39
35
  firstMessage?: string;
40
36
  }
41
- export interface SessionIndex {
42
- sessions: Record<string, Omit<SessionMetadata, "id" | "lastActiveAt" | "createdAt"> & {
43
- lastActiveAt: string;
44
- createdAt: string;
45
- }>;
46
- lastUpdated: string;
47
- }
48
37
  /**
49
38
  * Generate a new session ID using Node.js native crypto.randomUUID()
50
39
  * @returns UUID string for session identification
@@ -92,7 +81,7 @@ export declare function createSession(sessionId: string, workdir: string, sessio
92
81
  * @param workdir - Working directory for the session
93
82
  * @param sessionType - Type of session ("main" or "subagent", defaults to "main")
94
83
  */
95
- export declare function appendMessages(sessionId: string, newMessages: Message[], workdir: string, sessionType?: "main" | "subagent", rootSessionId?: string, parentSessionId?: string): Promise<void>;
84
+ export declare function appendMessages(sessionId: string, newMessages: Message[], workdir: string, sessionType?: "main" | "subagent"): Promise<void>;
96
85
  /**
97
86
  * Load session data from JSONL file (new approach)
98
87
  *
@@ -193,7 +182,9 @@ export declare function truncateContent(content: string, maxLength?: number): st
193
182
  */
194
183
  export declare function handleSessionRestoration(restoreSessionId?: string, continueLastSession?: boolean, workdir?: string): Promise<SessionData | undefined>;
195
184
  /**
196
- * Load the full message thread by following parentSessionId links
185
+ * Load the full message thread for a session.
186
+ * With append-only compaction, all messages are in a single file.
187
+ * Returns the active messages (post-compact boundary).
197
188
  * @param currentSessionId - The ID of the current session
198
189
  * @param workdir - Working directory for the session
199
190
  * @returns Promise that resolves to an array of all messages in the thread
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/services/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/services/session.ts"],"names":[],"mappings":"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"}
@@ -41,34 +41,6 @@ export function generateSubagentFilename(sessionId) {
41
41
  // Constants
42
42
  export const SESSION_DIR = join(homedir(), ".wave", "projects");
43
43
  const MAX_SESSION_AGE_DAYS = 14;
44
- const SESSION_INDEX_FILENAME = "sessions-index.json";
45
- /**
46
- * Update the session index for a project directory
47
- */
48
- async function updateSessionIndex(projectDirPath, metadata) {
49
- const indexPath = join(projectDirPath, SESSION_INDEX_FILENAME);
50
- let index = {
51
- sessions: {},
52
- lastUpdated: new Date().toISOString(),
53
- };
54
- try {
55
- const content = await fs.readFile(indexPath, "utf8");
56
- index = JSON.parse(content);
57
- }
58
- catch {
59
- // Index doesn't exist or is invalid, start fresh
60
- }
61
- const { id, ...rest } = metadata;
62
- index.sessions[id] = {
63
- ...rest,
64
- createdAt: metadata.createdAt.toISOString(),
65
- lastActiveAt: metadata.lastActiveAt.toISOString(),
66
- firstMessage: metadata.firstMessage || index.sessions[id]?.firstMessage,
67
- parentSessionId: metadata.parentSessionId,
68
- };
69
- index.lastUpdated = new Date().toISOString();
70
- await fs.writeFile(indexPath, JSON.stringify(index, null, 2), "utf8");
71
- }
72
44
  /**
73
45
  * Ensure session directory exists
74
46
  */
@@ -129,7 +101,7 @@ export async function createSession(sessionId, workdir, sessionType = "main") {
129
101
  * @param workdir - Working directory for the session
130
102
  * @param sessionType - Type of session ("main" or "subagent", defaults to "main")
131
103
  */
132
- export async function appendMessages(sessionId, newMessages, workdir, sessionType = "main", rootSessionId, parentSessionId) {
104
+ export async function appendMessages(sessionId, newMessages, workdir, sessionType = "main") {
133
105
  // Do not save session files in test environment
134
106
  if (process.env.NODE_ENV === "test") {
135
107
  return;
@@ -151,47 +123,6 @@ export async function appendMessages(sessionId, newMessages, workdir, sessionTyp
151
123
  await jsonlHandler.append(filePath, newMessages, {
152
124
  atomic: false,
153
125
  });
154
- // Update index
155
- const encoder = new PathEncoder();
156
- const projectDir = await encoder.getProjectDirectory(workdir, SESSION_DIR);
157
- const lastMessage = newMessages[newMessages.length - 1];
158
- // Get first message content and createdAt from existing index
159
- let firstMessage;
160
- let createdAt;
161
- try {
162
- const indexPath = join(projectDir.encodedPath, SESSION_INDEX_FILENAME);
163
- const content = await fs.readFile(indexPath, "utf8");
164
- const index = JSON.parse(content);
165
- if (!index.sessions[sessionId]?.firstMessage) {
166
- firstMessage =
167
- (await getFirstMessageContent(sessionId, workdir)) || undefined;
168
- }
169
- if (index.sessions[sessionId]?.createdAt) {
170
- createdAt = new Date(index.sessions[sessionId].createdAt);
171
- }
172
- }
173
- catch {
174
- // If index doesn't exist, this might be the first message
175
- firstMessage =
176
- (await getFirstMessageContent(sessionId, workdir)) || undefined;
177
- }
178
- // Derive createdAt from session ID if not in index
179
- if (!createdAt) {
180
- createdAt = new Date();
181
- }
182
- await updateSessionIndex(projectDir.encodedPath, {
183
- id: sessionId,
184
- rootSessionId,
185
- parentSessionId,
186
- sessionType,
187
- workdir,
188
- createdAt,
189
- lastActiveAt: new Date(lastMessage.timestamp),
190
- latestTotalTokens: lastMessage.usage
191
- ? extractLatestTotalTokens([lastMessage])
192
- : 0,
193
- firstMessage,
194
- });
195
126
  }
196
127
  /**
197
128
  * Load session data from JSONL file (new approach)
@@ -216,28 +147,20 @@ export async function loadSessionFromJsonl(sessionId, workdir, sessionType = "ma
216
147
  }
217
148
  throw error;
218
149
  }
219
- const messages = await jsonlHandler.read(filePath);
150
+ const allMessages = await jsonlHandler.read(filePath);
151
+ // Find the last compact boundary — only return messages from there forward
152
+ let lastCompactIndex = -1;
153
+ for (let i = allMessages.length - 1; i >= 0; i--) {
154
+ if (allMessages[i].blocks?.some((b) => b.type === "compact")) {
155
+ lastCompactIndex = i;
156
+ break;
157
+ }
158
+ }
159
+ const messages = lastCompactIndex >= 0 ? allMessages.slice(lastCompactIndex) : allMessages;
220
160
  // Extract metadata from messages
221
161
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
222
- // Try to get rootSessionId and parentSessionId from index
223
- let rootSessionId;
224
- let parentSessionId;
225
- try {
226
- const encoder = new PathEncoder();
227
- const projectDir = await encoder.getProjectDirectory(workdir, SESSION_DIR);
228
- const indexPath = join(projectDir.encodedPath, SESSION_INDEX_FILENAME);
229
- const indexContent = await fs.readFile(indexPath, "utf8");
230
- const index = JSON.parse(indexContent);
231
- rootSessionId = index.sessions[sessionId]?.rootSessionId;
232
- parentSessionId = index.sessions[sessionId]?.parentSessionId;
233
- }
234
- catch {
235
- // Ignore index errors
236
- }
237
162
  const sessionData = {
238
163
  id: sessionId,
239
- rootSessionId: rootSessionId || sessionId,
240
- parentSessionId,
241
164
  messages,
242
165
  metadata: {
243
166
  workdir,
@@ -311,29 +234,8 @@ export async function listSessionsFromJsonl(workdir) {
311
234
  const encoder = new PathEncoder();
312
235
  const baseDir = SESSION_DIR;
313
236
  const projectDir = await encoder.getProjectDirectory(workdir, baseDir);
314
- // Try to read from index first
315
- const indexPath = join(projectDir.encodedPath, SESSION_INDEX_FILENAME);
316
237
  const sevenDaysAgo = new Date();
317
238
  sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
318
- try {
319
- const indexContent = await fs.readFile(indexPath, "utf8");
320
- const index = JSON.parse(indexContent);
321
- const sessions = Object.entries(index.sessions)
322
- .filter(([, meta]) => {
323
- const lastActiveAt = new Date(meta.lastActiveAt);
324
- return meta.sessionType === "main" && lastActiveAt >= sevenDaysAgo;
325
- })
326
- .map(([id, meta]) => ({
327
- id,
328
- ...meta,
329
- createdAt: new Date(meta.createdAt),
330
- lastActiveAt: new Date(meta.lastActiveAt),
331
- }));
332
- return sessions.sort((a, b) => b.lastActiveAt.getTime() - a.lastActiveAt.getTime());
333
- }
334
- catch {
335
- // Fallback to manual listing if index fails
336
- }
337
239
  let files;
338
240
  try {
339
241
  files = await fs.readdir(projectDir.encodedPath);
@@ -382,7 +284,7 @@ export async function listSessionsFromJsonl(workdir) {
382
284
  const sessionMeta = {
383
285
  id: sessionId,
384
286
  sessionType: "main",
385
- subagentType: undefined, // No longer stored in metadata
287
+ subagentType: undefined,
386
288
  workdir: projectDir.originalPath,
387
289
  createdAt: new Date(),
388
290
  lastActiveAt,
@@ -390,7 +292,7 @@ export async function listSessionsFromJsonl(workdir) {
390
292
  ? extractLatestTotalTokens([lastMessage])
391
293
  : 0,
392
294
  };
393
- // Try to get first message content for the fallback/rebuild case
295
+ // Try to get first message content for display
394
296
  try {
395
297
  const firstContent = await getFirstMessageContent(sessionId, workdir);
396
298
  if (firstContent) {
@@ -408,27 +310,7 @@ export async function listSessionsFromJsonl(workdir) {
408
310
  }
409
311
  }
410
312
  // Sort by last active time (most recently active first)
411
- const sortedSessions = sessions.sort((a, b) => b.lastActiveAt.getTime() - a.lastActiveAt.getTime());
412
- // Rebuild index if we had to fall back
413
- try {
414
- const index = {
415
- sessions: {},
416
- lastUpdated: new Date().toISOString(),
417
- };
418
- for (const session of sessions) {
419
- const { id, ...rest } = session;
420
- index.sessions[id] = {
421
- ...rest,
422
- createdAt: session.createdAt.toISOString(),
423
- lastActiveAt: session.lastActiveAt.toISOString(),
424
- };
425
- }
426
- await fs.writeFile(indexPath, JSON.stringify(index, null, 2), "utf8");
427
- }
428
- catch (error) {
429
- logger.warn(`Failed to rebuild session index for ${workdir}:`, error);
430
- }
431
- return sortedSessions;
313
+ return sessions.sort((a, b) => b.lastActiveAt.getTime() - a.lastActiveAt.getTime());
432
314
  }
433
315
  catch (error) {
434
316
  throw new Error(`Failed to list sessions: ${error}`);
@@ -462,30 +344,49 @@ export async function listAllSessions() {
462
344
  if (!stat.isDirectory()) {
463
345
  continue;
464
346
  }
465
- // Try to read from index first
466
- const indexPath = join(projectPath, SESSION_INDEX_FILENAME);
467
- try {
468
- const indexContent = await fs.readFile(indexPath, "utf8");
469
- const index = JSON.parse(indexContent);
470
- for (const [id, meta] of Object.entries(index.sessions)) {
471
- const lastActiveAt = new Date(meta.lastActiveAt);
472
- if (meta.sessionType === "main" && lastActiveAt >= sevenDaysAgo) {
473
- allSessions.push({
474
- id,
475
- ...meta,
476
- createdAt: new Date(meta.createdAt),
477
- lastActiveAt,
478
- });
347
+ // Scan .jsonl files in the project directory
348
+ const files = await fs.readdir(projectPath);
349
+ for (const file of files) {
350
+ if (!file.endsWith(".jsonl") || file.startsWith("subagent-")) {
351
+ continue;
352
+ }
353
+ try {
354
+ const filePath = join(projectPath, file);
355
+ const uuidMatch = file.match(/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/);
356
+ if (!uuidMatch)
357
+ continue;
358
+ const sessionId = uuidMatch[1];
359
+ const jsonlHandler = new JsonlHandler();
360
+ const lastMessage = await jsonlHandler.getLastMessage(filePath);
361
+ let lastActiveAt;
362
+ if (lastMessage) {
363
+ lastActiveAt = new Date(lastMessage.timestamp);
364
+ }
365
+ else {
366
+ const stats = await fs.stat(filePath);
367
+ lastActiveAt = stats.mtime;
479
368
  }
369
+ if (lastActiveAt < sevenDaysAgo)
370
+ continue;
371
+ allSessions.push({
372
+ id: sessionId,
373
+ sessionType: "main",
374
+ subagentType: undefined,
375
+ workdir: projectDirName,
376
+ createdAt: new Date(),
377
+ lastActiveAt,
378
+ latestTotalTokens: lastMessage?.usage
379
+ ? extractLatestTotalTokens([lastMessage])
380
+ : 0,
381
+ });
382
+ }
383
+ catch {
384
+ continue;
480
385
  }
481
- }
482
- catch {
483
- // If index fails, we skip this project directory
484
- // In the future, we could scan for .jsonl files here
485
386
  }
486
387
  }
487
388
  catch {
488
- // Skip if stat fails
389
+ // Skip if stat/readdir fails
489
390
  }
490
391
  }
491
392
  return allSessions.sort((a, b) => b.lastActiveAt.getTime() - a.lastActiveAt.getTime());
@@ -523,26 +424,6 @@ export async function cleanupExpiredSessionsFromJsonl(workdir) {
523
424
  if (fileAge > maxAge) {
524
425
  await fs.unlink(filePath);
525
426
  deletedCount++;
526
- // Remove from index if it exists
527
- try {
528
- const indexPath = join(projectDir.encodedPath, SESSION_INDEX_FILENAME);
529
- const indexContent = await fs.readFile(indexPath, "utf8");
530
- const index = JSON.parse(indexContent);
531
- const uuidMatch = file.match(/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/);
532
- const subagentMatch = file.match(/^subagent-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/);
533
- const sessionId = uuidMatch
534
- ? uuidMatch[1]
535
- : subagentMatch
536
- ? subagentMatch[1]
537
- : null;
538
- if (sessionId && index.sessions[sessionId]) {
539
- delete index.sessions[sessionId];
540
- await fs.writeFile(indexPath, JSON.stringify(index, null, 2), "utf8");
541
- }
542
- }
543
- catch {
544
- // Ignore index update errors during cleanup
545
- }
546
427
  }
547
428
  }
548
429
  catch {
@@ -697,21 +578,6 @@ export async function deleteSession(sessionId, workdir, sessionType = "main") {
697
578
  throw error;
698
579
  }
699
580
  }
700
- // Also remove from index
701
- try {
702
- const encoder = new PathEncoder();
703
- const projectDir = await encoder.getProjectDirectory(workdir, SESSION_DIR);
704
- const indexPath = join(projectDir.encodedPath, SESSION_INDEX_FILENAME);
705
- const indexContent = await fs.readFile(indexPath, "utf8");
706
- const index = JSON.parse(indexContent);
707
- if (index.sessions[sessionId]) {
708
- delete index.sessions[sessionId];
709
- await fs.writeFile(indexPath, JSON.stringify(index, null, 2), "utf8");
710
- }
711
- }
712
- catch {
713
- // Ignore index errors
714
- }
715
581
  }
716
582
  /**
717
583
  * Truncate content to a maximum length, adding ellipsis if truncated.
@@ -778,38 +644,16 @@ export async function handleSessionRestoration(restoreSessionId, continueLastSes
778
644
  }
779
645
  }
780
646
  /**
781
- * Load the full message thread by following parentSessionId links
647
+ * Load the full message thread for a session.
648
+ * With append-only compaction, all messages are in a single file.
649
+ * Returns the active messages (post-compact boundary).
782
650
  * @param currentSessionId - The ID of the current session
783
651
  * @param workdir - Working directory for the session
784
652
  * @returns Promise that resolves to an array of all messages in the thread
785
653
  */
786
654
  export async function loadFullMessageThread(currentSessionId, workdir) {
787
- const sessionIds = [];
788
- let currentId = currentSessionId;
789
- const allMessages = [];
790
- while (currentId) {
791
- const sessionData = await loadSessionFromJsonl(currentId, workdir);
792
- if (!sessionData)
793
- break;
794
- sessionIds.unshift(currentId);
795
- // Add messages from this session to the beginning of the list
796
- // But skip the "compact" block if it's not the first session in our traversal (which is the latest)
797
- // Actually, we should probably keep all messages and let the UI/logic handle it.
798
- // But wait, if we are concatenating, the "compact" block in session N summarizes session N-1.
799
- // So if we have session N-1 and session N, we should probably skip the compact block in session N.
800
- const messages = sessionData.messages;
801
- if (allMessages.length > 0) {
802
- // If we already have messages (from "later" sessions),
803
- // we are now adding messages from an "earlier" session.
804
- // The later session's first message might be a "compact" block.
805
- if (allMessages[0].blocks.some((b) => b.type === "compact")) {
806
- // Remove the compact block from the later session's messages
807
- // because we are now providing the actual messages it summarized.
808
- allMessages.shift();
809
- }
810
- }
811
- allMessages.unshift(...messages);
812
- currentId = sessionData.parentSessionId;
813
- }
814
- return { messages: allMessages, sessionIds };
655
+ const sessionData = await loadSessionFromJsonl(currentSessionId, workdir);
656
+ if (!sessionData)
657
+ return { messages: [], sessionIds: [] };
658
+ return { messages: sessionData.messages, sessionIds: [currentSessionId] };
815
659
  }
@@ -10,7 +10,7 @@
10
10
  * endLLMRequestSpan, aligning with Claude Code's approach.
11
11
  */
12
12
  import type { Span } from "@opentelemetry/api";
13
- import type { LLMRequestMetadata, ToolMetadata } from "../types/telemetry.js";
13
+ import type { LLMRequestMetadata, LLMRequestInput, ToolMetadata } from "../types/telemetry.js";
14
14
  /**
15
15
  * Creates an interaction span for a user turn.
16
16
  * The span is stored in interactionContext for the duration of the turn.
@@ -20,16 +20,26 @@ export declare function startInteractionSpan(userPrompt: string, sequence: numbe
20
20
  * Ends the current interaction span and clears the context.
21
21
  */
22
22
  export declare function endInteractionSpan(): void;
23
+ /**
24
+ * Resets incremental tracing state. Should be called after compaction to
25
+ * prevent incorrect delta calculations.
26
+ */
27
+ export declare function resetTracingState(): void;
23
28
  /**
24
29
  * Creates an LLM request span as a child of the interaction span.
25
30
  * Does NOT enter any ALS — the span must be passed explicitly to endLLMRequestSpan.
31
+ *
32
+ * When logToolContent is enabled, captures:
33
+ * - system_prompt_hash / system_prompt_preview / system_prompt_length
34
+ * - new_context (incremental messages delta)
35
+ * - tools (JSON array of {name, hash}) / tools_count
26
36
  */
27
- export declare function startLLMRequestSpan(model: string, options?: {
28
- context?: string;
29
- }): Span | undefined;
37
+ export declare function startLLMRequestSpan(model: string, options?: LLMRequestInput): Span | undefined;
30
38
  /**
31
39
  * Ends an LLM request span with response metadata.
32
40
  * The span is passed explicitly — no ALS is read or modified.
41
+ *
42
+ * When logToolContent is enabled, captures response.model_output.
33
43
  */
34
44
  export declare function endLLMRequestSpan(span: Span | undefined, metadata: LLMRequestMetadata): void;
35
45
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sessionTracing.d.ts","sourceRoot":"","sources":["../../src/telemetry/sessionTracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAM/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAkB9E;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,GAAG,SAAS,CAiBlB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,IAAI,GAAG,SAAS,CAsBlB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,GAAG,SAAS,EACtB,QAAQ,EAAE,kBAAkB,GAC3B,IAAI,CAmBN;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,OAAO,GACd,IAAI,GAAG,SAAS,CA4BlB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAmBxD"}
1
+ {"version":3,"file":"sessionTracing.d.ts","sourceRoot":"","sources":["../../src/telemetry/sessionTracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAO/C,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,YAAY,EACb,MAAM,uBAAuB,CAAC;AAgD/B;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,GAAG,SAAS,CAiBlB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAIxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,GACxB,IAAI,GAAG,SAAS,CA2GlB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,GAAG,SAAS,EACtB,QAAQ,EAAE,kBAAkB,GAC3B,IAAI,CAiCN;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,OAAO,GACd,IAAI,GAAG,SAAS,CA8BlB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAoBxD"}