@exreve/exk 1.0.45 → 1.0.47
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/agentSession.js +107 -35
- package/dist/app-child.js +144 -1673
- package/dist/appHandlers.js +142 -0
- package/dist/appManager.js +5 -15
- package/dist/appRunner.js +2 -2
- package/dist/cloudflaredHandlers.js +279 -0
- package/dist/containerHandlers.js +193 -0
- package/dist/fsHandlers.js +86 -0
- package/dist/githubHandlers.js +521 -0
- package/dist/index.js +142 -1741
- package/dist/openaiAdapter.js +0 -11
- package/dist/projectManager.js +1 -24
- package/dist/runnerGenerator.js +2 -3
- package/dist/sessionHandlers.js +271 -0
- package/dist/ttc-cli.tar.gz +0 -0
- package/dist/updateHandlers.js +82 -0
- package/dist/updater.js +2 -5
- package/package.json +1 -1
package/dist/agentSession.js
CHANGED
|
@@ -251,7 +251,7 @@ function readProxyToggle() {
|
|
|
251
251
|
/** Env for the Claude Code child: copy of host env with host ANTHROPIC_* stripped, then inject from provider or ai-config.
|
|
252
252
|
* If a local model is provided, override baseUrl to point to the anthropic-proxy adapter.
|
|
253
253
|
* If resolvedProvider is provided, use its credentials instead of ai-config defaults. */
|
|
254
|
-
function envForClaudeCodeChild(
|
|
254
|
+
function envForClaudeCodeChild(_localModel, resolvedProvider) {
|
|
255
255
|
const env = { ...process.env };
|
|
256
256
|
// Strip any host ANTHROPIC_* vars to prevent leaking credentials or wrong URLs
|
|
257
257
|
delete env.ANTHROPIC_API_KEY;
|
|
@@ -333,6 +333,71 @@ export class AgentSessionManager {
|
|
|
333
333
|
promptAbortControllers = new Map(); // Map promptId -> AbortController for cancellation
|
|
334
334
|
emergencyStopInProgress = new Set(); // Track sessions being emergency stopped
|
|
335
335
|
sessionHandlers = new Map(); // Track handlers for each session
|
|
336
|
+
socketRef = null; // Socket.IO reference for fetching session history from backend
|
|
337
|
+
/** Set the socket reference for backend communication (called from app-child.ts) */
|
|
338
|
+
setSocketRef(socket) {
|
|
339
|
+
this.socketRef = socket;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Fetch conversation history from the backend DB for a session.
|
|
343
|
+
* Returns array of { role, content } pairs (user prompts + assistant responses).
|
|
344
|
+
*/
|
|
345
|
+
async fetchSessionHistory(sessionId) {
|
|
346
|
+
return new Promise((resolve) => {
|
|
347
|
+
if (!this.socketRef?.connected) {
|
|
348
|
+
console.log(`[AgentSessionManager] Cannot fetch history: socket not connected`);
|
|
349
|
+
resolve([]);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const timeoutId = setTimeout(() => {
|
|
353
|
+
console.warn(`[AgentSessionManager] fetchSessionHistory timed out for ${sessionId}`);
|
|
354
|
+
resolve([]);
|
|
355
|
+
}, 5000);
|
|
356
|
+
this.socketRef.emit('session:history', { sessionId }, (response) => {
|
|
357
|
+
clearTimeout(timeoutId);
|
|
358
|
+
if (response?.history && Array.isArray(response.history)) {
|
|
359
|
+
console.log(`[AgentSessionManager] Fetched ${response.history.length} history entries for session ${sessionId}`);
|
|
360
|
+
resolve(response.history);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
resolve([]);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Format conversation history into a text block for injection into a prompt.
|
|
370
|
+
* Takes the last N exchanges to avoid token overflow.
|
|
371
|
+
*/
|
|
372
|
+
formatHistoryForPrompt(history) {
|
|
373
|
+
if (!history.length)
|
|
374
|
+
return '';
|
|
375
|
+
// Take last N entries to stay within reasonable token limits
|
|
376
|
+
const maxEntries = 40;
|
|
377
|
+
const trimmed = history.slice(-maxEntries);
|
|
378
|
+
const lines = trimmed.map(m => {
|
|
379
|
+
const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
|
|
380
|
+
// Truncate very long individual messages
|
|
381
|
+
const maxLen = 2000;
|
|
382
|
+
const truncated = content.length > maxLen
|
|
383
|
+
? content.slice(0, maxLen) + '...[truncated]'
|
|
384
|
+
: content;
|
|
385
|
+
return `<${m.role}>\n${truncated}\n</${m.role}>`;
|
|
386
|
+
});
|
|
387
|
+
return [
|
|
388
|
+
'[Previous Conversation Context]',
|
|
389
|
+
'The following is conversation history from this session that was lost due to a session reset.',
|
|
390
|
+
'Use it as context for the current request.',
|
|
391
|
+
'',
|
|
392
|
+
'<conversation>',
|
|
393
|
+
...lines,
|
|
394
|
+
'</conversation>',
|
|
395
|
+
'',
|
|
396
|
+
'[End of Previous Context]',
|
|
397
|
+
'',
|
|
398
|
+
'',
|
|
399
|
+
].join('\n');
|
|
400
|
+
}
|
|
336
401
|
async createSession(handler) {
|
|
337
402
|
const { sessionId, projectPath, model } = handler;
|
|
338
403
|
const sessionModel = model || CLAUDE_CONFIG.model;
|
|
@@ -456,8 +521,10 @@ export class AgentSessionManager {
|
|
|
456
521
|
while (session.promptQueue.length > 0 && !this.emergencyStopInProgress.has(sessionId)) {
|
|
457
522
|
const queuedPrompt = session.promptQueue.shift();
|
|
458
523
|
const { enhancers, handler, promptId: queuedPromptId, abortController: queuedAbortController } = queuedPrompt;
|
|
459
|
-
const { projectPath, promptId, onOutput, onError, onComplete, onStatusUpdate } = handler;
|
|
460
|
-
const
|
|
524
|
+
const { projectPath, promptId, onOutput: _onOutput, onError: _onError, onComplete: _onComplete, onStatusUpdate } = handler;
|
|
525
|
+
const onOutput = _onOutput;
|
|
526
|
+
const onError = _onError;
|
|
527
|
+
const onComplete = _onComplete;
|
|
461
528
|
// Write attachments to temp dir and inject paths into prompt
|
|
462
529
|
let effectivePrompt = queuedPrompt.prompt;
|
|
463
530
|
let attachmentDir;
|
|
@@ -503,7 +570,9 @@ export class AgentSessionManager {
|
|
|
503
570
|
try {
|
|
504
571
|
for await (const _ of session.activeQueryStream) { }
|
|
505
572
|
}
|
|
506
|
-
catch {
|
|
573
|
+
catch (err) {
|
|
574
|
+
console.error(`[AgentSession] Error draining active query stream:`, err);
|
|
575
|
+
}
|
|
507
576
|
session.activeQueryStream = undefined;
|
|
508
577
|
}
|
|
509
578
|
session.activeQueryStream = undefined;
|
|
@@ -515,6 +584,25 @@ export class AgentSessionManager {
|
|
|
515
584
|
finalPrompt = `${skillContent}\n\n${effectivePrompt}`;
|
|
516
585
|
}
|
|
517
586
|
}
|
|
587
|
+
// Inject DB history if context was lost in a previous prompt (resume failed)
|
|
588
|
+
if (session.contextLost) {
|
|
589
|
+
console.log(`[agentSession] Context was lost previously, fetching DB history for session ${sessionId}`);
|
|
590
|
+
try {
|
|
591
|
+
const history = await this.fetchSessionHistory(sessionId);
|
|
592
|
+
if (history.length > 0) {
|
|
593
|
+
const historyPrefix = this.formatHistoryForPrompt(history);
|
|
594
|
+
finalPrompt = historyPrefix + finalPrompt;
|
|
595
|
+
console.log(`[agentSession] Injected ${history.length} history entries into prompt for session ${sessionId}`);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
console.log(`[agentSession] No DB history available for session ${sessionId}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
catch (err) {
|
|
602
|
+
console.error(`[agentSession] Failed to fetch/format history:`, err);
|
|
603
|
+
}
|
|
604
|
+
session.contextLost = false; // Reset after injection attempt
|
|
605
|
+
}
|
|
518
606
|
// Add user message to history
|
|
519
607
|
session.messages.push({
|
|
520
608
|
role: 'user',
|
|
@@ -577,7 +665,9 @@ export class AgentSessionManager {
|
|
|
577
665
|
mkdirSync(cwd2, { recursive: true });
|
|
578
666
|
}
|
|
579
667
|
}
|
|
580
|
-
catch {
|
|
668
|
+
catch (err) {
|
|
669
|
+
console.error(`[AgentSession] Failed to create working directory ${cwd2}:`, err);
|
|
670
|
+
}
|
|
581
671
|
const isWin = process.platform === 'win32';
|
|
582
672
|
// Ensure PATH includes common node locations, especially in containers
|
|
583
673
|
const defaultPath = isWin
|
|
@@ -778,15 +868,24 @@ export class AgentSessionManager {
|
|
|
778
868
|
// Capture Claude SDK session ID from system init message
|
|
779
869
|
if (message.type === 'system' && message.subtype === 'init') {
|
|
780
870
|
const systemMsg = message;
|
|
781
|
-
if (systemMsg.session_id
|
|
871
|
+
if (systemMsg.session_id) {
|
|
872
|
+
// Detect context loss: session_id changed unexpectedly (resume failed)
|
|
873
|
+
if (session.claudeSessionId && session.claudeSessionId !== systemMsg.session_id) {
|
|
874
|
+
session.contextLost = true;
|
|
875
|
+
console.warn(`[AgentSessionManager] Context lost! Session ID changed: ${session.claudeSessionId} → ${systemMsg.session_id}`);
|
|
876
|
+
}
|
|
782
877
|
session.claudeSessionId = systemMsg.session_id;
|
|
783
878
|
saveSessionState(sessionId, { claudeSessionId: systemMsg.session_id, model: session.model, provider: resolveProvider(session.model).provider, updatedAt: Date.now() });
|
|
784
879
|
}
|
|
785
880
|
}
|
|
786
881
|
if (message.type === 'assistant') {
|
|
787
882
|
const msg = message;
|
|
788
|
-
// Capture Claude session ID from assistant message
|
|
789
|
-
if (msg.session_id
|
|
883
|
+
// Capture Claude session ID from assistant message (always update to track session changes)
|
|
884
|
+
if (msg.session_id) {
|
|
885
|
+
if (session.claudeSessionId && session.claudeSessionId !== msg.session_id) {
|
|
886
|
+
session.contextLost = true;
|
|
887
|
+
console.warn(`[AgentSessionManager] Context lost! Session ID changed in assistant msg: ${session.claudeSessionId} → ${msg.session_id}`);
|
|
888
|
+
}
|
|
790
889
|
session.claudeSessionId = msg.session_id;
|
|
791
890
|
saveSessionState(sessionId, { claudeSessionId: msg.session_id, model: session.model, provider: resolveProvider(session.model).provider, updatedAt: Date.now() });
|
|
792
891
|
}
|
|
@@ -1073,33 +1172,6 @@ export class AgentSessionManager {
|
|
|
1073
1172
|
this.processPromptQueue(sessionId);
|
|
1074
1173
|
}
|
|
1075
1174
|
}
|
|
1076
|
-
getContextInfo(sessionId) {
|
|
1077
|
-
const session = this.sessions.get(sessionId);
|
|
1078
|
-
if (!session)
|
|
1079
|
-
return null;
|
|
1080
|
-
return {
|
|
1081
|
-
messageCount: session.messages.length,
|
|
1082
|
-
totalInputTokens: session.totalInputTokens,
|
|
1083
|
-
totalOutputTokens: session.totalOutputTokens,
|
|
1084
|
-
totalTokens: session.totalInputTokens + session.totalOutputTokens,
|
|
1085
|
-
totalCostUsd: session.totalCostUsd,
|
|
1086
|
-
lastUsage: session.lastUsage
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
clearContext(sessionId) {
|
|
1090
|
-
const session = this.sessions.get(sessionId);
|
|
1091
|
-
if (session) {
|
|
1092
|
-
session.messages = [];
|
|
1093
|
-
session.totalInputTokens = 0;
|
|
1094
|
-
session.totalOutputTokens = 0;
|
|
1095
|
-
session.totalCostUsd = 0;
|
|
1096
|
-
// Clear Claude session ID to start fresh
|
|
1097
|
-
session.claudeSessionId = undefined;
|
|
1098
|
-
session.lastUsage = undefined;
|
|
1099
|
-
// Also clear persisted state
|
|
1100
|
-
deleteSessionState(sessionId);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
1175
|
async deleteSession(sessionId) {
|
|
1104
1176
|
const session = this.sessions.get(sessionId);
|
|
1105
1177
|
if (session) {
|