@exreve/exk 1.0.74 → 1.0.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/agentSession.js +157 -13
- package/dist/cli/sessionHandlers.js +61 -0
- package/dist/ttc-cli.tar.gz +0 -0
- package/package.json +1 -1
package/dist/cli/agentSession.js
CHANGED
|
@@ -588,23 +588,43 @@ export class AgentSessionManager {
|
|
|
588
588
|
finalPrompt = `${skillContent}\n\n${effectivePrompt}`;
|
|
589
589
|
}
|
|
590
590
|
}
|
|
591
|
-
// Inject DB history if context was lost
|
|
591
|
+
// Inject compacted summary or raw DB history if context was lost
|
|
592
592
|
if (session.contextLost) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
593
|
+
if (session.compactionSummary) {
|
|
594
|
+
// Use AI-generated summary from compaction
|
|
595
|
+
console.log(`[agentSession] Injecting AI compaction summary for session ${sessionId}`);
|
|
596
|
+
const summaryPrefix = [
|
|
597
|
+
'[Compacted Session Context]',
|
|
598
|
+
'The following is an AI-generated summary of the previous conversation in this session.',
|
|
599
|
+
'Use it as context to continue where you left off.',
|
|
600
|
+
'',
|
|
601
|
+
session.compactionSummary,
|
|
602
|
+
'',
|
|
603
|
+
'[End of Compacted Context]',
|
|
604
|
+
'',
|
|
605
|
+
'',
|
|
606
|
+
].join('\n');
|
|
607
|
+
finalPrompt = summaryPrefix + finalPrompt;
|
|
608
|
+
session.compactionSummary = undefined; // Only inject once
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
// Fallback: inject raw DB history
|
|
612
|
+
console.log(`[agentSession] No compaction summary, fetching DB history for session ${sessionId}`);
|
|
613
|
+
try {
|
|
614
|
+
const history = await this.fetchSessionHistory(sessionId);
|
|
615
|
+
if (history.length > 0) {
|
|
616
|
+
const historyPrefix = this.formatHistoryForPrompt(history);
|
|
617
|
+
finalPrompt = historyPrefix + finalPrompt;
|
|
618
|
+
console.log(`[agentSession] Injected ${history.length} history entries into prompt for session ${sessionId}`);
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
console.log(`[agentSession] No DB history available for session ${sessionId}`);
|
|
622
|
+
}
|
|
600
623
|
}
|
|
601
|
-
|
|
602
|
-
console.
|
|
624
|
+
catch (err) {
|
|
625
|
+
console.error(`[agentSession] Failed to fetch/format history:`, err);
|
|
603
626
|
}
|
|
604
627
|
}
|
|
605
|
-
catch (err) {
|
|
606
|
-
console.error(`[agentSession] Failed to fetch/format history:`, err);
|
|
607
|
-
}
|
|
608
628
|
session.contextLost = false; // Reset after injection attempt
|
|
609
629
|
}
|
|
610
630
|
// Add user message to history
|
|
@@ -1087,6 +1107,130 @@ export class AgentSessionManager {
|
|
|
1087
1107
|
session.claudeProcessGroupId = undefined;
|
|
1088
1108
|
}
|
|
1089
1109
|
}
|
|
1110
|
+
/**
|
|
1111
|
+
* AI-powered session compaction.
|
|
1112
|
+
*
|
|
1113
|
+
* 1. Fetches full conversation history from the backend DB
|
|
1114
|
+
* 2. Writes it to a temp file
|
|
1115
|
+
* 3. Spawns a real prompt asking the model to summarize the conversation
|
|
1116
|
+
* 4. Captures the model's summary
|
|
1117
|
+
* 5. Clears the SDK session and stores the summary for injection on next prompt
|
|
1118
|
+
*
|
|
1119
|
+
* Returns true if the compaction prompt was queued successfully.
|
|
1120
|
+
* The `onCompactionComplete` callback fires when the model finishes summarizing.
|
|
1121
|
+
*/
|
|
1122
|
+
async compactSession(sessionId, projectPath, onOutput, onCompactionComplete) {
|
|
1123
|
+
const session = this.sessions.get(sessionId);
|
|
1124
|
+
if (!session)
|
|
1125
|
+
return false;
|
|
1126
|
+
// Don't compact while a prompt is actively running
|
|
1127
|
+
if (session.isProcessingQueue && session.activeBackendStream)
|
|
1128
|
+
return false;
|
|
1129
|
+
// 1. Fetch conversation history from DB
|
|
1130
|
+
const history = await this.fetchSessionHistory(sessionId);
|
|
1131
|
+
if (history.length === 0) {
|
|
1132
|
+
console.log(`[AgentSessionManager] No history to compact for session ${sessionId}`);
|
|
1133
|
+
// Still allow compaction even without history — just clears context
|
|
1134
|
+
session.sdkSessionId = undefined;
|
|
1135
|
+
session.contextLost = true;
|
|
1136
|
+
session.messages = [];
|
|
1137
|
+
deleteSessionState(sessionId);
|
|
1138
|
+
onCompactionComplete?.(true, 'Session context cleared (no history available)');
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
console.log(`[AgentSessionManager] Starting AI compaction for session ${sessionId} with ${history.length} history entries`);
|
|
1142
|
+
// 2. Build the conversation text
|
|
1143
|
+
const conversationLines = [];
|
|
1144
|
+
for (const entry of history) {
|
|
1145
|
+
const content = typeof entry.content === 'string' ? entry.content : JSON.stringify(entry.content);
|
|
1146
|
+
conversationLines.push(`=== ${entry.role.toUpperCase()} ===\n${content}`);
|
|
1147
|
+
}
|
|
1148
|
+
const conversationText = conversationLines.join('\n\n');
|
|
1149
|
+
// 3. Write conversation to a temp file
|
|
1150
|
+
const attachmentDir = path.join(os.tmpdir(), 'talk-to-code', 'compaction', sessionId);
|
|
1151
|
+
mkdirSync(attachmentDir, { recursive: true });
|
|
1152
|
+
const conversationFilePath = path.join(attachmentDir, 'conversation.md');
|
|
1153
|
+
writeFileSync(conversationFilePath, conversationText, 'utf-8');
|
|
1154
|
+
// 4. Build the compaction prompt
|
|
1155
|
+
const compactionPrompt = [
|
|
1156
|
+
'You are performing a CONTEXT COMPACTION for an ongoing coding session.',
|
|
1157
|
+
'',
|
|
1158
|
+
'Read the full conversation history in the attached file `conversation.md`.',
|
|
1159
|
+
'',
|
|
1160
|
+
'Your task: Create a comprehensive summary that captures:',
|
|
1161
|
+
'1. What the user is working on (project, goals, current task)',
|
|
1162
|
+
'2. All decisions made and their reasoning',
|
|
1163
|
+
'3. The current state of the codebase (what was changed, added, removed)',
|
|
1164
|
+
'4. Any open issues, bugs, or TODOs discussed',
|
|
1165
|
+
'5. Important technical details (file paths, configurations, API designs)',
|
|
1166
|
+
'6. The last thing the user was doing or about to do',
|
|
1167
|
+
'',
|
|
1168
|
+
'Be thorough but concise. Include specific file paths, code snippets, and technical details.',
|
|
1169
|
+
'This summary will be the ONLY context carried forward — everything else is lost.',
|
|
1170
|
+
'',
|
|
1171
|
+
'Format the summary as a structured document with clear sections.',
|
|
1172
|
+
'Write the summary directly — no preamble or meta-commentary.',
|
|
1173
|
+
].join('\n');
|
|
1174
|
+
// 5. Create a synthetic attachment for the conversation file
|
|
1175
|
+
const attachment = {
|
|
1176
|
+
filename: 'conversation.md',
|
|
1177
|
+
mimeType: 'text/markdown',
|
|
1178
|
+
content: Buffer.from(conversationText).toString('base64'),
|
|
1179
|
+
};
|
|
1180
|
+
// 6. Spawn a compaction prompt through the normal pipeline
|
|
1181
|
+
const compactionPromptId = `compact-${sessionId}-${Date.now()}`;
|
|
1182
|
+
let summaryParts = [];
|
|
1183
|
+
await this.sendPrompt(sessionId, compactionPrompt, [], {
|
|
1184
|
+
sessionId,
|
|
1185
|
+
projectPath,
|
|
1186
|
+
promptId: compactionPromptId,
|
|
1187
|
+
model: session.model,
|
|
1188
|
+
attachments: [attachment],
|
|
1189
|
+
onOutput: (output) => {
|
|
1190
|
+
// Forward to caller for UI display (shows compacting progress)
|
|
1191
|
+
onOutput?.(output);
|
|
1192
|
+
// Capture assistant text for the summary
|
|
1193
|
+
if (output.type === 'assistant' && output.data) {
|
|
1194
|
+
const d = output.data;
|
|
1195
|
+
const text = typeof output.data === 'string'
|
|
1196
|
+
? output.data
|
|
1197
|
+
: (d?.content?.[0]?.text || d?.text || '');
|
|
1198
|
+
if (text)
|
|
1199
|
+
summaryParts.push(text);
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
onError: (error) => {
|
|
1203
|
+
console.error(`[AgentSessionManager] Compaction prompt error: ${error}`);
|
|
1204
|
+
onCompactionComplete?.(false, `Compaction failed: ${error}`);
|
|
1205
|
+
},
|
|
1206
|
+
onComplete: (exitCode) => {
|
|
1207
|
+
const summary = summaryParts.join('\n').trim();
|
|
1208
|
+
if (summary && exitCode === 0) {
|
|
1209
|
+
// Store summary and clear old context
|
|
1210
|
+
session.compactionSummary = summary;
|
|
1211
|
+
session.sdkSessionId = undefined;
|
|
1212
|
+
session.contextLost = true;
|
|
1213
|
+
session.messages = [];
|
|
1214
|
+
deleteSessionState(sessionId);
|
|
1215
|
+
console.log(`[AgentSessionManager] AI compaction complete for session ${sessionId} (${summary.length} chars summary)`);
|
|
1216
|
+
onCompactionComplete?.(true, 'Session context compacted with AI summary');
|
|
1217
|
+
}
|
|
1218
|
+
else {
|
|
1219
|
+
// Compaction prompt failed — fall back to simple clear
|
|
1220
|
+
session.sdkSessionId = undefined;
|
|
1221
|
+
session.contextLost = true;
|
|
1222
|
+
session.messages = [];
|
|
1223
|
+
deleteSessionState(sessionId);
|
|
1224
|
+
console.warn(`[AgentSessionManager] Compaction prompt failed (exit=${exitCode}), falling back to simple clear`);
|
|
1225
|
+
onCompactionComplete?.(true, 'Session context cleared (compaction prompt did not produce summary)');
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
onStatusUpdate: () => {
|
|
1229
|
+
// Status updates from the compaction prompt — ignore
|
|
1230
|
+
},
|
|
1231
|
+
});
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1090
1234
|
/**
|
|
1091
1235
|
* Emergency stop - immediately halt all activity in a session
|
|
1092
1236
|
* This is a forceful stop that kills all processes and clears state
|
|
@@ -243,6 +243,67 @@ export function registerSessionHandlers(socket, foreground, activeSessions, getS
|
|
|
243
243
|
callback?.({ success: false, error: error.message });
|
|
244
244
|
}
|
|
245
245
|
});
|
|
246
|
+
socket.on('session:compact', async (data, callback) => {
|
|
247
|
+
try {
|
|
248
|
+
const { sessionId } = data;
|
|
249
|
+
if (!sessionId) {
|
|
250
|
+
callback?.({ success: false, message: 'Missing sessionId' });
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const sessionInfo = activeSessions.get(sessionId);
|
|
254
|
+
if (!sessionInfo) {
|
|
255
|
+
callback?.({ success: false, message: 'Session not found' });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (foreground) {
|
|
259
|
+
console.log(`[CLI] Compacting session: ${sessionId}`);
|
|
260
|
+
}
|
|
261
|
+
// Respond immediately — compaction runs in the background
|
|
262
|
+
callback?.({ success: true, message: 'Compaction started' });
|
|
263
|
+
const compacted = await agentSessionManager.compactSession(sessionId, sessionInfo.projectPath, (output) => {
|
|
264
|
+
// Forward compaction prompt output to the backend for UI display
|
|
265
|
+
const compactPromptId = `compact-${sessionId}`;
|
|
266
|
+
getSocket().emit('prompt:output', {
|
|
267
|
+
sessionId,
|
|
268
|
+
promptId: compactPromptId,
|
|
269
|
+
type: output.type,
|
|
270
|
+
data: output.data,
|
|
271
|
+
timestamp: output.timestamp,
|
|
272
|
+
metadata: { ...output.metadata, isCompaction: true },
|
|
273
|
+
});
|
|
274
|
+
}, (success, message) => {
|
|
275
|
+
// Compaction finished — notify UI
|
|
276
|
+
getSocket().emit('session:compacted', {
|
|
277
|
+
sessionId,
|
|
278
|
+
timestamp: Date.now(),
|
|
279
|
+
success,
|
|
280
|
+
message,
|
|
281
|
+
});
|
|
282
|
+
if (foreground) {
|
|
283
|
+
console.log(`[CLI] Compaction ${success ? 'complete' : 'failed'}: ${message}`);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
if (!compacted) {
|
|
287
|
+
getSocket().emit('session:compacted', {
|
|
288
|
+
sessionId,
|
|
289
|
+
timestamp: Date.now(),
|
|
290
|
+
success: false,
|
|
291
|
+
message: 'Session not found or prompt is running',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
if (foreground) {
|
|
297
|
+
console.error(`✗ Error compacting session: ${error.message}`);
|
|
298
|
+
}
|
|
299
|
+
getSocket().emit('session:compacted', {
|
|
300
|
+
sessionId: data.sessionId,
|
|
301
|
+
timestamp: Date.now(),
|
|
302
|
+
success: false,
|
|
303
|
+
message: error.message,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
});
|
|
246
307
|
socket.on('emergency:stop', async (data, callback) => {
|
|
247
308
|
try {
|
|
248
309
|
const { sessionId } = data;
|
package/dist/ttc-cli.tar.gz
CHANGED
|
Binary file
|