@xagent-ai/cli 1.3.0 → 1.3.2
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/.github/release.yml +76 -0
- package/.github/workflows/ci.yml +3 -0
- package/.github/workflows/release.yml +11 -17
- package/README.md +2 -2
- package/README_CN.md +2 -2
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +7 -3
- package/dist/agents.js.map +1 -1
- package/dist/ai-client/factory.d.ts +0 -12
- package/dist/ai-client/factory.d.ts.map +1 -1
- package/dist/ai-client/factory.js +0 -32
- package/dist/ai-client/factory.js.map +1 -1
- package/dist/ai-client/index.js +1 -1
- package/dist/ai-client/index.js.map +1 -1
- package/dist/ai-client/providers/anthropic.d.ts.map +1 -1
- package/dist/ai-client/providers/anthropic.js +10 -4
- package/dist/ai-client/providers/anthropic.js.map +1 -1
- package/dist/ai-client/providers/openai.d.ts.map +1 -1
- package/dist/ai-client/providers/openai.js +8 -4
- package/dist/ai-client/providers/openai.js.map +1 -1
- package/dist/ai-client/providers/remote.d.ts +0 -1
- package/dist/ai-client/providers/remote.d.ts.map +1 -1
- package/dist/ai-client/providers/remote.js +11 -10
- package/dist/ai-client/providers/remote.js.map +1 -1
- package/dist/ai-client/types.d.ts +14 -0
- package/dist/ai-client/types.d.ts.map +1 -1
- package/dist/ai-client/types.js +17 -0
- package/dist/ai-client/types.js.map +1 -1
- package/dist/ai-client-factory.d.ts.map +1 -1
- package/dist/ai-client-factory.js +4 -4
- package/dist/ai-client-factory.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +10 -12
- package/dist/auth.js.map +1 -1
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/cancellation.js +3 -5
- package/dist/cancellation.js.map +1 -1
- package/dist/checkpoint.d.ts +1 -0
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +38 -4
- package/dist/checkpoint.js.map +1 -1
- package/dist/cli.js +132 -32
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts +1 -2
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +22 -17
- package/dist/context-compressor.js.map +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +8 -7
- package/dist/conversation.js.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.js +2 -2
- package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.d.ts +10 -0
- package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.js +105 -32
- package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
- package/dist/gui-subagent/index.d.ts +7 -0
- package/dist/gui-subagent/index.d.ts.map +1 -1
- package/dist/gui-subagent/index.js +2 -0
- package/dist/gui-subagent/index.js.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.js +2 -0
- package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
- package/dist/input-processor.js +2 -2
- package/dist/input-processor.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +84 -21
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +3 -3
- package/dist/memory.js.map +1 -1
- package/dist/output-util.d.ts +27 -0
- package/dist/output-util.d.ts.map +1 -0
- package/dist/output-util.js +74 -0
- package/dist/output-util.js.map +1 -0
- package/dist/retry.js +1 -1
- package/dist/retry.js.map +1 -1
- package/dist/ripgrep.d.ts.map +1 -1
- package/dist/ripgrep.js +5 -3
- package/dist/ripgrep.js.map +1 -1
- package/dist/sdk-output-adapter.d.ts +265 -0
- package/dist/sdk-output-adapter.d.ts.map +1 -0
- package/dist/sdk-output-adapter.js +701 -0
- package/dist/sdk-output-adapter.js.map +1 -0
- package/dist/sdk-session.d.ts +13 -0
- package/dist/sdk-session.d.ts.map +1 -0
- package/dist/sdk-session.js +50 -0
- package/dist/sdk-session.js.map +1 -0
- package/dist/session-manager.js +3 -3
- package/dist/session-manager.js.map +1 -1
- package/dist/session.d.ts +96 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +849 -262
- package/dist/session.js.map +1 -1
- package/dist/shell.d.ts.map +1 -1
- package/dist/shell.js +5 -4
- package/dist/shell.js.map +1 -1
- package/dist/skill-installer.js +3 -3
- package/dist/skill-installer.js.map +1 -1
- package/dist/skill-invoker.d.ts +1 -1
- package/dist/skill-invoker.d.ts.map +1 -1
- package/dist/skill-invoker.js +2 -2
- package/dist/skill-invoker.js.map +1 -1
- package/dist/skill-loader.js +6 -5
- package/dist/skill-loader.js.map +1 -1
- package/dist/skill-manager.d.ts.map +1 -1
- package/dist/skill-manager.js +3 -2
- package/dist/skill-manager.js.map +1 -1
- package/dist/slash-commands.d.ts +1 -1
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +24 -11
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts +20 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +58 -1
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.js +3 -3
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +9 -8
- package/dist/theme.js.map +1 -1
- package/dist/tools.d.ts +15 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +487 -215
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +49 -0
- package/dist/types.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +12 -9
- package/dist/update.js.map +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +1 -2
- package/dist/workflow.js.map +1 -1
- package/docs/third-party-models.md +16 -15
- package/package.json +3 -1
- package/src/agents.ts +7 -3
- package/src/ai-client/factory.ts +1 -36
- package/src/ai-client/index.ts +1 -1
- package/src/ai-client/providers/anthropic.ts +12 -3
- package/src/ai-client/providers/openai.ts +10 -4
- package/src/ai-client/providers/remote.ts +13 -10
- package/src/ai-client/types.ts +19 -0
- package/src/ai-client-factory.ts +5 -5
- package/src/auth.ts +11 -13
- package/src/cancellation.ts +3 -6
- package/src/checkpoint.ts +41 -4
- package/src/cli.ts +154 -37
- package/src/config.ts +1 -1
- package/src/context-compressor.ts +27 -22
- package/src/conversation.ts +9 -7
- package/src/gui-subagent/action-parser/actionParser.ts +2 -2
- package/src/gui-subagent/agent/gui-agent.ts +117 -34
- package/src/gui-subagent/index.ts +8 -0
- package/src/gui-subagent/operator/computer-operator.ts +2 -1
- package/src/input-processor.ts +2 -2
- package/src/logger.ts +2 -4
- package/src/mcp.ts +87 -23
- package/src/memory.ts +3 -4
- package/src/output-util.ts +80 -0
- package/src/retry.ts +1 -1
- package/src/ripgrep.ts +5 -3
- package/src/sdk-output-adapter.ts +842 -0
- package/src/sdk-session.ts +62 -0
- package/src/session-manager.ts +3 -3
- package/src/session.ts +942 -302
- package/src/shell.ts +6 -5
- package/src/skill-installer.ts +3 -3
- package/src/skill-invoker.ts +3 -4
- package/src/skill-loader.ts +7 -7
- package/src/skill-manager.ts +4 -3
- package/src/slash-commands.ts +24 -16
- package/src/smart-approval.ts +76 -1
- package/src/system-prompt-generator.ts +3 -3
- package/src/theme.ts +10 -9
- package/src/tools.ts +563 -267
- package/src/types.ts +118 -0
- package/src/update.ts +12 -9
- package/src/workflow.ts +2 -4
- package/test/cli-launch.test.ts +279 -0
- package/vitest.config.ts +2 -0
- /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/src/session.ts
CHANGED
|
@@ -18,10 +18,8 @@ import {
|
|
|
18
18
|
ChatMessage,
|
|
19
19
|
ToolCall,
|
|
20
20
|
AuthType,
|
|
21
|
-
AuthConfig,
|
|
22
|
-
AgentConfig,
|
|
23
|
-
ToolCallItem,
|
|
24
21
|
} from './types.js';
|
|
22
|
+
import type { AgentConfig, ToolCallItem } from './types.js';
|
|
25
23
|
import { createAIClient, type AIClientInterface } from './ai-client-factory.js';
|
|
26
24
|
import { detectThinkingKeywords, getThinkingTokens } from './ai-client/types.js';
|
|
27
25
|
import { TokenInvalidError } from './ai-client/types.js';
|
|
@@ -29,13 +27,13 @@ import { fetchDefaultModels } from './ai-client/providers/remote.js';
|
|
|
29
27
|
import { getConfigManager, ConfigManager } from './config.js';
|
|
30
28
|
import { AuthService, selectAuthType } from './auth.js';
|
|
31
29
|
import { getToolRegistry } from './tools.js';
|
|
32
|
-
import { getAgentManager,
|
|
30
|
+
import { getAgentManager, AgentManager } from './agents.js';
|
|
33
31
|
import { getMemoryManager, MemoryManager } from './memory.js';
|
|
34
32
|
import { getMCPManager, MCPManager } from './mcp.js';
|
|
35
33
|
import { getCheckpointManager, CheckpointManager } from './checkpoint.js';
|
|
36
34
|
import { getConversationManager, ConversationManager } from './conversation.js';
|
|
37
35
|
import { getSessionManager, SessionManager } from './session-manager.js';
|
|
38
|
-
import { SlashCommandHandler, parseInput
|
|
36
|
+
import { SlashCommandHandler, parseInput } from './slash-commands.js';
|
|
39
37
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
40
38
|
import {
|
|
41
39
|
theme,
|
|
@@ -45,7 +43,6 @@ import {
|
|
|
45
43
|
renderMarkdown,
|
|
46
44
|
renderDiff,
|
|
47
45
|
renderLines,
|
|
48
|
-
TERMINAL_BG,
|
|
49
46
|
} from './theme.js';
|
|
50
47
|
import { getCancellationManager, CancellationManager } from './cancellation.js';
|
|
51
48
|
import {
|
|
@@ -53,8 +50,9 @@ import {
|
|
|
53
50
|
ContextCompressor,
|
|
54
51
|
CompressionResult,
|
|
55
52
|
} from './context-compressor.js';
|
|
56
|
-
import {
|
|
53
|
+
import { getLogger } from './logger.js';
|
|
57
54
|
import { ensureTtySane, setupEscKeyHandler } from './terminal.js';
|
|
55
|
+
import { SdkOutputAdapter } from './sdk-output-adapter.js';
|
|
58
56
|
|
|
59
57
|
// Type aliases for backward compatibility
|
|
60
58
|
type AIClient = AIClientInterface;
|
|
@@ -86,6 +84,18 @@ export class InteractiveSession {
|
|
|
86
84
|
private currentTaskId: string | null = null;
|
|
87
85
|
private taskCompleted: boolean = false;
|
|
88
86
|
private isFirstApiCall: boolean = true;
|
|
87
|
+
private sdkOutputAdapter: SdkOutputAdapter | null = null;
|
|
88
|
+
private isSdkMode: boolean = false;
|
|
89
|
+
private sdkInputBuffer: string[] = [];
|
|
90
|
+
private resolveInput: ((value: string | null) => void) | null = null;
|
|
91
|
+
private _currentRequestId: string | null = null;
|
|
92
|
+
private heartbeatTimeout: NodeJS.Timeout | null = null;
|
|
93
|
+
private heartbeatTimeoutMs: number = 300000; // 5 minutes timeout for long AI responses
|
|
94
|
+
private lastActivityTime: number = Date.now();
|
|
95
|
+
|
|
96
|
+
// SDK response handling for approvals and questions
|
|
97
|
+
private approvalPromises: Map<string, { resolve: (approved: boolean) => void; reject: (err: Error) => void }> = new Map();
|
|
98
|
+
private questionPromises: Map<string, { resolve: (answers: string[]) => void; reject: (err: Error) => void }> = new Map();
|
|
89
99
|
|
|
90
100
|
constructor(indentLevel: number = 0) {
|
|
91
101
|
this.rl = readline.createInterface({
|
|
@@ -173,6 +183,76 @@ export class InteractiveSession {
|
|
|
173
183
|
this.executionMode = mode;
|
|
174
184
|
}
|
|
175
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Set SDK mode for programmatic access.
|
|
188
|
+
* In SDK mode, output is formatted as JSON to stdout.
|
|
189
|
+
*/
|
|
190
|
+
setSdkMode(adapter: SdkOutputAdapter): void {
|
|
191
|
+
this.isSdkMode = true;
|
|
192
|
+
this.sdkOutputAdapter = adapter;
|
|
193
|
+
|
|
194
|
+
// Initialize SDK mode for other modules using centralized output util
|
|
195
|
+
const { initOutputMode } = require('./output-util.js');
|
|
196
|
+
initOutputMode(true, adapter);
|
|
197
|
+
|
|
198
|
+
// Initialize SmartApprovalEngine in SDK mode
|
|
199
|
+
this.initSmartApprovalSdkMode(adapter).catch(() => {
|
|
200
|
+
// Silently ignore errors - not critical
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Initialize tool registry in SDK mode (fire and forget, doesn't need to await)
|
|
204
|
+
this.initToolRegistrySdkMode(adapter).catch(() => {
|
|
205
|
+
// Silently ignore errors - tool registry init is not critical
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Initialize SmartApprovalEngine in SDK mode.
|
|
211
|
+
*/
|
|
212
|
+
private async initSmartApprovalSdkMode(adapter: SdkOutputAdapter): Promise<void> {
|
|
213
|
+
const { getSmartApprovalEngine } = await import('./smart-approval.js');
|
|
214
|
+
const approvalEngine = getSmartApprovalEngine();
|
|
215
|
+
approvalEngine.setSdkMode(true, adapter);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Initialize tool registry in SDK mode.
|
|
220
|
+
*/
|
|
221
|
+
private async initToolRegistrySdkMode(adapter: SdkOutputAdapter): Promise<void> {
|
|
222
|
+
const toolRegistry = getToolRegistry();
|
|
223
|
+
await toolRegistry.setSdkMode(true, adapter);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get SDK mode status.
|
|
228
|
+
*/
|
|
229
|
+
getIsSdkMode(): boolean {
|
|
230
|
+
return this.isSdkMode;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Output assistant response - handles SDK mode and normal mode differently.
|
|
235
|
+
*/
|
|
236
|
+
private outputAssistant(content: string, reasoningContent?: string): void {
|
|
237
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
238
|
+
this.sdkOutputAdapter.outputAssistant(content, reasoningContent);
|
|
239
|
+
} else {
|
|
240
|
+
const indent = this.getIndent();
|
|
241
|
+
console.log('');
|
|
242
|
+
console.log(`${indent}${colors.primaryBright(`${icons.robot} Assistant:`)}`);
|
|
243
|
+
console.log(
|
|
244
|
+
`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
|
|
245
|
+
);
|
|
246
|
+
console.log('');
|
|
247
|
+
const renderedContent = renderMarkdown(
|
|
248
|
+
content,
|
|
249
|
+
(process.stdout.columns || 80) - indent.length * 2
|
|
250
|
+
);
|
|
251
|
+
console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
|
|
252
|
+
console.log('');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
176
256
|
/**
|
|
177
257
|
* Update system prompt to reflect MCP changes (called after add/remove MCP)
|
|
178
258
|
*/
|
|
@@ -228,6 +308,18 @@ export class InteractiveSession {
|
|
|
228
308
|
|
|
229
309
|
let lastUpdateTime = 0;
|
|
230
310
|
|
|
311
|
+
// Initialize with current state to avoid triggering update on first check
|
|
312
|
+
try {
|
|
313
|
+
const { existsSync, readFileSync } = require('fs');
|
|
314
|
+
if (existsSync(stateFilePath)) {
|
|
315
|
+
const content = readFileSync(stateFilePath, 'utf-8');
|
|
316
|
+
const state = JSON.parse(content);
|
|
317
|
+
lastUpdateTime = state.lastSkillUpdate || 0;
|
|
318
|
+
}
|
|
319
|
+
} catch {
|
|
320
|
+
// Silent fail
|
|
321
|
+
}
|
|
322
|
+
|
|
231
323
|
// Check for updates every 2 seconds
|
|
232
324
|
const checkInterval = setInterval(async () => {
|
|
233
325
|
try {
|
|
@@ -238,14 +330,16 @@ export class InteractiveSession {
|
|
|
238
330
|
|
|
239
331
|
if (state.lastSkillUpdate && state.lastSkillUpdate > lastUpdateTime) {
|
|
240
332
|
lastUpdateTime = state.lastSkillUpdate;
|
|
241
|
-
|
|
333
|
+
|
|
242
334
|
// Update system prompt with new skills
|
|
243
335
|
await this.updateSystemPrompt();
|
|
244
|
-
|
|
245
|
-
|
|
336
|
+
|
|
337
|
+
if (!this.isSdkMode) {
|
|
338
|
+
console.log(colors.textMuted(' 🔄 Skills updated from CLI'));
|
|
339
|
+
}
|
|
246
340
|
}
|
|
247
341
|
}
|
|
248
|
-
} catch
|
|
342
|
+
} catch {
|
|
249
343
|
// Silent fail - watcher is optional
|
|
250
344
|
}
|
|
251
345
|
}, 2000);
|
|
@@ -265,45 +359,56 @@ export class InteractiveSession {
|
|
|
265
359
|
// Initialize taskId for GUI operations
|
|
266
360
|
this.currentTaskId = crypto.randomUUID();
|
|
267
361
|
|
|
268
|
-
const
|
|
269
|
-
console.log('');
|
|
270
|
-
console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
|
|
271
|
-
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
272
|
-
console.log(
|
|
273
|
-
colors.gradient('║') +
|
|
274
|
-
' '.repeat(13) +
|
|
275
|
-
'🤖 ' +
|
|
276
|
-
colors.gradient('XAGENT CLI') +
|
|
277
|
-
' '.repeat(32) +
|
|
278
|
-
colors.gradient(' ║')
|
|
279
|
-
);
|
|
280
|
-
console.log(
|
|
281
|
-
colors.gradient('║') +
|
|
282
|
-
' '.repeat(16) +
|
|
283
|
-
colors.textMuted(`v${packageJson.version}`) +
|
|
284
|
-
' '.repeat(36) +
|
|
285
|
-
colors.gradient(' ║')
|
|
286
|
-
);
|
|
287
|
-
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
288
|
-
console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
|
|
289
|
-
console.log(colors.textMuted(' AI-powered command-line assistant'));
|
|
362
|
+
const _separator = icons.separator.repeat(60);
|
|
290
363
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
console.log(
|
|
364
|
+
if (!this.isSdkMode) {
|
|
365
|
+
// Normal mode: show ASCII art welcome
|
|
366
|
+
console.log('');
|
|
367
|
+
console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
|
|
368
|
+
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
369
|
+
console.log(
|
|
370
|
+
colors.gradient('║') +
|
|
371
|
+
' '.repeat(13) +
|
|
372
|
+
'🤖 ' +
|
|
373
|
+
colors.gradient('XAGENT CLI') +
|
|
374
|
+
' '.repeat(32) +
|
|
375
|
+
colors.gradient(' ║')
|
|
376
|
+
);
|
|
377
|
+
console.log(
|
|
378
|
+
colors.gradient('║') +
|
|
379
|
+
' '.repeat(16) +
|
|
380
|
+
colors.textMuted(`v${packageJson.version}`) +
|
|
381
|
+
' '.repeat(36) +
|
|
382
|
+
colors.gradient(' ║')
|
|
383
|
+
);
|
|
384
|
+
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
385
|
+
console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
|
|
386
|
+
console.log(colors.textMuted(' AI-powered command-line assistant'));
|
|
387
|
+
|
|
388
|
+
// Show initialization message if skills were initialized
|
|
389
|
+
if (initializedCount > 0) {
|
|
390
|
+
console.log(colors.textMuted(` ✨ Initialized ${initializedCount} built-in skills`));
|
|
391
|
+
}
|
|
392
|
+
console.log('');
|
|
294
393
|
}
|
|
295
|
-
console.log('');
|
|
296
394
|
|
|
297
395
|
await this.initialize();
|
|
298
396
|
this.showWelcomeMessage();
|
|
299
397
|
|
|
300
398
|
// Start watching for skill updates from CLI
|
|
301
399
|
this.startSkillUpdateWatcher();
|
|
400
|
+
|
|
401
|
+
// SDK 模式初始化:设置输出适配器
|
|
402
|
+
if (this.isSdkMode) {
|
|
403
|
+
// Start heartbeat timeout monitoring in SDK mode
|
|
404
|
+
this.startHeartbeatMonitoring();
|
|
405
|
+
}
|
|
406
|
+
|
|
302
407
|
// Set up ESC key handler using the terminal module
|
|
303
408
|
// This avoids conflicts with readline and provides clean ESC detection
|
|
304
|
-
let
|
|
409
|
+
let _escCleanup: (() => void) | undefined;
|
|
305
410
|
if (process.stdin.isTTY) {
|
|
306
|
-
|
|
411
|
+
_escCleanup = setupEscKeyHandler(() => {
|
|
307
412
|
if ((this as any)._isOperationInProgress) {
|
|
308
413
|
// An operation is running, let it be cancelled
|
|
309
414
|
this.cancellationManager.cancel();
|
|
@@ -490,31 +595,49 @@ export class InteractiveSession {
|
|
|
490
595
|
|
|
491
596
|
const mcpServers = this.configManager.getMcpServers();
|
|
492
597
|
Object.entries(mcpServers).forEach(([name, config]) => {
|
|
493
|
-
|
|
598
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
599
|
+
this.sdkOutputAdapter.outputMCPRegistering(name, config.transport || 'stdio');
|
|
600
|
+
} else {
|
|
601
|
+
console.log(`📝 Registering MCP server: ${name} (${config.transport})`);
|
|
602
|
+
}
|
|
494
603
|
this.mcpManager.registerServer(name, config);
|
|
495
604
|
});
|
|
496
605
|
|
|
497
606
|
// Eagerly connect to MCP servers to get tool definitions
|
|
498
607
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
499
608
|
try {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
609
|
+
const serverCount = Object.keys(mcpServers).length;
|
|
610
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
611
|
+
this.sdkOutputAdapter.outputMCPLoading(serverCount);
|
|
612
|
+
} else {
|
|
613
|
+
console.log(
|
|
614
|
+
`${colors.info(`${icons.brain} Connecting to ${serverCount} MCP server(s)...`)}`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
503
617
|
await this.mcpManager.connectAllServers();
|
|
504
618
|
const connectedCount = Array.from(this.mcpManager.getAllServers()).filter((s: any) =>
|
|
505
619
|
s.isServerConnected()
|
|
506
620
|
).length;
|
|
507
621
|
const mcpTools = this.mcpManager.getToolDefinitions();
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
622
|
+
|
|
623
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
624
|
+
this.sdkOutputAdapter.outputMCPConnected(serverCount, connectedCount, mcpTools.length);
|
|
625
|
+
} else {
|
|
626
|
+
console.log(
|
|
627
|
+
`${colors.success(`✓ ${connectedCount}/${serverCount} MCP server(s) connected (${mcpTools.length} tools available)`)}`
|
|
628
|
+
);
|
|
629
|
+
}
|
|
511
630
|
|
|
512
631
|
// Register MCP tools with the tool registry (hide MCP origin from LLM)
|
|
513
632
|
const toolRegistry = getToolRegistry();
|
|
514
633
|
const allMcpTools = this.mcpManager.getAllTools();
|
|
515
634
|
toolRegistry.registerMCPTools(allMcpTools);
|
|
516
635
|
} catch (error: any) {
|
|
517
|
-
|
|
636
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
637
|
+
this.sdkOutputAdapter.outputMCPConnectionFailed(error.message);
|
|
638
|
+
} else {
|
|
639
|
+
console.log(`${colors.warning(`⚠ MCP connection failed: ${error.message}`)}`);
|
|
640
|
+
}
|
|
518
641
|
}
|
|
519
642
|
}
|
|
520
643
|
|
|
@@ -530,7 +653,11 @@ export class InteractiveSession {
|
|
|
530
653
|
|
|
531
654
|
this.currentAgent = this.agentManager.getAgent('general-purpose') ?? null;
|
|
532
655
|
|
|
533
|
-
|
|
656
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
657
|
+
this.sdkOutputAdapter.outputSuccess('Initialization complete');
|
|
658
|
+
} else {
|
|
659
|
+
console.log(colors.success('✔ Initialization complete'));
|
|
660
|
+
}
|
|
534
661
|
} catch (error: any) {
|
|
535
662
|
const spinner = ora({ text: '', spinner: 'dots', color: 'red' }).start();
|
|
536
663
|
spinner.fail(colors.error(`Initialization failed: ${error.message}`));
|
|
@@ -596,7 +723,7 @@ export class InteractiveSession {
|
|
|
596
723
|
} else {
|
|
597
724
|
return null;
|
|
598
725
|
}
|
|
599
|
-
} catch
|
|
726
|
+
} catch {
|
|
600
727
|
return null;
|
|
601
728
|
}
|
|
602
729
|
}
|
|
@@ -641,10 +768,14 @@ export class InteractiveSession {
|
|
|
641
768
|
// VLM configuration is optional - only show for non-OAuth (local) mode
|
|
642
769
|
// Remote mode uses backend VLM configuration
|
|
643
770
|
if (authType !== AuthType.OAUTH_XAGENT) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
771
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
772
|
+
this.sdkOutputAdapter.outputInfo('VLM configuration is optional. You can configure it later using the /model command if needed.');
|
|
773
|
+
} else {
|
|
774
|
+
console.log('');
|
|
775
|
+
console.log(colors.info(`${icons.info} VLM configuration is optional.`));
|
|
776
|
+
console.log(colors.info(`You can configure it later using the /model command if needed.`));
|
|
777
|
+
console.log('');
|
|
778
|
+
}
|
|
648
779
|
}
|
|
649
780
|
|
|
650
781
|
this.configManager.setAuthConfig(authConfig);
|
|
@@ -665,21 +796,82 @@ export class InteractiveSession {
|
|
|
665
796
|
const language = this.configManager.getLanguage();
|
|
666
797
|
const separator = icons.separator.repeat(40);
|
|
667
798
|
|
|
668
|
-
|
|
669
|
-
|
|
799
|
+
if (!this.isSdkMode) {
|
|
800
|
+
console.log('');
|
|
801
|
+
console.log(colors.border(separator));
|
|
670
802
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
803
|
+
if (language === 'zh') {
|
|
804
|
+
console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
|
|
805
|
+
console.log(colors.textMuted('Type /help to see available commands'));
|
|
806
|
+
} else {
|
|
807
|
+
console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
|
|
808
|
+
console.log(colors.textMuted('Type /help to see available commands'));
|
|
809
|
+
}
|
|
678
810
|
|
|
679
|
-
|
|
680
|
-
|
|
811
|
+
console.log(colors.border(separator));
|
|
812
|
+
console.log('');
|
|
813
|
+
}
|
|
681
814
|
|
|
682
815
|
this.showExecutionMode();
|
|
816
|
+
|
|
817
|
+
// In SDK mode, output ready signal
|
|
818
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
819
|
+
this.sdkOutputAdapter.outputReady();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Start heartbeat timeout monitoring in SDK mode
|
|
825
|
+
*/
|
|
826
|
+
private startHeartbeatMonitoring(): void {
|
|
827
|
+
this.stopHeartbeatMonitoring();
|
|
828
|
+
|
|
829
|
+
// Check heartbeat timeout periodically
|
|
830
|
+
this.heartbeatTimeout = setInterval(() => {
|
|
831
|
+
const elapsed = Date.now() - this.lastActivityTime;
|
|
832
|
+
|
|
833
|
+
if (elapsed > this.heartbeatTimeoutMs) {
|
|
834
|
+
// Heartbeat timeout - no activity for too long
|
|
835
|
+
this.sdkOutputAdapter?.output({
|
|
836
|
+
type: 'error',
|
|
837
|
+
subtype: 'heartbeat_timeout',
|
|
838
|
+
timestamp: Date.now(),
|
|
839
|
+
data: {
|
|
840
|
+
message: 'Heartbeat timeout - no activity detected',
|
|
841
|
+
elapsed_ms: elapsed,
|
|
842
|
+
timeout_ms: this.heartbeatTimeoutMs
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
// Stop monitoring
|
|
847
|
+
this.stopHeartbeatMonitoring();
|
|
848
|
+
}
|
|
849
|
+
}, 30000); // Check every 30 seconds
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Stop heartbeat timeout monitoring
|
|
854
|
+
*/
|
|
855
|
+
private stopHeartbeatMonitoring(): void {
|
|
856
|
+
if (this.heartbeatTimeout) {
|
|
857
|
+
clearInterval(this.heartbeatTimeout);
|
|
858
|
+
this.heartbeatTimeout = null;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Reset heartbeat timeout (called on activity)
|
|
864
|
+
*/
|
|
865
|
+
private resetHeartbeatTimeout(): void {
|
|
866
|
+
this.lastActivityTime = Date.now();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Stop heartbeat monitoring (public method for cleanup)
|
|
871
|
+
* Used by SDK session to clean up when session ends
|
|
872
|
+
*/
|
|
873
|
+
public stopHeartbeatMonitor(): void {
|
|
874
|
+
this.stopHeartbeatMonitoring();
|
|
683
875
|
}
|
|
684
876
|
|
|
685
877
|
private showExecutionMode(): void {
|
|
@@ -714,10 +906,12 @@ export class InteractiveSession {
|
|
|
714
906
|
const config = modeConfig[this.executionMode];
|
|
715
907
|
const modeName = this.executionMode;
|
|
716
908
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
909
|
+
if (!this.isSdkMode) {
|
|
910
|
+
console.log(colors.textMuted(`${icons.info} Current Mode:`));
|
|
911
|
+
console.log(` ${config.color(config.icon)} ${styleHelpers.text.bold(config.color(modeName))}`);
|
|
912
|
+
console.log(` ${colors.textDim(` ${config.description}`)}`);
|
|
913
|
+
console.log('');
|
|
914
|
+
}
|
|
721
915
|
|
|
722
916
|
this.showRemoteModelInfo();
|
|
723
917
|
}
|
|
@@ -726,6 +920,21 @@ export class InteractiveSession {
|
|
|
726
920
|
const authConfig = this.configManager.getAuthConfig();
|
|
727
921
|
const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
728
922
|
|
|
923
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
924
|
+
// SDK 模式:通过 adapter 输出
|
|
925
|
+
if (isRemote) {
|
|
926
|
+
const llmModel = authConfig.remote_llmModelName || 'Not set';
|
|
927
|
+
const vlmModel = authConfig.remote_vlmModelName || 'Not set';
|
|
928
|
+
this.sdkOutputAdapter.outputInfo(`Remote Models - LLM: ${llmModel}, VLM: ${vlmModel}`);
|
|
929
|
+
} else {
|
|
930
|
+
const modelName = authConfig.modelName || 'Not set';
|
|
931
|
+
const guiSubagentModel = this.configManager.get('guiSubagentModel') || 'Not set';
|
|
932
|
+
this.sdkOutputAdapter.outputInfo(`Local Models - LLM: ${modelName}, VLM: ${guiSubagentModel}`);
|
|
933
|
+
}
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// 正常模式:控制台输出
|
|
729
938
|
if (isRemote) {
|
|
730
939
|
const llmModel = authConfig.remote_llmModelName || colors.textMuted('Not set');
|
|
731
940
|
const vlmModel = authConfig.remote_vlmModelName || colors.textMuted('Not set');
|
|
@@ -748,6 +957,12 @@ export class InteractiveSession {
|
|
|
748
957
|
return;
|
|
749
958
|
}
|
|
750
959
|
|
|
960
|
+
// In SDK mode, use a different input loop
|
|
961
|
+
if (this.isSdkMode) {
|
|
962
|
+
await this.sdkPromptLoop();
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
|
|
751
966
|
// Recreate readline interface for input
|
|
752
967
|
if (this.rl) {
|
|
753
968
|
this.rl.close();
|
|
@@ -779,12 +994,54 @@ export class InteractiveSession {
|
|
|
779
994
|
}
|
|
780
995
|
|
|
781
996
|
private async handleInput(input: string): Promise<void> {
|
|
997
|
+
// Reset heartbeat timeout on any input activity
|
|
998
|
+
this.resetHeartbeatTimeout();
|
|
999
|
+
|
|
782
1000
|
const trimmedInput = input.trim();
|
|
783
1001
|
|
|
784
1002
|
if (!trimmedInput) {
|
|
785
1003
|
return;
|
|
786
1004
|
}
|
|
787
1005
|
|
|
1006
|
+
// Check for SDK JSON message format
|
|
1007
|
+
if (this.isSdkMode) {
|
|
1008
|
+
const { isSdkMessage, parseSdkMessage } = await import('./types.js');
|
|
1009
|
+
|
|
1010
|
+
if (isSdkMessage(trimmedInput)) {
|
|
1011
|
+
const sdkMessage = parseSdkMessage(trimmedInput);
|
|
1012
|
+
|
|
1013
|
+
if (sdkMessage) {
|
|
1014
|
+
if (sdkMessage.type === 'ping') {
|
|
1015
|
+
// Handle ping - respond with pong
|
|
1016
|
+
await this.handlePing(sdkMessage);
|
|
1017
|
+
return;
|
|
1018
|
+
} else if (sdkMessage.type === 'control_request') {
|
|
1019
|
+
// Handle control request
|
|
1020
|
+
await this.handleControlRequest(sdkMessage);
|
|
1021
|
+
return;
|
|
1022
|
+
} else if (sdkMessage.type === 'user') {
|
|
1023
|
+
// Store request_id for tracking
|
|
1024
|
+
this._currentRequestId = sdkMessage.request_id || null;
|
|
1025
|
+
// Handle user message from SDK
|
|
1026
|
+
await this.processUserMessage(sdkMessage.content);
|
|
1027
|
+
return;
|
|
1028
|
+
} else if (sdkMessage.type === 'approval_response') {
|
|
1029
|
+
// Handle approval response
|
|
1030
|
+
await this.handleApprovalResponse(sdkMessage);
|
|
1031
|
+
return;
|
|
1032
|
+
} else if (sdkMessage.type === 'question_response') {
|
|
1033
|
+
// Handle question response
|
|
1034
|
+
await this.handleQuestionResponse(sdkMessage);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
} else {
|
|
1039
|
+
// Not a JSON SDK message, treat as regular text
|
|
1040
|
+
await this.processUserMessage(trimmedInput);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
788
1045
|
if (trimmedInput.startsWith('/')) {
|
|
789
1046
|
const handled = await this.slashCommandHandler.handleCommand(trimmedInput);
|
|
790
1047
|
if (handled) {
|
|
@@ -804,6 +1061,327 @@ export class InteractiveSession {
|
|
|
804
1061
|
await this.processUserMessage(trimmedInput);
|
|
805
1062
|
}
|
|
806
1063
|
|
|
1064
|
+
/**
|
|
1065
|
+
* SDK prompt loop - reads input from stdin without showing prompt
|
|
1066
|
+
*/
|
|
1067
|
+
private async sdkPromptLoop(): Promise<void> {
|
|
1068
|
+
// Read input from stdin directly without outputting prompt
|
|
1069
|
+
const input = await this.readSdkInput();
|
|
1070
|
+
|
|
1071
|
+
if ((this as any)._isShuttingDown || input === null) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
try {
|
|
1076
|
+
await this.handleInput(input);
|
|
1077
|
+
} catch (err: any) {
|
|
1078
|
+
this.sdkOutputAdapter?.output({
|
|
1079
|
+
type: 'error',
|
|
1080
|
+
subtype: 'general',
|
|
1081
|
+
timestamp: Date.now(),
|
|
1082
|
+
data: { message: err.message }
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// Continue the loop
|
|
1087
|
+
this.sdkPromptLoop();
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
private sdkRl: readline.Interface | null = null;
|
|
1091
|
+
private sdkInputProcessing: boolean = false;
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Check if a line is an SDK control message (approval_response, question_response)
|
|
1095
|
+
*/
|
|
1096
|
+
private isSdkControlMessage(line: string): boolean {
|
|
1097
|
+
try {
|
|
1098
|
+
const parsed = JSON.parse(line);
|
|
1099
|
+
return parsed.type === 'approval_response' || parsed.type === 'question_response';
|
|
1100
|
+
} catch {
|
|
1101
|
+
return false;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Process an SDK input line (returns true if processed)
|
|
1107
|
+
*/
|
|
1108
|
+
private async processSdkInputLine(line: string): Promise<boolean> {
|
|
1109
|
+
const { isSdkMessage, parseSdkMessage } = await import('./types.js');
|
|
1110
|
+
|
|
1111
|
+
if (isSdkMessage(line)) {
|
|
1112
|
+
const sdkMessage = parseSdkMessage(line);
|
|
1113
|
+
|
|
1114
|
+
if (sdkMessage) {
|
|
1115
|
+
if (sdkMessage.type === 'ping') {
|
|
1116
|
+
await this.handlePing(sdkMessage);
|
|
1117
|
+
return true;
|
|
1118
|
+
} else if (sdkMessage.type === 'control_request') {
|
|
1119
|
+
await this.handleControlRequest(sdkMessage);
|
|
1120
|
+
return true;
|
|
1121
|
+
} else if (sdkMessage.type === 'user') {
|
|
1122
|
+
this._currentRequestId = sdkMessage.request_id || null;
|
|
1123
|
+
await this.processUserMessage(sdkMessage.content);
|
|
1124
|
+
return true;
|
|
1125
|
+
} else if (sdkMessage.type === 'approval_response') {
|
|
1126
|
+
await this.handleApprovalResponse(sdkMessage);
|
|
1127
|
+
return true;
|
|
1128
|
+
} else if (sdkMessage.type === 'question_response') {
|
|
1129
|
+
await this.handleQuestionResponse(sdkMessage);
|
|
1130
|
+
return true;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Read a line of input from stdin (SDK mode)
|
|
1139
|
+
* Uses readline 'line' event for reliable stdin reading
|
|
1140
|
+
* SDK control messages (approval_response, question_response) bypass the main loop
|
|
1141
|
+
*/
|
|
1142
|
+
private readSdkInput(): Promise<string | null> {
|
|
1143
|
+
return new Promise((resolve) => {
|
|
1144
|
+
// Create readline interface if not exists
|
|
1145
|
+
if (!this.sdkRl) {
|
|
1146
|
+
this.sdkRl = readline.createInterface({
|
|
1147
|
+
input: process.stdin,
|
|
1148
|
+
crlfDelay: Infinity,
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
// Handle line events - SDK control messages bypass normal flow
|
|
1152
|
+
this.sdkRl.on('line', async (line) => {
|
|
1153
|
+
const cleanLine = line
|
|
1154
|
+
.replace(/^\uFEFF/, '')
|
|
1155
|
+
// eslint-disable-next-line no-control-regex
|
|
1156
|
+
.replace(/[\x00-\x1F\x7F-\x9F]/g, '');
|
|
1157
|
+
|
|
1158
|
+
// Check if this is an SDK control message
|
|
1159
|
+
if (this.isSdkControlMessage(cleanLine)) {
|
|
1160
|
+
// Process immediately, don't wait for main loop
|
|
1161
|
+
// We need to set a flag to prevent re-entrancy issues
|
|
1162
|
+
if (this.sdkInputProcessing) {
|
|
1163
|
+
// Already processing, queue it
|
|
1164
|
+
this.sdkInputBuffer.push(cleanLine);
|
|
1165
|
+
} else {
|
|
1166
|
+
this.sdkInputProcessing = true;
|
|
1167
|
+
try {
|
|
1168
|
+
await this.processSdkInputLine(cleanLine);
|
|
1169
|
+
} finally {
|
|
1170
|
+
this.sdkInputProcessing = false;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Regular input
|
|
1177
|
+
if (this.resolveInput) {
|
|
1178
|
+
// Immediate handler available, resolve immediately
|
|
1179
|
+
this.resolveInput(cleanLine);
|
|
1180
|
+
this.resolveInput = null;
|
|
1181
|
+
} else {
|
|
1182
|
+
// No handler available, queue the message
|
|
1183
|
+
this.sdkInputBuffer.push(cleanLine);
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
// Handle close events
|
|
1188
|
+
this.sdkRl.on('close', () => {
|
|
1189
|
+
if (this.resolveInput) {
|
|
1190
|
+
this.resolveInput(null);
|
|
1191
|
+
this.resolveInput = null;
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
// Handle errors
|
|
1196
|
+
this.sdkRl.on('error', () => {
|
|
1197
|
+
if (this.resolveInput) {
|
|
1198
|
+
this.resolveInput(null);
|
|
1199
|
+
this.resolveInput = null;
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// Check for SDK control messages in buffer first
|
|
1205
|
+
for (let i = 0; i < this.sdkInputBuffer.length; i++) {
|
|
1206
|
+
const line = this.sdkInputBuffer[i];
|
|
1207
|
+
if (this.isSdkControlMessage(line)) {
|
|
1208
|
+
this.sdkInputBuffer.splice(i, 1);
|
|
1209
|
+
resolve(line);
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
if (this.sdkInputBuffer.length > 0) {
|
|
1215
|
+
const line = this.sdkInputBuffer.shift()!;
|
|
1216
|
+
resolve(line);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
// Set up the resolve callback
|
|
1221
|
+
this.resolveInput = (value: string | null) => {
|
|
1222
|
+
resolve(value);
|
|
1223
|
+
};
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/**
|
|
1228
|
+
* Handle SDK ping messages (heartbeat)
|
|
1229
|
+
*/
|
|
1230
|
+
private async handlePing(pingMessage: any): Promise<void> {
|
|
1231
|
+
const requestId = pingMessage.request_id || `ping_${Date.now()}`;
|
|
1232
|
+
|
|
1233
|
+
// Reset activity timestamp on ping (heartbeat activity)
|
|
1234
|
+
this.lastActivityTime = Date.now();
|
|
1235
|
+
|
|
1236
|
+
// Send pong response through SDK adapter for consistency
|
|
1237
|
+
this.sdkOutputAdapter?.output({
|
|
1238
|
+
type: 'system',
|
|
1239
|
+
subtype: 'pong',
|
|
1240
|
+
timestamp: Date.now(),
|
|
1241
|
+
data: {
|
|
1242
|
+
type: 'pong',
|
|
1243
|
+
request_id: requestId,
|
|
1244
|
+
timestamp: Date.now()
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Handle SDK control requests
|
|
1251
|
+
*/
|
|
1252
|
+
private async handleControlRequest(request: any): Promise<void> {
|
|
1253
|
+
// Update activity to prevent heartbeat timeout during control requests
|
|
1254
|
+
this.lastActivityTime = Date.now();
|
|
1255
|
+
|
|
1256
|
+
const { request_id, request: req } = request;
|
|
1257
|
+
|
|
1258
|
+
switch (req.subtype) {
|
|
1259
|
+
case 'interrupt':
|
|
1260
|
+
this.sdkOutputAdapter?.outputSystem('interrupt', { request_id });
|
|
1261
|
+
(this as any)._isShuttingDown = true;
|
|
1262
|
+
process.exit(0);
|
|
1263
|
+
break;
|
|
1264
|
+
|
|
1265
|
+
case 'set_permission_mode':
|
|
1266
|
+
{
|
|
1267
|
+
const { ExecutionMode } = await import('./types.js');
|
|
1268
|
+
const modeMap: Record<string, ExecutionMode> = {
|
|
1269
|
+
'default': ExecutionMode.DEFAULT,
|
|
1270
|
+
'acceptEdits': ExecutionMode.ACCEPT_EDITS,
|
|
1271
|
+
'plan': ExecutionMode.PLAN,
|
|
1272
|
+
'bypassPermissions': ExecutionMode.YOLO,
|
|
1273
|
+
};
|
|
1274
|
+
const mode = modeMap[req.mode] || ExecutionMode.SMART;
|
|
1275
|
+
this.executionMode = mode;
|
|
1276
|
+
this.sdkOutputAdapter?.outputSystem('permission_mode_changed', {
|
|
1277
|
+
request_id,
|
|
1278
|
+
mode: req.mode
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
break;
|
|
1282
|
+
|
|
1283
|
+
case 'set_model':
|
|
1284
|
+
this.sdkOutputAdapter?.outputSystem('model_changed', {
|
|
1285
|
+
request_id,
|
|
1286
|
+
model: req.model
|
|
1287
|
+
});
|
|
1288
|
+
break;
|
|
1289
|
+
|
|
1290
|
+
default:
|
|
1291
|
+
this.sdkOutputAdapter?.outputWarning(`Unknown control request: ${req.subtype}`);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* Handle SDK approval responses
|
|
1297
|
+
*/
|
|
1298
|
+
private async handleApprovalResponse(response: {
|
|
1299
|
+
request_id: string;
|
|
1300
|
+
approved: boolean;
|
|
1301
|
+
}): Promise<void> {
|
|
1302
|
+
const { request_id, approved } = response;
|
|
1303
|
+
|
|
1304
|
+
const pending = this.approvalPromises.get(request_id);
|
|
1305
|
+
if (pending) {
|
|
1306
|
+
pending.resolve(approved);
|
|
1307
|
+
this.approvalPromises.delete(request_id);
|
|
1308
|
+
} else {
|
|
1309
|
+
this.sdkOutputAdapter?.outputWarning(`Unknown approval request ID: ${request_id}`);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* Handle SDK question responses
|
|
1315
|
+
*/
|
|
1316
|
+
private async handleQuestionResponse(response: {
|
|
1317
|
+
request_id: string;
|
|
1318
|
+
answers: string[];
|
|
1319
|
+
}): Promise<void> {
|
|
1320
|
+
const { request_id, answers } = response;
|
|
1321
|
+
|
|
1322
|
+
const pending = this.questionPromises.get(request_id);
|
|
1323
|
+
if (pending) {
|
|
1324
|
+
pending.resolve(answers);
|
|
1325
|
+
this.questionPromises.delete(request_id);
|
|
1326
|
+
} else {
|
|
1327
|
+
this.sdkOutputAdapter?.outputWarning(`Unknown question request ID: ${request_id}`);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* Wait for SDK approval response
|
|
1333
|
+
*/
|
|
1334
|
+
async waitForApprovalResponse(requestId: string, timeoutMs: number = 300000): Promise<boolean> {
|
|
1335
|
+
return new Promise((resolve, reject) => {
|
|
1336
|
+
const timeout = setTimeout(() => {
|
|
1337
|
+
const pending = this.approvalPromises.get(requestId);
|
|
1338
|
+
if (pending) {
|
|
1339
|
+
pending.reject(new Error('Approval request timeout'));
|
|
1340
|
+
this.approvalPromises.delete(requestId);
|
|
1341
|
+
}
|
|
1342
|
+
resolve(false);
|
|
1343
|
+
}, timeoutMs);
|
|
1344
|
+
|
|
1345
|
+
this.approvalPromises.set(requestId, {
|
|
1346
|
+
resolve: (approved: boolean) => {
|
|
1347
|
+
clearTimeout(timeout);
|
|
1348
|
+
resolve(approved);
|
|
1349
|
+
},
|
|
1350
|
+
reject: (err: Error) => {
|
|
1351
|
+
clearTimeout(timeout);
|
|
1352
|
+
reject(err);
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/**
|
|
1359
|
+
* Wait for SDK question response
|
|
1360
|
+
*/
|
|
1361
|
+
async waitForQuestionResponse(requestId: string, timeoutMs: number = 300000): Promise<string[]> {
|
|
1362
|
+
return new Promise((resolve, reject) => {
|
|
1363
|
+
const timeout = setTimeout(() => {
|
|
1364
|
+
const pending = this.questionPromises.get(requestId);
|
|
1365
|
+
if (pending) {
|
|
1366
|
+
pending.reject(new Error('Question request timeout'));
|
|
1367
|
+
this.questionPromises.delete(requestId);
|
|
1368
|
+
}
|
|
1369
|
+
resolve([]); // Return empty array on timeout instead of rejecting
|
|
1370
|
+
}, timeoutMs);
|
|
1371
|
+
|
|
1372
|
+
this.questionPromises.set(requestId, {
|
|
1373
|
+
resolve: (answers: string[]) => {
|
|
1374
|
+
clearTimeout(timeout);
|
|
1375
|
+
resolve(answers);
|
|
1376
|
+
},
|
|
1377
|
+
reject: (err: Error) => {
|
|
1378
|
+
clearTimeout(timeout);
|
|
1379
|
+
reject(err);
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
|
|
807
1385
|
private async handleSubAgentCommand(input: string): Promise<void> {
|
|
808
1386
|
const [agentType, ...taskParts] = input.slice(1).split(' ');
|
|
809
1387
|
const task = taskParts.join(' ');
|
|
@@ -829,8 +1407,8 @@ export class InteractiveSession {
|
|
|
829
1407
|
await this.processUserMessage(task, agent);
|
|
830
1408
|
}
|
|
831
1409
|
|
|
832
|
-
public async processUserMessage(message: string,
|
|
833
|
-
const inputs = parseInput(message);
|
|
1410
|
+
public async processUserMessage(message: string, _agent?: AgentConfig): Promise<void> {
|
|
1411
|
+
const inputs = await parseInput(message);
|
|
834
1412
|
const textInput = inputs.find((i) => i.type === 'text');
|
|
835
1413
|
const fileInputs = inputs.filter((i) => i.type === 'file');
|
|
836
1414
|
const commandInput = inputs.find((i) => i.type === 'command');
|
|
@@ -908,6 +1486,12 @@ export class InteractiveSession {
|
|
|
908
1486
|
const thinkingConfig = this.configManager.getThinkingConfig();
|
|
909
1487
|
const displayMode = thinkingConfig.displayMode || 'compact';
|
|
910
1488
|
|
|
1489
|
+
// SDK 模式:使用 adapter 输出
|
|
1490
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1491
|
+
this.sdkOutputAdapter.outputThinking(reasoningContent, displayMode);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
911
1495
|
const separator = icons.separator.repeat(
|
|
912
1496
|
Math.min(60, process.stdout.columns || 80) - indent.length
|
|
913
1497
|
);
|
|
@@ -923,7 +1507,7 @@ export class InteractiveSession {
|
|
|
923
1507
|
console.log(`${indent}${colors.textDim(reasoningContent.replace(/^/gm, indent))}`);
|
|
924
1508
|
break;
|
|
925
1509
|
|
|
926
|
-
case 'compact':
|
|
1510
|
+
case 'compact': {
|
|
927
1511
|
// Compact display, truncate partial content
|
|
928
1512
|
const maxLength = 500;
|
|
929
1513
|
const truncatedContent =
|
|
@@ -936,6 +1520,7 @@ export class InteractiveSession {
|
|
|
936
1520
|
console.log(`${indent}${colors.textDim(truncatedContent.replace(/^/gm, indent))}`);
|
|
937
1521
|
console.log(`${indent}${colors.textDim(`[${reasoningContent.length} chars total]`)}`);
|
|
938
1522
|
break;
|
|
1523
|
+
}
|
|
939
1524
|
|
|
940
1525
|
case 'indicator':
|
|
941
1526
|
// Show indicator only
|
|
@@ -966,7 +1551,7 @@ export class InteractiveSession {
|
|
|
966
1551
|
}
|
|
967
1552
|
|
|
968
1553
|
const indent = this.getIndent();
|
|
969
|
-
const
|
|
1554
|
+
const _currentTokens = this.contextCompressor.estimateContextTokens(this.conversation);
|
|
970
1555
|
const currentMessages = this.conversation.length;
|
|
971
1556
|
const { needsCompression, reason, tokenCount } = this.contextCompressor.needsCompression(
|
|
972
1557
|
this.conversation,
|
|
@@ -983,10 +1568,14 @@ export class InteractiveSession {
|
|
|
983
1568
|
const threshold = thresholdMatch ? parseInt(thresholdMatch[1], 10) : 0;
|
|
984
1569
|
const contextWindow = contextWindowMatch ? parseInt(contextWindowMatch[1], 10) : 0;
|
|
985
1570
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1571
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1572
|
+
this.sdkOutputAdapter.outputContextCompressionTriggered(reason);
|
|
1573
|
+
} else {
|
|
1574
|
+
console.log('');
|
|
1575
|
+
console.log(
|
|
1576
|
+
`${indent}${colors.success(`${icons.sparkles} Compressing context (${currentMessages} msgs, ${tokenCount.toLocaleString()} > ${threshold.toLocaleString()}/${contextWindow.toLocaleString()} tokens, ${Math.round((tokenCount / contextWindow) * 100)}% of context window)...`)}`
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
990
1579
|
|
|
991
1580
|
const toolRegistry = getToolRegistry();
|
|
992
1581
|
const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
|
|
@@ -1003,9 +1592,20 @@ export class InteractiveSession {
|
|
|
1003
1592
|
if (result.wasCompressed) {
|
|
1004
1593
|
this.conversation = result.compressedMessages;
|
|
1005
1594
|
const reductionPercent = Math.round((1 - result.compressedSize / result.originalSize) * 100);
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1595
|
+
|
|
1596
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1597
|
+
this.sdkOutputAdapter.outputContextCompressionResult(
|
|
1598
|
+
result.originalSize,
|
|
1599
|
+
result.compressedSize,
|
|
1600
|
+
reductionPercent,
|
|
1601
|
+
result.originalMessageCount,
|
|
1602
|
+
result.compressedMessageCount
|
|
1603
|
+
);
|
|
1604
|
+
} else {
|
|
1605
|
+
console.log(
|
|
1606
|
+
`${indent}${colors.success(`${icons.success} Compressed ${result.originalMessageCount} → ${result.compressedMessageCount} messages (${reductionPercent}% smaller)`)}`
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1009
1609
|
|
|
1010
1610
|
// Summary is embedded in first user message, look for it
|
|
1011
1611
|
// The format is: "[Conversation Summary - X messages compressed]\n\n${summary}"
|
|
@@ -1034,27 +1634,36 @@ export class InteractiveSession {
|
|
|
1034
1634
|
summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
|
|
1035
1635
|
}
|
|
1036
1636
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
summaryContent,
|
|
1047
|
-
(process.stdout.columns || 80) - indent.length * 4
|
|
1048
|
-
);
|
|
1049
|
-
console.log(
|
|
1050
|
-
`${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`
|
|
1051
|
-
);
|
|
1052
|
-
if (isTruncated) {
|
|
1637
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1638
|
+
this.sdkOutputAdapter.outputContextCompressionSummary(
|
|
1639
|
+
'Conversation compressed successfully',
|
|
1640
|
+
summaryContent,
|
|
1641
|
+
isTruncated,
|
|
1642
|
+
summaryMessage.content.length
|
|
1643
|
+
);
|
|
1644
|
+
} else {
|
|
1645
|
+
console.log('');
|
|
1053
1646
|
console.log(
|
|
1054
|
-
`${indent}${
|
|
1647
|
+
`${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`
|
|
1055
1648
|
);
|
|
1649
|
+
const separator = icons.separator.repeat(
|
|
1650
|
+
Math.min(60, process.stdout.columns || 80) - indent.length * 2
|
|
1651
|
+
);
|
|
1652
|
+
console.log(`${indent}${colors.border(separator)}`);
|
|
1653
|
+
const renderedSummary = renderMarkdown(
|
|
1654
|
+
summaryContent,
|
|
1655
|
+
(process.stdout.columns || 80) - indent.length * 4
|
|
1656
|
+
);
|
|
1657
|
+
console.log(
|
|
1658
|
+
`${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`
|
|
1659
|
+
);
|
|
1660
|
+
if (isTruncated) {
|
|
1661
|
+
console.log(
|
|
1662
|
+
`${indent}${colors.textMuted(`(... ${summaryMessage.content.length - maxPreviewLength} more chars hidden)`)}`
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
console.log(`${indent}${colors.border(separator)}`);
|
|
1056
1666
|
}
|
|
1057
|
-
console.log(`${indent}${colors.border(separator)}`);
|
|
1058
1667
|
}
|
|
1059
1668
|
|
|
1060
1669
|
// Sync compressed conversation history to slashCommandHandler
|
|
@@ -1135,7 +1744,7 @@ export class InteractiveSession {
|
|
|
1135
1744
|
/**
|
|
1136
1745
|
* Create remote mode LLM caller
|
|
1137
1746
|
*/
|
|
1138
|
-
private createRemoteCaller(taskId: string,
|
|
1747
|
+
private createRemoteCaller(taskId: string, _status: 'begin' | 'continue') {
|
|
1139
1748
|
const client = this.remoteAIClient!;
|
|
1140
1749
|
|
|
1141
1750
|
|
|
@@ -1195,17 +1804,25 @@ export class InteractiveSession {
|
|
|
1195
1804
|
// Mark that an operation is in progress
|
|
1196
1805
|
(this as any)._isOperationInProgress = true;
|
|
1197
1806
|
|
|
1198
|
-
const indent = this.getIndent();
|
|
1199
1807
|
const thinkingText = colors.textMuted(`Thinking... (Press ESC to cancel)`);
|
|
1200
1808
|
const icon = colors.primary(icons.brain);
|
|
1201
1809
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
1202
1810
|
let frameIndex = 0;
|
|
1203
1811
|
|
|
1812
|
+
// SDK 模式下不显示 spinner
|
|
1813
|
+
const showThinkingSpinner = !this.isSdkMode;
|
|
1814
|
+
let spinnerInterval: NodeJS.Timeout | null = null;
|
|
1815
|
+
|
|
1204
1816
|
// Custom spinner: only icon rotates, text stays static
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1817
|
+
if (showThinkingSpinner) {
|
|
1818
|
+
spinnerInterval = setInterval(() => {
|
|
1819
|
+
process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${icon} ${thinkingText}`);
|
|
1820
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
1821
|
+
}, 120);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
let content = '';
|
|
1825
|
+
let reasoningContent = '';
|
|
1209
1826
|
|
|
1210
1827
|
try {
|
|
1211
1828
|
const memory = await this.memoryManager.loadMemory();
|
|
@@ -1257,30 +1874,20 @@ export class InteractiveSession {
|
|
|
1257
1874
|
// Mark that first API call is complete
|
|
1258
1875
|
this.isFirstApiCall = false;
|
|
1259
1876
|
|
|
1260
|
-
clearInterval(spinnerInterval);
|
|
1877
|
+
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
1261
1878
|
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r'); // Clear spinner line
|
|
1262
1879
|
|
|
1263
1880
|
const assistantMessage = response.choices[0].message;
|
|
1264
1881
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1882
|
+
content = typeof assistantMessage.content === 'string' ? assistantMessage.content : '';
|
|
1883
|
+
reasoningContent = assistantMessage.reasoning_content || '';
|
|
1267
1884
|
// Display reasoning content if available and thinking mode is enabled
|
|
1268
1885
|
if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
|
|
1269
1886
|
this.displayThinkingContent(reasoningContent);
|
|
1270
1887
|
}
|
|
1271
1888
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
console.log(
|
|
1275
|
-
`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
|
|
1276
|
-
);
|
|
1277
|
-
console.log('');
|
|
1278
|
-
const renderedContent = renderMarkdown(
|
|
1279
|
-
content,
|
|
1280
|
-
(process.stdout.columns || 80) - indent.length * 2
|
|
1281
|
-
);
|
|
1282
|
-
console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
|
|
1283
|
-
console.log('');
|
|
1889
|
+
// Output assistant response
|
|
1890
|
+
this.outputAssistant(content, reasoningContent);
|
|
1284
1891
|
|
|
1285
1892
|
this.conversation.push({
|
|
1286
1893
|
role: 'assistant',
|
|
@@ -1313,15 +1920,28 @@ export class InteractiveSession {
|
|
|
1313
1920
|
);
|
|
1314
1921
|
}
|
|
1315
1922
|
|
|
1923
|
+
// Signal request completion to SDK (no tools to execute)
|
|
1924
|
+
if (this.isSdkMode && this.sdkOutputAdapter && this._currentRequestId) {
|
|
1925
|
+
this.sdkOutputAdapter.outputRequestDone(this._currentRequestId, 'success');
|
|
1926
|
+
this._currentRequestId = null;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1316
1929
|
// Operation completed successfully, clear the flag
|
|
1317
1930
|
(this as any)._isOperationInProgress = false;
|
|
1318
1931
|
} catch (error: any) {
|
|
1319
|
-
clearInterval(spinnerInterval);
|
|
1932
|
+
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
1320
1933
|
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
|
|
1321
1934
|
|
|
1322
1935
|
// Clear the operation flag
|
|
1323
1936
|
(this as any)._isOperationInProgress = false;
|
|
1324
1937
|
|
|
1938
|
+
// Signal request completion to SDK
|
|
1939
|
+
if (this.isSdkMode && this.sdkOutputAdapter && this._currentRequestId) {
|
|
1940
|
+
const status = error.message === 'Operation cancelled by user' ? 'cancelled' : 'error';
|
|
1941
|
+
this.sdkOutputAdapter.outputRequestDone(this._currentRequestId, status);
|
|
1942
|
+
this._currentRequestId = null;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1325
1945
|
if (error.message === 'Operation cancelled by user') {
|
|
1326
1946
|
// Notify backend to cancel the task
|
|
1327
1947
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
@@ -1423,7 +2043,7 @@ export class InteractiveSession {
|
|
|
1423
2043
|
const authConfig = this.configManager.getAuthConfig();
|
|
1424
2044
|
|
|
1425
2045
|
logger.debug('[DEBUG generateRemoteResponse] After re-auth:');
|
|
1426
|
-
logger.debug(' - authConfig.apiKey exists:',
|
|
2046
|
+
logger.debug(' - authConfig.apiKey exists:', authConfig.apiKey ? 'true' : 'false');
|
|
1427
2047
|
|
|
1428
2048
|
// Recreate readline interface after interactive prompt
|
|
1429
2049
|
this.rl.close();
|
|
@@ -1491,7 +2111,7 @@ export class InteractiveSession {
|
|
|
1491
2111
|
let parsedParams: any;
|
|
1492
2112
|
try {
|
|
1493
2113
|
parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
|
|
1494
|
-
} catch
|
|
2114
|
+
} catch {
|
|
1495
2115
|
parsedParams = params;
|
|
1496
2116
|
}
|
|
1497
2117
|
|
|
@@ -1500,13 +2120,19 @@ export class InteractiveSession {
|
|
|
1500
2120
|
|
|
1501
2121
|
// Display all tool calls info
|
|
1502
2122
|
for (const { name, params } of preparedToolCalls) {
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
console.log(`${indent}${colors.textDim(JSON.stringify(params, null, 2))}`);
|
|
2123
|
+
// SDK mode: use adapter output
|
|
2124
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
2125
|
+
this.sdkOutputAdapter.outputToolStart(name, params);
|
|
1507
2126
|
} else {
|
|
1508
|
-
|
|
1509
|
-
|
|
2127
|
+
// Normal mode: console output
|
|
2128
|
+
if (showToolDetails) {
|
|
2129
|
+
console.log('');
|
|
2130
|
+
console.log(`${indent}${colors.warning(`${icons.tool} Tool Call: ${name}`)}`);
|
|
2131
|
+
console.log(`${indent}${colors.textDim(JSON.stringify(params, null, 2))}`);
|
|
2132
|
+
} else {
|
|
2133
|
+
const toolDescription = this.getToolDescription(name, params);
|
|
2134
|
+
console.log(`${indent}${colors.textMuted(`${icons.loading} ${toolDescription}`)}`);
|
|
2135
|
+
}
|
|
1510
2136
|
}
|
|
1511
2137
|
}
|
|
1512
2138
|
|
|
@@ -1532,7 +2158,7 @@ export class InteractiveSession {
|
|
|
1532
2158
|
}
|
|
1533
2159
|
|
|
1534
2160
|
// Process results in the original tool_calls order (critical for Anthropic format APIs)
|
|
1535
|
-
let
|
|
2161
|
+
let _hasError = false;
|
|
1536
2162
|
for (let i = 0; i < preparedToolCalls.length; i++) {
|
|
1537
2163
|
const toolCall = preparedToolCalls[i];
|
|
1538
2164
|
const { name: tool, params } = toolCall;
|
|
@@ -1555,10 +2181,16 @@ export class InteractiveSession {
|
|
|
1555
2181
|
return;
|
|
1556
2182
|
}
|
|
1557
2183
|
|
|
1558
|
-
|
|
2184
|
+
_hasError = true;
|
|
1559
2185
|
|
|
1560
|
-
|
|
1561
|
-
|
|
2186
|
+
// SDK mode: use adapter output
|
|
2187
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
2188
|
+
this.sdkOutputAdapter.outputToolError(tool, error);
|
|
2189
|
+
} else {
|
|
2190
|
+
// Normal mode: console output
|
|
2191
|
+
console.log('');
|
|
2192
|
+
console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${tool} - ${error}`)}`);
|
|
2193
|
+
}
|
|
1562
2194
|
|
|
1563
2195
|
// Add detailed error info including tool name and params for AI understanding and correction
|
|
1564
2196
|
this.conversation.push({
|
|
@@ -1572,200 +2204,208 @@ export class InteractiveSession {
|
|
|
1572
2204
|
timestamp: Date.now(),
|
|
1573
2205
|
});
|
|
1574
2206
|
} else {
|
|
2207
|
+
// SDK mode: output tool result via adapter
|
|
2208
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
2209
|
+
this.sdkOutputAdapter.outputToolResult(tool, result);
|
|
2210
|
+
}
|
|
2211
|
+
|
|
1575
2212
|
// Use correct indent for gui-subagent tasks
|
|
1576
2213
|
const isGuiSubagent = tool === 'task' && params?.subagent_type === 'gui-subagent';
|
|
1577
2214
|
const displayIndent = isGuiSubagent ? indent + ' ' : indent;
|
|
1578
2215
|
|
|
1579
|
-
//
|
|
1580
|
-
|
|
2216
|
+
// Normal mode: console output (SDK mode already output via adapter above)
|
|
2217
|
+
if (!this.isSdkMode || !this.sdkOutputAdapter) {
|
|
2218
|
+
// Always show details for todo tools so users can see their task lists
|
|
2219
|
+
const isTodoTool = tool === 'todo_write' || tool === 'todo_read';
|
|
1581
2220
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
2221
|
+
// Special handling for edit tool with diff
|
|
2222
|
+
const isEditTool = tool === 'Edit';
|
|
2223
|
+
const hasDiff = isEditTool && result?.diff;
|
|
1585
2224
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
2225
|
+
// Special handling for Write tool with file preview
|
|
2226
|
+
const isWriteTool = tool === 'Write';
|
|
2227
|
+
const hasFilePreview = isWriteTool && result?.preview;
|
|
1589
2228
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
2229
|
+
// Special handling for DeleteFile tool
|
|
2230
|
+
const isDeleteTool = tool === 'DeleteFile';
|
|
2231
|
+
const hasDeleteInfo = isDeleteTool && result?.filePath;
|
|
1593
2232
|
|
|
1594
|
-
|
|
1595
|
-
|
|
2233
|
+
// Special handling for task tool (subagent)
|
|
2234
|
+
const isTaskTool = tool === 'task' && params?.subagent_type;
|
|
1596
2235
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
}
|
|
1611
|
-
} else if (hasDiff) {
|
|
1612
|
-
// Show edit result with diff
|
|
1613
|
-
console.log('');
|
|
1614
|
-
const diffOutput = renderDiff(result.diff);
|
|
1615
|
-
const indentedDiff = diffOutput
|
|
1616
|
-
.split('\n')
|
|
1617
|
-
.map((line) => `${displayIndent} ${line}`)
|
|
1618
|
-
.join('\n');
|
|
1619
|
-
console.log(`${indentedDiff}`);
|
|
1620
|
-
} else if (hasFilePreview) {
|
|
1621
|
-
// Show new file content in diff-like style
|
|
1622
|
-
console.log('');
|
|
1623
|
-
console.log(`${displayIndent}${colors.success(`${icons.file} ${result.filePath}`)}`);
|
|
1624
|
-
console.log(`${displayIndent}${colors.textDim(` ${result.lineCount} lines`)}`);
|
|
1625
|
-
console.log('');
|
|
1626
|
-
console.log(renderLines(result.preview, { maxLines: 10, indent: displayIndent + ' ' }));
|
|
1627
|
-
} else if (hasDeleteInfo) {
|
|
1628
|
-
// Show DeleteFile result
|
|
1629
|
-
console.log('');
|
|
1630
|
-
console.log(
|
|
1631
|
-
`${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`
|
|
1632
|
-
);
|
|
1633
|
-
} else if (isTaskTool) {
|
|
1634
|
-
// Special handling for task tool (subagent) - show friendly summary
|
|
1635
|
-
console.log('');
|
|
1636
|
-
const subagentType = params.subagent_type;
|
|
1637
|
-
const subagentName =
|
|
1638
|
-
params.description ||
|
|
1639
|
-
(params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
|
|
1640
|
-
|
|
1641
|
-
if (result?.success) {
|
|
1642
|
-
console.log(
|
|
1643
|
-
`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`
|
|
1644
|
-
);
|
|
1645
|
-
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1646
|
-
if (result.message) {
|
|
1647
|
-
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
2236
|
+
// Check if tool is an MCP wrapper tool by looking up in tool registry
|
|
2237
|
+
const { getToolRegistry } = await import('./tools.js');
|
|
2238
|
+
const toolRegistry = getToolRegistry();
|
|
2239
|
+
const toolDef = toolRegistry.get(tool);
|
|
2240
|
+
const isMcpTool = toolDef && (toolDef as any)._isMcpTool === true;
|
|
2241
|
+
|
|
2242
|
+
if (isTodoTool) {
|
|
2243
|
+
console.log('');
|
|
2244
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
2245
|
+
console.log(this.renderTodoList(result?.todos || [], displayIndent));
|
|
2246
|
+
// Show summary if available
|
|
2247
|
+
if (result?.message) {
|
|
2248
|
+
console.log(`${displayIndent}${colors.textDim(result.message)}`);
|
|
1648
2249
|
}
|
|
1649
|
-
} else if (
|
|
2250
|
+
} else if (hasDiff) {
|
|
2251
|
+
// Show edit result with diff
|
|
2252
|
+
console.log('');
|
|
2253
|
+
const diffOutput = renderDiff(result.diff);
|
|
2254
|
+
const indentedDiff = diffOutput
|
|
2255
|
+
.split('\n')
|
|
2256
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
2257
|
+
.join('\n');
|
|
2258
|
+
console.log(`${indentedDiff}`);
|
|
2259
|
+
} else if (hasFilePreview) {
|
|
2260
|
+
// Show new file content in diff-like style
|
|
2261
|
+
console.log('');
|
|
2262
|
+
console.log(`${displayIndent}${colors.success(`${icons.file} ${result.filePath}`)}`);
|
|
2263
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.lineCount} lines`)}`);
|
|
2264
|
+
console.log('');
|
|
2265
|
+
console.log(renderLines(result.preview, { maxLines: 10, indent: displayIndent + ' ' }));
|
|
2266
|
+
} else if (hasDeleteInfo) {
|
|
2267
|
+
// Show DeleteFile result
|
|
2268
|
+
console.log('');
|
|
1650
2269
|
console.log(
|
|
1651
|
-
`${displayIndent}${colors.
|
|
2270
|
+
`${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`
|
|
1652
2271
|
);
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
console.log(
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
2272
|
+
} else if (isTaskTool) {
|
|
2273
|
+
// Special handling for task tool (subagent) - show friendly summary
|
|
2274
|
+
console.log('');
|
|
2275
|
+
const subagentType = params.subagent_type;
|
|
2276
|
+
const subagentName =
|
|
2277
|
+
params.description ||
|
|
2278
|
+
(params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
|
|
2279
|
+
|
|
2280
|
+
if (result?.success) {
|
|
2281
|
+
console.log(
|
|
2282
|
+
`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`
|
|
2283
|
+
);
|
|
2284
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
2285
|
+
if (result.message) {
|
|
2286
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
2287
|
+
}
|
|
2288
|
+
} else if (result?.cancelled) {
|
|
2289
|
+
console.log(
|
|
2290
|
+
`${displayIndent}${colors.warning(`${icons.cross} ${subagentType}: Cancelled`)}`
|
|
2291
|
+
);
|
|
2292
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
2293
|
+
} else {
|
|
2294
|
+
console.log(
|
|
2295
|
+
`${displayIndent}${colors.error(`${icons.cross} ${subagentType}: Failed`)}`
|
|
2296
|
+
);
|
|
2297
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
2298
|
+
if (result?.message) {
|
|
2299
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
} else if (isMcpTool) {
|
|
2303
|
+
// Special handling for MCP tools - show friendly summary
|
|
2304
|
+
console.log('');
|
|
2305
|
+
// Extract server name and tool name from tool name (format: serverName__toolName)
|
|
2306
|
+
let serverName = 'MCP';
|
|
2307
|
+
let toolDisplayName = tool;
|
|
2308
|
+
if (tool.includes('__')) {
|
|
2309
|
+
const parts = tool.split('__');
|
|
2310
|
+
serverName = parts[0];
|
|
2311
|
+
toolDisplayName = parts.slice(1).join('__');
|
|
1661
2312
|
}
|
|
1662
|
-
}
|
|
1663
|
-
} else if (isMcpTool) {
|
|
1664
|
-
// Special handling for MCP tools - show friendly summary
|
|
1665
|
-
console.log('');
|
|
1666
|
-
// Extract server name and tool name from tool name (format: serverName__toolName)
|
|
1667
|
-
let serverName = 'MCP';
|
|
1668
|
-
let toolDisplayName = tool;
|
|
1669
|
-
if (tool.includes('__')) {
|
|
1670
|
-
const parts = tool.split('__');
|
|
1671
|
-
serverName = parts[0];
|
|
1672
|
-
toolDisplayName = parts.slice(1).join('__');
|
|
1673
|
-
}
|
|
1674
2313
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
2314
|
+
// Try to extract meaningful content from MCP result
|
|
2315
|
+
let summary = '';
|
|
2316
|
+
if (result?.content && Array.isArray(result.content) && result.content.length > 0) {
|
|
2317
|
+
const firstBlock = result.content[0];
|
|
2318
|
+
if (firstBlock?.type === 'text' && firstBlock?.text) {
|
|
2319
|
+
const text = firstBlock.text;
|
|
2320
|
+
if (typeof text === 'string') {
|
|
2321
|
+
// Detect HTML content
|
|
2322
|
+
if (text.trim().startsWith('<!DOCTYPE') || text.trim().startsWith('<html')) {
|
|
2323
|
+
summary = '[HTML content fetched]';
|
|
2324
|
+
} else {
|
|
2325
|
+
// Try to parse if it's JSON
|
|
2326
|
+
try {
|
|
2327
|
+
const parsed = JSON.parse(text);
|
|
2328
|
+
if (Array.isArray(parsed) && parsed.length > 0 && parsed[0]?.title) {
|
|
2329
|
+
// Search results format
|
|
2330
|
+
summary = `Found ${parsed.length} result(s)`;
|
|
2331
|
+
} else if (parsed?.message) {
|
|
2332
|
+
summary = parsed.message;
|
|
2333
|
+
} else if (typeof parsed === 'string') {
|
|
2334
|
+
summary = parsed.substring(0, 100);
|
|
2335
|
+
}
|
|
2336
|
+
} catch {
|
|
2337
|
+
// Not JSON, use as-is with truncation
|
|
2338
|
+
summary = text.substring(0, 100);
|
|
1696
2339
|
}
|
|
1697
|
-
} catch {
|
|
1698
|
-
// Not JSON, use as-is with truncation
|
|
1699
|
-
summary = text.substring(0, 100);
|
|
1700
2340
|
}
|
|
1701
2341
|
}
|
|
1702
2342
|
}
|
|
2343
|
+
} else if (result?.message) {
|
|
2344
|
+
summary = result.message;
|
|
1703
2345
|
}
|
|
1704
|
-
} else if (result?.message) {
|
|
1705
|
-
summary = result.message;
|
|
1706
|
-
}
|
|
1707
2346
|
|
|
1708
|
-
|
|
1709
|
-
console.log(
|
|
1710
|
-
`${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`
|
|
1711
|
-
);
|
|
1712
|
-
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1713
|
-
if (summary) {
|
|
1714
|
-
console.log(`${displayIndent}${colors.textDim(` ${summary}`)}`);
|
|
1715
|
-
}
|
|
1716
|
-
} else {
|
|
1717
|
-
console.log(`${displayIndent}${colors.error(`${icons.cross} ${serverName}: Failed`)}`);
|
|
1718
|
-
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1719
|
-
if (result?.message || result?.error) {
|
|
2347
|
+
if (result?.success !== false) {
|
|
1720
2348
|
console.log(
|
|
1721
|
-
`${displayIndent}${colors.
|
|
2349
|
+
`${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`
|
|
1722
2350
|
);
|
|
2351
|
+
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
2352
|
+
if (summary) {
|
|
2353
|
+
console.log(`${displayIndent}${colors.textDim(` ${summary}`)}`);
|
|
2354
|
+
}
|
|
2355
|
+
} else {
|
|
2356
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} ${serverName}: Failed`)}`);
|
|
2357
|
+
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
2358
|
+
if (result?.message || result?.error) {
|
|
2359
|
+
console.log(
|
|
2360
|
+
`${displayIndent}${colors.textDim(` ${result?.message || result?.error}`)}`
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
1723
2363
|
}
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2364
|
+
} else if (tool === 'InvokeSkill') {
|
|
2365
|
+
// Special handling for InvokeSkill - show friendly summary
|
|
2366
|
+
console.log('');
|
|
2367
|
+
const skillName = params?.skillId || 'Unknown skill';
|
|
2368
|
+
const taskDesc = params?.taskDescription || '';
|
|
2369
|
+
|
|
2370
|
+
if (result?.success) {
|
|
2371
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Skill: Completed`)}`);
|
|
2372
|
+
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
2373
|
+
if (taskDesc) {
|
|
2374
|
+
const truncatedTask =
|
|
2375
|
+
taskDesc.length > 60 ? taskDesc.substring(0, 60) + '...' : taskDesc;
|
|
2376
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${truncatedTask}`)}`);
|
|
2377
|
+
}
|
|
2378
|
+
} else {
|
|
2379
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} Skill: Failed`)}`);
|
|
2380
|
+
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
2381
|
+
if (result?.message) {
|
|
2382
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
2383
|
+
}
|
|
1738
2384
|
}
|
|
2385
|
+
} else if (showToolDetails) {
|
|
2386
|
+
console.log('');
|
|
2387
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
|
|
2388
|
+
console.log(`${displayIndent}${colors.textDim(JSON.stringify(result, null, 2))}`);
|
|
2389
|
+
} else if (result && result.success === false) {
|
|
2390
|
+
// GUI task or other tool failed
|
|
2391
|
+
console.log(
|
|
2392
|
+
`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`
|
|
2393
|
+
);
|
|
2394
|
+
} else if (result) {
|
|
2395
|
+
// Show brief preview by default (consistent with subagent behavior)
|
|
2396
|
+
const resultPreview =
|
|
2397
|
+
typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
2398
|
+
const truncatedPreview =
|
|
2399
|
+
resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
2400
|
+
// Indent the preview
|
|
2401
|
+
const indentedPreview = truncatedPreview
|
|
2402
|
+
.split('\n')
|
|
2403
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
2404
|
+
.join('\n');
|
|
2405
|
+
console.log(`${indentedPreview}`);
|
|
1739
2406
|
} else {
|
|
1740
|
-
console.log(`${displayIndent}${colors.
|
|
1741
|
-
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
1742
|
-
if (result?.message) {
|
|
1743
|
-
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1744
|
-
}
|
|
2407
|
+
console.log(`${displayIndent}${colors.textDim('(no result)')}`);
|
|
1745
2408
|
}
|
|
1746
|
-
} else if (showToolDetails) {
|
|
1747
|
-
console.log('');
|
|
1748
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
|
|
1749
|
-
console.log(`${displayIndent}${colors.textDim(JSON.stringify(result, null, 2))}`);
|
|
1750
|
-
} else if (result && result.success === false) {
|
|
1751
|
-
// GUI task or other tool failed
|
|
1752
|
-
console.log(
|
|
1753
|
-
`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`
|
|
1754
|
-
);
|
|
1755
|
-
} else if (result) {
|
|
1756
|
-
// Show brief preview by default (consistent with subagent behavior)
|
|
1757
|
-
const resultPreview =
|
|
1758
|
-
typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1759
|
-
const truncatedPreview =
|
|
1760
|
-
resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1761
|
-
// Indent the preview
|
|
1762
|
-
const indentedPreview = truncatedPreview
|
|
1763
|
-
.split('\n')
|
|
1764
|
-
.map((line) => `${displayIndent} ${line}`)
|
|
1765
|
-
.join('\n');
|
|
1766
|
-
console.log(`${indentedPreview}`);
|
|
1767
|
-
} else {
|
|
1768
|
-
console.log(`${displayIndent}${colors.textDim('(no result)')}`);
|
|
1769
2409
|
}
|
|
1770
2410
|
|
|
1771
2411
|
const toolCallRecord: ToolCall = {
|
|
@@ -2188,7 +2828,7 @@ async function initializeSkillsOnDemand(): Promise<number> {
|
|
|
2188
2828
|
}
|
|
2189
2829
|
|
|
2190
2830
|
// Synchronous version (kept for backwards compatibility)
|
|
2191
|
-
function
|
|
2831
|
+
function _copyDirectoryRecursive(src: string, dest: string): void {
|
|
2192
2832
|
if (!fs.existsSync(dest)) {
|
|
2193
2833
|
fs.mkdirSync(dest, { recursive: true });
|
|
2194
2834
|
}
|
|
@@ -2199,7 +2839,7 @@ function copyDirectoryRecursive(src: string, dest: string): void {
|
|
|
2199
2839
|
const destPath = path.join(dest, entry.name);
|
|
2200
2840
|
|
|
2201
2841
|
if (entry.isDirectory()) {
|
|
2202
|
-
|
|
2842
|
+
_copyDirectoryRecursive(srcPath, destPath);
|
|
2203
2843
|
} else if (entry.isFile()) {
|
|
2204
2844
|
fs.copyFileSync(srcPath, destPath);
|
|
2205
2845
|
}
|
|
@@ -2279,7 +2919,7 @@ export async function startInteractiveSession(): Promise<void> {
|
|
|
2279
2919
|
try {
|
|
2280
2920
|
const { checkUpdatesOnStartup } = await import('./update.js');
|
|
2281
2921
|
await checkUpdatesOnStartup();
|
|
2282
|
-
} catch
|
|
2922
|
+
} catch {
|
|
2283
2923
|
// Silently ignore update check failures
|
|
2284
2924
|
}
|
|
2285
2925
|
|