tabby-ai-assistant 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +3 -3
  3. package/src/components/chat/ai-sidebar.component.scss +43 -0
  4. package/src/components/chat/ai-sidebar.component.ts +43 -1
  5. package/src/components/settings/ai-settings-tab.component.html +13 -5
  6. package/src/components/settings/ai-settings-tab.component.scss +164 -33
  7. package/src/components/settings/ai-settings-tab.component.ts +1 -0
  8. package/src/components/settings/context-settings.component.html +87 -0
  9. package/src/components/settings/context-settings.component.scss +133 -0
  10. package/src/components/settings/context-settings.component.ts +91 -0
  11. package/src/components/settings/provider-config.component.html +46 -12
  12. package/src/components/settings/provider-config.component.scss +59 -0
  13. package/src/components/settings/provider-config.component.ts +112 -15
  14. package/src/i18n/translations/en-US.ts +27 -1
  15. package/src/i18n/translations/ja-JP.ts +27 -1
  16. package/src/i18n/translations/zh-CN.ts +27 -1
  17. package/src/i18n/types.ts +28 -0
  18. package/src/index.ts +6 -0
  19. package/src/services/chat/chat-session.service.ts +91 -2
  20. package/src/services/context/manager.ts +33 -2
  21. package/src/services/core/ai-assistant.service.ts +35 -11
  22. package/src/services/core/config-provider.service.ts +66 -0
  23. package/src/services/core/toast.service.ts +36 -0
  24. package/src/services/providers/anthropic-provider.service.ts +12 -4
  25. package/src/services/providers/glm-provider.service.ts +12 -4
  26. package/src/services/providers/minimax-provider.service.ts +12 -4
  27. package/src/types/ai.types.ts +3 -3
  28. package/src/types/provider.types.ts +1 -0
  29. package/dist/components/chat/ai-sidebar.component.d.ts +0 -160
  30. package/dist/components/chat/chat-input.component.d.ts +0 -69
  31. package/dist/components/chat/chat-interface.component.d.ts +0 -124
  32. package/dist/components/chat/chat-message.component.d.ts +0 -53
  33. package/dist/components/chat/chat-settings.component.d.ts +0 -72
  34. package/dist/components/common/error-message.component.d.ts +0 -11
  35. package/dist/components/common/loading-spinner.component.d.ts +0 -4
  36. package/dist/components/security/consent-dialog.component.d.ts +0 -11
  37. package/dist/components/security/password-prompt.component.d.ts +0 -10
  38. package/dist/components/security/risk-confirm-dialog.component.d.ts +0 -36
  39. package/dist/components/settings/ai-settings-tab.component.d.ts +0 -82
  40. package/dist/components/settings/general-settings.component.d.ts +0 -94
  41. package/dist/components/settings/provider-config.component.d.ts +0 -273
  42. package/dist/components/settings/security-settings.component.d.ts +0 -33
  43. package/dist/components/terminal/ai-toolbar-button.component.d.ts +0 -10
  44. package/dist/components/terminal/command-preview.component.d.ts +0 -53
  45. package/dist/components/terminal/command-suggestion.component.d.ts +0 -16
  46. package/dist/i18n/index.d.ts +0 -48
  47. package/dist/i18n/translations/en-US.d.ts +0 -5
  48. package/dist/i18n/translations/ja-JP.d.ts +0 -5
  49. package/dist/i18n/translations/zh-CN.d.ts +0 -5
  50. package/dist/i18n/types.d.ts +0 -198
  51. package/dist/index-full.d.ts +0 -8
  52. package/dist/index-minimal.d.ts +0 -3
  53. package/dist/index.d.ts +0 -12
  54. package/dist/main.d.ts +0 -8
  55. package/dist/providers/tabby/ai-config.provider.d.ts +0 -70
  56. package/dist/providers/tabby/ai-hotkey.provider.d.ts +0 -15
  57. package/dist/providers/tabby/ai-settings-tab.provider.d.ts +0 -11
  58. package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +0 -16
  59. package/dist/services/chat/ai-sidebar.service.d.ts +0 -111
  60. package/dist/services/chat/chat-history.service.d.ts +0 -145
  61. package/dist/services/chat/chat-session.service.d.ts +0 -113
  62. package/dist/services/chat/command-generator.service.d.ts +0 -49
  63. package/dist/services/context/compaction.d.ts +0 -90
  64. package/dist/services/context/manager.d.ts +0 -69
  65. package/dist/services/context/memory.d.ts +0 -116
  66. package/dist/services/context/token-budget.d.ts +0 -105
  67. package/dist/services/core/ai-assistant.service.d.ts +0 -127
  68. package/dist/services/core/ai-provider-manager.service.d.ts +0 -119
  69. package/dist/services/core/checkpoint.service.d.ts +0 -130
  70. package/dist/services/core/config-provider.service.d.ts +0 -137
  71. package/dist/services/core/logger.service.d.ts +0 -21
  72. package/dist/services/core/theme.service.d.ts +0 -53
  73. package/dist/services/platform/escape-sequence.service.d.ts +0 -132
  74. package/dist/services/platform/platform-detection.service.d.ts +0 -146
  75. package/dist/services/providers/anthropic-provider.service.d.ts +0 -44
  76. package/dist/services/providers/base-provider.service.d.ts +0 -142
  77. package/dist/services/providers/glm-provider.service.d.ts +0 -96
  78. package/dist/services/providers/minimax-provider.service.d.ts +0 -102
  79. package/dist/services/providers/ollama-provider.service.d.ts +0 -76
  80. package/dist/services/providers/openai-compatible.service.d.ts +0 -44
  81. package/dist/services/providers/openai-provider.service.d.ts +0 -43
  82. package/dist/services/providers/vllm-provider.service.d.ts +0 -82
  83. package/dist/services/security/consent-manager.service.d.ts +0 -65
  84. package/dist/services/security/password-manager.service.d.ts +0 -67
  85. package/dist/services/security/risk-assessment.service.d.ts +0 -65
  86. package/dist/services/security/security-validator.service.d.ts +0 -36
  87. package/dist/services/terminal/buffer-analyzer.service.d.ts +0 -128
  88. package/dist/services/terminal/command-analyzer.service.d.ts +0 -20
  89. package/dist/services/terminal/context-menu.service.d.ts +0 -24
  90. package/dist/services/terminal/hotkey.service.d.ts +0 -28
  91. package/dist/services/terminal/terminal-context.service.d.ts +0 -100
  92. package/dist/services/terminal/terminal-manager.service.d.ts +0 -185
  93. package/dist/services/terminal/terminal-tools.service.d.ts +0 -79
  94. package/dist/types/ai.types.d.ts +0 -199
  95. package/dist/types/provider.types.d.ts +0 -105
  96. package/dist/types/security.types.d.ts +0 -85
  97. package/dist/types/terminal.types.d.ts +0 -150
  98. package/dist/utils/encryption.utils.d.ts +0 -83
  99. package/dist/utils/formatting.utils.d.ts +0 -106
  100. package/dist/utils/validation.utils.d.ts +0 -83
  101. package/jest.config.js +0 -47
  102. package/webpack-docker.config.js +0 -42
  103. package/webpack.config.js +0 -81
package/src/i18n/types.ts CHANGED
@@ -31,6 +31,7 @@ export interface SettingsTranslations {
31
31
  title: string;
32
32
  generalTab: string;
33
33
  providersTab: string;
34
+ contextTab: string;
34
35
  securityTab: string;
35
36
  chatTab: string;
36
37
  advancedTab: string;
@@ -130,6 +131,8 @@ export interface ProvidersTranslations {
130
131
  apiKey: string;
131
132
  baseURL: string;
132
133
  model: string;
134
+ contextWindow: string;
135
+ contextWindowDesc: string;
133
136
  saveConfig: string;
134
137
  testConnection: string;
135
138
  detectService: string;
@@ -199,6 +202,30 @@ export interface AdvancedSettingsTranslations {
199
202
  currentProvider: string;
200
203
  }
201
204
 
205
+ // 上下文设置
206
+ export interface ContextSettingsTranslations {
207
+ title: string;
208
+ autoCompact: string;
209
+ enableAutoCompact: string;
210
+ autoCompactDesc: string;
211
+ autoCompactEnabled: string;
212
+ autoCompactDisabled: string;
213
+ tokenConfig: string;
214
+ currentProviderLimit: string;
215
+ maxContextTokens: string;
216
+ maxContextTokensDesc: string;
217
+ reservedOutputTokens: string;
218
+ reservedOutputTokensDesc: string;
219
+ thresholdConfig: string;
220
+ pruneThreshold: string;
221
+ pruneThresholdDesc: string;
222
+ compactThreshold: string;
223
+ compactThresholdDesc: string;
224
+ messagesToKeep: string;
225
+ messagesToKeepDesc: string;
226
+ configSaved: string;
227
+ }
228
+
202
229
  // 主翻译类型
203
230
  export interface TranslationKeys {
204
231
  common: CommonTranslations;
@@ -211,6 +238,7 @@ export interface TranslationKeys {
211
238
  chatInterface: ChatInterfaceTranslations;
212
239
  riskLevel: RiskLevelTranslations;
213
240
  advancedSettings: AdvancedSettingsTranslations;
241
+ contextSettings: ContextSettingsTranslations;
214
242
  }
215
243
 
216
244
  // 支持的语言类型
package/src/index.ts CHANGED
@@ -59,6 +59,7 @@ import { PlatformDetectionService } from './services/platform/platform-detection
59
59
 
60
60
  // Core Services
61
61
  import { CheckpointManager } from './services/core/checkpoint.service';
62
+ import { ToastService } from './services/core/toast.service';
62
63
 
63
64
  // Enhanced Terminal Services
64
65
  import { BufferAnalyzerService } from './services/terminal/buffer-analyzer.service';
@@ -76,6 +77,7 @@ import { AiSettingsTabComponent } from './components/settings/ai-settings-tab.co
76
77
  import { ProviderConfigComponent } from './components/settings/provider-config.component';
77
78
  import { SecuritySettingsComponent } from './components/settings/security-settings.component';
78
79
  import { GeneralSettingsComponent } from './components/settings/general-settings.component';
80
+ import { ContextSettingsComponent } from './components/settings/context-settings.component';
79
81
 
80
82
  import { RiskConfirmDialogComponent } from './components/security/risk-confirm-dialog.component';
81
83
  import { PasswordPromptComponent } from './components/security/password-prompt.component';
@@ -151,6 +153,9 @@ import { AiHotkeyProvider } from './providers/tabby/ai-hotkey.provider';
151
153
  // Core Services
152
154
  CheckpointManager,
153
155
 
156
+ // Toast Service
157
+ ToastService,
158
+
154
159
  // Enhanced Terminal Services
155
160
  BufferAnalyzerService,
156
161
 
@@ -173,6 +178,7 @@ import { AiHotkeyProvider } from './providers/tabby/ai-hotkey.provider';
173
178
  ProviderConfigComponent,
174
179
  SecuritySettingsComponent,
175
180
  GeneralSettingsComponent,
181
+ ContextSettingsComponent,
176
182
 
177
183
  // Security Components
178
184
  RiskConfirmDialogComponent,
@@ -3,7 +3,9 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs';
3
3
  import { ChatMessage, ChatRequest, ChatResponse, MessageRole, Checkpoint, ApiMessage } from '../../types/ai.types';
4
4
  import { AiAssistantService } from '../core/ai-assistant.service';
5
5
  import { LoggerService } from '../core/logger.service';
6
+ import { ConfigProviderService } from '../core/config-provider.service';
6
7
  import { ChatHistoryService } from './chat-history.service';
8
+ import { ContextManager } from '../context/manager';
7
9
 
8
10
  /**
9
11
  * 聊天会话服务
@@ -27,7 +29,9 @@ export class ChatSessionService {
27
29
  constructor(
28
30
  private aiService: AiAssistantService,
29
31
  private logger: LoggerService,
30
- private chatHistoryService: ChatHistoryService
32
+ private chatHistoryService: ChatHistoryService,
33
+ private contextManager: ContextManager,
34
+ private configService: ConfigProviderService
31
35
  ) { }
32
36
 
33
37
  /**
@@ -56,8 +60,10 @@ export class ChatSessionService {
56
60
  if (!this.currentSessionId) {
57
61
  this.createSession();
58
62
  }
59
-
60
63
  try {
64
+ // === 发送前检查并管理上下文 ===
65
+ await this.checkAndManageContext();
66
+
61
67
  // 添加用户消息
62
68
  const userMessage: ChatMessage = {
63
69
  id: this.generateMessageId(),
@@ -450,4 +456,87 @@ export class ChatSessionService {
450
456
  return sum + Math.ceil(content.length / 4);
451
457
  }, 0);
452
458
  }
459
+
460
+ // ==================== 上下文自动管理 ====================
461
+
462
+ /**
463
+ * 检查并管理上下文(自动压缩)
464
+ */
465
+ private async checkAndManageContext(): Promise<void> {
466
+ if (!this.currentSessionId) {
467
+ return;
468
+ }
469
+ // 检查自动压缩开关是否启用
470
+ if (!this.configService.isAutoCompactEnabled()) {
471
+ return;
472
+ }
473
+ // 同步当前消息到 ChatHistoryService
474
+ this.syncMessagesToHistory();
475
+ // 检查是否需要管理上下文
476
+ if (this.contextManager.shouldManageContext(this.currentSessionId)) {
477
+ this.logger.info('Context management triggered', {
478
+ sessionId: this.currentSessionId
479
+ });
480
+ try {
481
+ const results = await this.contextManager.manageContext(this.currentSessionId);
482
+ // 记录压缩结果
483
+ if (results.compactionResult) {
484
+ this.logger.info('Context compacted', {
485
+ tokensSaved: results.compactionResult.tokensSaved,
486
+ summary: results.compactionResult.summary?.substring(0, 100)
487
+ });
488
+ }
489
+ if (results.pruneResult && results.pruneResult.pruned) {
490
+ this.logger.info('Context pruned', {
491
+ tokensSaved: results.pruneResult.tokensSaved,
492
+ partsCompacted: results.pruneResult.partsCompacted
493
+ });
494
+ }
495
+ if (results.truncationResult) {
496
+ this.logger.info('Context truncated', {
497
+ messagesRemoved: results.truncationResult.messagesRemoved
498
+ });
499
+ }
500
+ // 重新加载压缩后的消息
501
+ this.reloadMessagesFromHistory();
502
+ } catch (error) {
503
+ this.logger.error('Context management failed', {
504
+ error,
505
+ sessionId: this.currentSessionId
506
+ });
507
+ // 即使压缩失败,也继续发送消息
508
+ }
509
+ }
510
+ }
511
+
512
+ /**
513
+ * 同步当前消息到历史存储
514
+ */
515
+ private syncMessagesToHistory(): void {
516
+ if (!this.currentSessionId) return;
517
+ const currentMessages = this.messagesSubject.value;
518
+ // 将当前会话保存到历史
519
+ this.chatHistoryService.saveSession(this.currentSessionId, currentMessages);
520
+ }
521
+
522
+ /**
523
+ * 从历史存储重新加载消息(压缩后)
524
+ */
525
+ private reloadMessagesFromHistory(): void {
526
+ if (!this.currentSessionId) return;
527
+ // 获取有效历史(已被压缩过滤)
528
+ const effectiveMessages = this.contextManager.getEffectiveHistory(this.currentSessionId);
529
+ // 转换为 ChatMessage 格式
530
+ const chatMessages: ChatMessage[] = effectiveMessages.map(msg => ({
531
+ id: this.generateMessageId(),
532
+ role: msg.role as any,
533
+ content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
534
+ timestamp: new Date(msg.ts)
535
+ }));
536
+ this.messagesSubject.next(chatMessages);
537
+ this.logger.info('Messages reloaded after context management', {
538
+ messageCount: chatMessages.length,
539
+ sessionId: this.currentSessionId
540
+ });
541
+ }
453
542
  }
@@ -1,5 +1,6 @@
1
1
  import { Injectable } from '@angular/core';
2
2
  import { LoggerService } from '../core/logger.service';
3
+ import { ConfigProviderService } from '../core/config-provider.service';
3
4
  import { ChatHistoryService, SavedSession } from '../chat/chat-history.service';
4
5
  import {
5
6
  ApiMessage,
@@ -23,12 +24,28 @@ export class ContextManager {
23
24
 
24
25
  constructor(
25
26
  private logger: LoggerService,
26
- private chatHistoryService: ChatHistoryService
27
+ private chatHistoryService: ChatHistoryService,
28
+ private configService: ConfigProviderService
27
29
  ) {
28
30
  this.config = { ...DEFAULT_CONTEXT_CONFIG };
31
+ // 动态获取当前供应商的上下文限制
32
+ this.updateContextLimit();
29
33
  this.logger.info('ContextManager initialized', { config: this.config });
30
34
  }
31
35
 
36
+ /**
37
+ * 更新上下文限制(根据当前供应商)
38
+ */
39
+ updateContextLimit(): void {
40
+ const providerContextWindow = this.configService.getActiveProviderContextWindow();
41
+ if (providerContextWindow !== this.config.maxContextTokens) {
42
+ this.config.maxContextTokens = providerContextWindow;
43
+ this.logger.info('Context limit updated', {
44
+ newLimit: providerContextWindow
45
+ });
46
+ }
47
+ }
48
+
32
49
  /**
33
50
  * 计算Token使用量
34
51
  */
@@ -71,10 +88,24 @@ export class ContextManager {
71
88
  * 判断是否需要管理上下文
72
89
  */
73
90
  shouldManageContext(sessionId: string): boolean {
91
+ // 动态更新上下文限制
92
+ this.updateContextLimit();
93
+
74
94
  const session = this.chatHistoryService.loadSession(sessionId);
75
- if (!session || !session.contextInfo?.tokenUsage) {
95
+ if (!session) {
76
96
  return false;
77
97
  }
98
+ if (!session.messages || session.messages.length < 2) {
99
+ // 消息太少,不需要管理
100
+ return false;
101
+ }
102
+ if (!session.contextInfo?.tokenUsage) {
103
+ // 没有 token 使用统计,检查消息数量
104
+ const messages = session.messages as unknown as ApiMessage[];
105
+ const tokenUsage = this.calculateTokenUsage(messages);
106
+ const usageRate = this.calculateUsageRate(tokenUsage);
107
+ return usageRate >= this.config.pruneThreshold;
108
+ }
78
109
 
79
110
  const usageRate = this.calculateUsageRate(session.contextInfo.tokenUsage);
80
111
 
@@ -172,18 +172,16 @@ export class AiAssistantService {
172
172
  // 处理工具调用(如果响应包含工具调用)
173
173
  response = await this.handleToolCalls(request, response, activeProvider);
174
174
 
175
- // 添加:检测未调用工具的问题响应
175
+ // 检测幻觉:AI声称执行了操作但未调用工具
176
176
  const toolCalls = (response as any).toolCalls;
177
- if (!toolCalls || toolCalls.length === 0) {
178
- const content = response.message.content.toLowerCase();
179
- const intentPatterns = ['我将向', '我会执行', '正在执行', '向终端写入'];
180
- const hasExecutionIntent = intentPatterns.some(p => content.includes(p));
181
-
182
- if (hasExecutionIntent) {
183
- this.logger.warn('AI stated execution intent but did not call tools');
184
- // 添加警告消息到响应中
185
- response.message.content += '\n\n⚠️ 注意:命令未实际执行。请明确告诉我需要执行的命令。';
186
- }
177
+ const hallucinationDetected = this.detectHallucination({
178
+ text: response.message.content,
179
+ toolCallCount: toolCalls?.length || 0
180
+ });
181
+
182
+ if (hallucinationDetected) {
183
+ // 附加警告消息,提醒用户
184
+ response.message.content += '\n\n⚠️ **检测到可能的幻觉**:AI声称执行了操作但未实际调用工具。\n实际执行的命令可能为空。请重新描述您的需求。';
187
185
  }
188
186
 
189
187
  this.logger.info('Chat request completed successfully');
@@ -606,4 +604,30 @@ export class AiAssistantService {
606
604
  return null;
607
605
  }
608
606
  }
607
+
608
+ /**
609
+ * 检测AI幻觉
610
+ * 当AI声称执行了操作(如切换终端、执行命令)但未调用相应工具时触发
611
+ */
612
+ private detectHallucination(response: { text: string; toolCallCount: number }): boolean {
613
+ const actionKeywords = [
614
+ '已切换', '已执行', '已完成', '已写入', '已读取',
615
+ '切换成功', '执行成功', '写入成功', '读取成功',
616
+ '现在切换', '现在执行', '已经为您切换', '已经为您执行',
617
+ '我将切换', '我会切换', '已经切换到', '已经执行了',
618
+ '终端已切换', '命令已执行', '操作已完成'
619
+ ];
620
+
621
+ const hasActionClaim = actionKeywords.some(keyword => response.text.includes(keyword));
622
+
623
+ if (hasActionClaim && response.toolCallCount === 0) {
624
+ this.logger.warn('AI Hallucination detected', {
625
+ textPreview: response.text.substring(0, 100),
626
+ toolCallCount: response.toolCallCount
627
+ });
628
+ return true;
629
+ }
630
+
631
+ return false;
632
+ }
609
633
  }
@@ -3,6 +3,7 @@ import { Subject, Observable } from 'rxjs';
3
3
  import { LoggerService } from './logger.service';
4
4
  import { SecurityConfig } from '../../types/security.types';
5
5
  import { ProviderConfig } from '../../types/provider.types';
6
+ import { ContextConfig } from '../../types/ai.types';
6
7
 
7
8
  /**
8
9
  * AI助手配置
@@ -203,6 +204,32 @@ export class ConfigProviderService {
203
204
  return { ...this.config.providers };
204
205
  }
205
206
 
207
+ /**
208
+ * 获取活跃供应商的上下文窗口大小
209
+ */
210
+ getActiveProviderContextWindow(): number {
211
+ const activeProvider = this.config.defaultProvider;
212
+ if (!activeProvider) {
213
+ return 200000; // 默认值
214
+ }
215
+ const providerConfig = this.getProviderConfig(activeProvider);
216
+ const contextWindow = providerConfig?.contextWindow;
217
+ if (contextWindow && contextWindow > 0) {
218
+ return contextWindow;
219
+ }
220
+ // 返回供应商默认值
221
+ const defaults: { [key: string]: number } = {
222
+ 'openai': 128000,
223
+ 'anthropic': 200000,
224
+ 'minimax': 128000,
225
+ 'glm': 128000,
226
+ 'ollama': 8192,
227
+ 'vllm': 8192,
228
+ 'openai-compatible': 128000
229
+ };
230
+ return defaults[activeProvider] || 200000;
231
+ }
232
+
206
233
  /**
207
234
  * 获取默认提供商
208
235
  */
@@ -344,4 +371,43 @@ export class ConfigProviderService {
344
371
  errors
345
372
  };
346
373
  }
374
+
375
+ // ==================== 上下文配置 ====================
376
+
377
+ /**
378
+ * 获取上下文配置
379
+ */
380
+ getContextConfig(): ContextConfig | null {
381
+ try {
382
+ const key = 'tabby-ai-assistant-context-config';
383
+ const stored = localStorage.getItem(key);
384
+ return stored ? JSON.parse(stored) : null;
385
+ } catch {
386
+ return null;
387
+ }
388
+ }
389
+
390
+ /**
391
+ * 设置上下文配置
392
+ */
393
+ setContextConfig(config: ContextConfig): void {
394
+ const key = 'tabby-ai-assistant-context-config';
395
+ localStorage.setItem(key, JSON.stringify(config));
396
+ }
397
+
398
+ /**
399
+ * 获取自动压缩开关状态
400
+ */
401
+ isAutoCompactEnabled(): boolean {
402
+ const key = 'tabby-ai-assistant-auto-compact';
403
+ return localStorage.getItem(key) !== 'false';
404
+ }
405
+
406
+ /**
407
+ * 设置自动压缩开关状态
408
+ */
409
+ setAutoCompactEnabled(enabled: boolean): void {
410
+ const key = 'tabby-ai-assistant-auto-compact';
411
+ localStorage.setItem(key, String(enabled));
412
+ }
347
413
  }
@@ -0,0 +1,36 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+
4
+ export interface ToastMessage {
5
+ id: string;
6
+ type: 'success' | 'error' | 'warning' | 'info';
7
+ message: string;
8
+ duration?: number;
9
+ }
10
+
11
+ @Injectable({ providedIn: 'root' })
12
+ export class ToastService {
13
+ private toastSubject = new Subject<ToastMessage>();
14
+ toast$ = this.toastSubject.asObservable();
15
+
16
+ show(type: ToastMessage['type'], message: string, duration = 3000): void {
17
+ const id = `toast-${Date.now()}`;
18
+ this.toastSubject.next({ id, type, message, duration });
19
+ }
20
+
21
+ success(message: string, duration = 3000): void {
22
+ this.show('success', message, duration);
23
+ }
24
+
25
+ error(message: string, duration = 5000): void {
26
+ this.show('error', message, duration);
27
+ }
28
+
29
+ warning(message: string, duration = 4000): void {
30
+ this.show('warning', message, duration);
31
+ }
32
+
33
+ info(message: string, duration = 3000): void {
34
+ this.show('info', message, duration);
35
+ }
36
+ }
@@ -385,16 +385,24 @@ export class AnthropicProviderService extends BaseAiProvider {
385
385
  - read_terminal_output: 读取终端输出
386
386
  - get_terminal_list: 获取所有终端列表
387
387
  - get_terminal_cwd: 获取当前工作目录
388
+ - focus_terminal: 切换到指定索引的终端(需要参数 terminal_index)
389
+ - get_terminal_selection: 获取终端中选中的文本
388
390
 
389
391
  ## 重要规则
390
392
  1. 当用户请求执行命令(如"查看当前目录"、"列出文件"等),你必须使用 write_to_terminal 工具来执行
391
- 2. 不要只是描述你"将要做什么",而是直接调用工具执行
392
- 3. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
393
- 4. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
393
+ 2. **当用户请求切换终端(如"切换到终端0"、"打开终端4"等),你必须使用 focus_terminal 工具**
394
+ 3. 不要只是描述你"将要做什么",而是直接调用工具执行
395
+ 4. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
396
+ 5. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
397
+ 6. **永远不要假装执行了操作,必须真正调用工具**
394
398
 
395
399
  ## 示例
396
400
  用户:"查看当前目录的文件"
397
401
  正确做法:调用 write_to_terminal 工具,参数 { "command": "dir", "execute": true }
398
- 错误做法:仅回复文字"我将执行 dir 命令"`;
402
+ 错误做法:仅回复文字"我将执行 dir 命令"
403
+
404
+ 用户:"切换到终端4"
405
+ 正确做法:调用 focus_terminal 工具,参数 { "terminal_index": 4 }
406
+ 错误做法:仅回复文字"已切换到终端4"(不调用工具)`;
399
407
  }
400
408
  }
@@ -479,16 +479,24 @@ export class GlmProviderService extends BaseAiProvider {
479
479
  - read_terminal_output: 读取终端输出
480
480
  - get_terminal_list: 获取所有终端列表
481
481
  - get_terminal_cwd: 获取当前工作目录
482
+ - focus_terminal: 切换到指定索引的终端(需要参数 terminal_index)
483
+ - get_terminal_selection: 获取终端中选中的文本
482
484
 
483
485
  ## 重要规则
484
486
  1. 当用户请求执行命令(如"查看当前目录"、"列出文件"等),你必须使用 write_to_terminal 工具来执行
485
- 2. 不要只是描述你"将要做什么",而是直接调用工具执行
486
- 3. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
487
- 4. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
487
+ 2. **当用户请求切换终端(如"切换到终端0"、"打开终端4"等),你必须使用 focus_terminal 工具**
488
+ 3. 不要只是描述你"将要做什么",而是直接调用工具执行
489
+ 4. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
490
+ 5. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
491
+ 6. **永远不要假装执行了操作,必须真正调用工具**
488
492
 
489
493
  ## 示例
490
494
  用户:"查看当前目录的文件"
491
495
  正确做法:调用 write_to_terminal 工具,参数 { "command": "dir", "execute": true }
492
- 错误做法:仅回复文字"我将执行 dir 命令"`;
496
+ 错误做法:仅回复文字"我将执行 dir 命令"
497
+
498
+ 用户:"切换到终端4"
499
+ 正确做法:调用 focus_terminal 工具,参数 { "terminal_index": 4 }
500
+ 错误做法:仅回复文字"已切换到终端4"(不调用工具)`;
493
501
  }
494
502
  }
@@ -610,16 +610,24 @@ export class MinimaxProviderService extends BaseAiProvider {
610
610
  - read_terminal_output: 读取终端输出
611
611
  - get_terminal_list: 获取所有终端列表
612
612
  - get_terminal_cwd: 获取当前工作目录
613
+ - focus_terminal: 切换到指定索引的终端(需要参数 terminal_index)
614
+ - get_terminal_selection: 获取终端中选中的文本
613
615
 
614
616
  ## 重要规则
615
617
  1. 当用户请求执行命令(如"查看当前目录"、"列出文件"等),你必须使用 write_to_terminal 工具来执行
616
- 2. 不要只是描述你"将要做什么",而是直接调用工具执行
617
- 3. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
618
- 4. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
618
+ 2. **当用户请求切换终端(如"切换到终端0"、"打开终端4"等),你必须使用 focus_terminal 工具**
619
+ 3. 不要只是描述你"将要做什么",而是直接调用工具执行
620
+ 4. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
621
+ 5. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
622
+ 6. **永远不要假装执行了操作,必须真正调用工具**
619
623
 
620
624
  ## 示例
621
625
  用户:"查看当前目录的文件"
622
626
  正确做法:调用 write_to_terminal 工具,参数 { "command": "dir", "execute": true }
623
- 错误做法:仅回复文字"我将执行 dir 命令"`;
627
+ 错误做法:仅回复文字"我将执行 dir 命令"
628
+
629
+ 用户:"切换到终端4"
630
+ 正确做法:调用 focus_terminal 工具,参数 { "terminal_index": 4 }
631
+ 错误做法:仅回复文字"已切换到终端4"(不调用工具)`;
624
632
  }
625
633
  }
@@ -192,6 +192,7 @@ export interface ContextConfig {
192
192
  pruneThreshold: number; // 裁剪触发阈值 (0-1)
193
193
  messagesToKeep: number; // 保留的最近消息数
194
194
  bufferPercentage: number; // 安全缓冲区百分比
195
+ summaryPrompt?: string; // 自定义摘要提示词
195
196
  }
196
197
 
197
198
  // 默认配置
@@ -201,10 +202,9 @@ export const DEFAULT_CONTEXT_CONFIG: ContextConfig = {
201
202
  compactThreshold: 0.85,
202
203
  pruneThreshold: 0.70,
203
204
  messagesToKeep: 3,
204
- bufferPercentage: 0.10
205
+ bufferPercentage: 0.10,
206
+ summaryPrompt: '请用一句话总结以下对话的要点,保留关键信息和用户意图:'
205
207
  };
206
-
207
- // 压缩结果
208
208
  export interface CompactionResult {
209
209
  success: boolean;
210
210
  messages: ApiMessage[];
@@ -29,6 +29,7 @@ export interface ProviderConfig {
29
29
  retries?: number;
30
30
  authConfig?: AuthConfig;
31
31
  enabled?: boolean;
32
+ contextWindow?: number; // 供应商上下文窗口限制
32
33
  }
33
34
 
34
35
  // 提供商信息