@exreve/exk 1.0.13 → 1.0.15
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 +114 -17
- package/dist/ttc-cli.tar.gz +0 -0
- package/package.json +1 -1
package/dist/agentSession.js
CHANGED
|
@@ -51,12 +51,35 @@ function extractToolName(toolResult) {
|
|
|
51
51
|
if (toolResult.type === 'text' && toolResult.file)
|
|
52
52
|
return 'Read';
|
|
53
53
|
if (toolResult.file_path || toolResult.filePath) {
|
|
54
|
+
// Has old_string/new_string → Edit; has content/create → Write; else Read
|
|
55
|
+
if (toolResult.old_string !== undefined && toolResult.new_string !== undefined)
|
|
56
|
+
return 'Edit';
|
|
54
57
|
return (toolResult.content !== undefined || toolResult.type === 'create') ? 'Write' : 'Read';
|
|
55
58
|
}
|
|
56
59
|
if (toolResult.stdout !== undefined || toolResult.stderr !== undefined)
|
|
57
60
|
return 'Bash';
|
|
61
|
+
// SDK 0.2.x: content-only results from nested tool calls (no stdout/stderr wrapper)
|
|
62
|
+
if (toolResult.content && typeof toolResult.content === 'string' && toolResult.type === 'text')
|
|
63
|
+
return 'Bash';
|
|
58
64
|
return 'unknown';
|
|
59
65
|
}
|
|
66
|
+
// Look up tool name from the most recent assistant message's tool_use blocks by tool_use_id
|
|
67
|
+
function lookupToolNameFromHistory(messages, toolUseId) {
|
|
68
|
+
if (!toolUseId)
|
|
69
|
+
return null;
|
|
70
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
71
|
+
const msg = messages[i];
|
|
72
|
+
if (msg.role !== 'assistant')
|
|
73
|
+
continue;
|
|
74
|
+
const content = typeof msg.content === 'string' ? null : msg.content;
|
|
75
|
+
if (!Array.isArray(content))
|
|
76
|
+
continue;
|
|
77
|
+
const toolUse = content.find((c) => c.type === 'tool_use' && c.id === toolUseId);
|
|
78
|
+
if (toolUse?.name)
|
|
79
|
+
return toolUse.name;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
60
83
|
// AI config - loaded from server after registration, stored in ~/.talk-to-code/ai-config.json
|
|
61
84
|
// (Do not read ANTHROPIC_* / CLAUDE_MODEL from the host environment — only this file + code default model.)
|
|
62
85
|
const AI_CONFIG_PATH = path.join(os.homedir(), '.talk-to-code', 'ai-config.json');
|
|
@@ -251,6 +274,13 @@ export class AgentSessionManager {
|
|
|
251
274
|
if (!session.isProcessingQueue) {
|
|
252
275
|
this.processPromptQueue(sessionId);
|
|
253
276
|
}
|
|
277
|
+
else if (session.isProcessingQueue && !session.activeQueryStream && !this.emergencyStopInProgress.has(sessionId)) {
|
|
278
|
+
// Safety: isProcessingQueue is true but there's no active stream and no emergency stop
|
|
279
|
+
// This means the queue got stuck (e.g. from a previous abort return that bypassed cleanup)
|
|
280
|
+
console.warn(`[agentSession] Queue stuck detected for session ${sessionId}, resetting isProcessingQueue`);
|
|
281
|
+
session.isProcessingQueue = false;
|
|
282
|
+
this.processPromptQueue(sessionId);
|
|
283
|
+
}
|
|
254
284
|
}
|
|
255
285
|
async processPromptQueue(sessionId) {
|
|
256
286
|
const session = this.sessions.get(sessionId);
|
|
@@ -647,19 +677,62 @@ export class AgentSessionManager {
|
|
|
647
677
|
}
|
|
648
678
|
else if (message.type === 'user') {
|
|
649
679
|
const msg = message;
|
|
680
|
+
// SDK 0.2.x: tool results can appear in two places:
|
|
681
|
+
// 1. msg.tool_use_result (top-level field, present in 0.1.x and some 0.2.x messages)
|
|
682
|
+
// 2. msg.message.content array with type='tool_result' blocks (common in 0.2.x subagent calls)
|
|
683
|
+
let toolResult = null;
|
|
684
|
+
let toolUseId = msg.parent_tool_use_id;
|
|
685
|
+
// Check top-level tool_use_result first
|
|
650
686
|
if (msg.tool_use_result) {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
687
|
+
toolResult = msg.tool_use_result;
|
|
688
|
+
}
|
|
689
|
+
// Check message.content for tool_result blocks (SDK 0.2.x nested calls)
|
|
690
|
+
if (!toolResult && Array.isArray(msg.message?.content)) {
|
|
691
|
+
const contentBlocks = msg.message.content;
|
|
692
|
+
const toolResultBlock = contentBlocks.find((c) => c.type === 'tool_result');
|
|
693
|
+
if (toolResultBlock) {
|
|
694
|
+
// Extract tool use ID from the content block
|
|
695
|
+
if (toolResultBlock.tool_use_id) {
|
|
696
|
+
toolUseId = toolResultBlock.tool_use_id;
|
|
697
|
+
}
|
|
698
|
+
// The result content can be a string or array of content blocks
|
|
699
|
+
if (typeof toolResultBlock.content === 'string') {
|
|
700
|
+
try {
|
|
701
|
+
toolResult = JSON.parse(toolResultBlock.content);
|
|
702
|
+
}
|
|
703
|
+
catch {
|
|
704
|
+
toolResult = { content: toolResultBlock.content, type: 'text' };
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
else if (Array.isArray(toolResultBlock.content)) {
|
|
708
|
+
// Extract text from content blocks
|
|
709
|
+
const textParts = toolResultBlock.content
|
|
710
|
+
.filter((c) => c.type === 'text')
|
|
711
|
+
.map((c) => c.text);
|
|
712
|
+
const rawContent = textParts.join('\n');
|
|
713
|
+
try {
|
|
714
|
+
toolResult = JSON.parse(rawContent);
|
|
715
|
+
}
|
|
716
|
+
catch {
|
|
717
|
+
toolResult = { content: rawContent, type: 'text' };
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
toolResult = toolResultBlock;
|
|
722
|
+
}
|
|
656
723
|
}
|
|
724
|
+
}
|
|
725
|
+
if (toolResult) {
|
|
726
|
+
const detectedName = extractToolName(toolResult);
|
|
727
|
+
const resolvedName = detectedName !== 'unknown'
|
|
728
|
+
? detectedName
|
|
729
|
+
: (lookupToolNameFromHistory(session.messages, toolUseId) || detectedName);
|
|
657
730
|
onOutput({
|
|
658
731
|
type: 'tool_result',
|
|
659
732
|
data: toolResult,
|
|
660
733
|
timestamp: Date.now(),
|
|
661
734
|
metadata: {
|
|
662
|
-
toolName:
|
|
735
|
+
toolName: resolvedName,
|
|
663
736
|
toolResult: toolResult,
|
|
664
737
|
toolUseId: toolUseId || undefined,
|
|
665
738
|
parentToolUseId: msg.parent_tool_use_id,
|
|
@@ -785,7 +858,9 @@ export class AgentSessionManager {
|
|
|
785
858
|
onComplete(null);
|
|
786
859
|
session.activeQueryStream = undefined;
|
|
787
860
|
session.currentPromptId = undefined;
|
|
788
|
-
return
|
|
861
|
+
// Use break instead of return to ensure isProcessingQueue gets reset
|
|
862
|
+
// after the while loop at the end of processPromptQueue
|
|
863
|
+
break;
|
|
789
864
|
}
|
|
790
865
|
// Re-throw non-abort errors
|
|
791
866
|
throw streamError;
|
|
@@ -972,24 +1047,46 @@ export class AgentSessionManager {
|
|
|
972
1047
|
if (!session) {
|
|
973
1048
|
return { success: false, message: 'Session not found' };
|
|
974
1049
|
}
|
|
975
|
-
// 1. Abort
|
|
1050
|
+
// 1. Abort session-level controller only (not all sessions' controllers)
|
|
976
1051
|
session.abortController.abort();
|
|
977
|
-
|
|
978
|
-
|
|
1052
|
+
// Abort only controllers belonging to THIS session
|
|
1053
|
+
// Find and abort controllers for prompts in this session's queue and current prompt
|
|
1054
|
+
if (session.currentPromptId) {
|
|
1055
|
+
const ctrl = this.promptAbortControllers.get(session.currentPromptId);
|
|
1056
|
+
if (ctrl)
|
|
1057
|
+
ctrl.abort();
|
|
1058
|
+
}
|
|
1059
|
+
for (const queued of session.promptQueue) {
|
|
1060
|
+
if (queued.abortController)
|
|
1061
|
+
queued.abortController.abort();
|
|
1062
|
+
if (queued.promptId) {
|
|
1063
|
+
const ctrl = this.promptAbortControllers.get(queued.promptId);
|
|
1064
|
+
if (ctrl)
|
|
1065
|
+
ctrl.abort();
|
|
1066
|
+
}
|
|
979
1067
|
}
|
|
980
1068
|
// 2. Kill the entire process tree
|
|
981
1069
|
await this.killProcessTree(sessionId);
|
|
982
|
-
// 3.
|
|
1070
|
+
// 3. Collect prompt IDs from queue BEFORE clearing it
|
|
983
1071
|
const queueSize = session.promptQueue.length;
|
|
1072
|
+
const queuedPromptIds = session.promptQueue
|
|
1073
|
+
.map(p => p.promptId)
|
|
1074
|
+
.filter((id) => !!id);
|
|
1075
|
+
const currentPromptId = session.currentPromptId;
|
|
1076
|
+
// 4. Clear the prompt queue
|
|
984
1077
|
session.promptQueue = [];
|
|
985
|
-
//
|
|
1078
|
+
// 5. Clear active stream
|
|
986
1079
|
session.activeQueryStream = undefined;
|
|
987
|
-
//
|
|
1080
|
+
// 6. Reset processing state
|
|
988
1081
|
session.isProcessingQueue = false;
|
|
989
|
-
//
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1082
|
+
// 7. Clean up abort controllers map (only for this session's prompts, not ALL sessions)
|
|
1083
|
+
if (currentPromptId) {
|
|
1084
|
+
this.promptAbortControllers.delete(currentPromptId);
|
|
1085
|
+
}
|
|
1086
|
+
for (const pid of queuedPromptIds) {
|
|
1087
|
+
this.promptAbortControllers.delete(pid);
|
|
1088
|
+
}
|
|
1089
|
+
session.currentPromptId = undefined;
|
|
993
1090
|
// 8. Remove from emergency stop tracking
|
|
994
1091
|
this.emergencyStopInProgress.delete(sessionId);
|
|
995
1092
|
// 9. Resolve any pending choice request with null (cancelled)
|
package/dist/ttc-cli.tar.gz
CHANGED
|
Binary file
|