oricore 1.4.1 → 1.5.0

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.
@@ -3,13 +3,13 @@ import {
3
3
  SessionConfigManager,
4
4
  filterMessages,
5
5
  loadSessionMessages
6
- } from "./chunk-AXJGNOSQ.js";
7
- import "./chunk-XKZSVWRX.js";
8
- import "./chunk-SXDGT4YB.js";
6
+ } from "./chunk-OYWDQD3F.js";
7
+ import "./chunk-4QYFQSAC.js";
8
+ import "./chunk-DO76AL42.js";
9
9
  export {
10
10
  Session,
11
11
  SessionConfigManager,
12
12
  filterMessages,
13
13
  loadSessionMessages
14
14
  };
15
- //# sourceMappingURL=session-34VFUDZB.js.map
15
+ //# sourceMappingURL=session-QMS6OYG2.js.map
@@ -0,0 +1,5 @@
1
+ import {
2
+ require_undici
3
+ } from "./chunk-DO76AL42.js";
4
+ export default require_undici();
5
+ //# sourceMappingURL=undici-326ZBRKH.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oricore",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "OriCore - A powerful AI engine with multi-modal support, tool calling, and extensible architecture",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/core/loop.ts CHANGED
@@ -354,6 +354,7 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
354
354
  response: result.response,
355
355
  });
356
356
 
357
+ let finishChunkReceived = false;
357
358
  for await (const chunk of result.stream) {
358
359
  if (opts.signal?.aborted) {
359
360
  return createCancelError();
@@ -385,6 +386,7 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
385
386
  });
386
387
  break;
387
388
  case 'finish':
389
+ finishChunkReceived = true;
388
390
  lastUsage = Usage.fromEventUsage(chunk.usage);
389
391
  totalUsage.add(lastUsage);
390
392
  if (toolCalls.length === 0 && text.trim() === '') {
@@ -423,6 +425,19 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
423
425
  }
424
426
  }
425
427
 
428
+ // Check if stream ended without receiving finish chunk and no content
429
+ if (
430
+ !finishChunkReceived &&
431
+ toolCalls.length === 0 &&
432
+ text.trim() === ''
433
+ ) {
434
+ const error = new Error(
435
+ 'Empty response: stream ended without any chunks',
436
+ );
437
+ (error as any).isRetryable = true;
438
+ throw error;
439
+ }
440
+
426
441
  break;
427
442
  } catch (error: any) {
428
443
  const nextRetryCount = retryCount + 1;
@@ -617,6 +632,9 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
617
632
  }
618
633
  };
619
634
 
635
+ const approvedToolUses: ToolUse[] = [];
636
+ let earlyReturn: LoopResult | null = null;
637
+
620
638
  for (const toolCall of toolCalls) {
621
639
  let toolUse: ToolUse = {
622
640
  name: toolCall.toolName,
@@ -642,26 +660,10 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
642
660
  }
643
661
 
644
662
  if (approved) {
645
- toolCallsCount++;
646
663
  if (updatedParams) {
647
664
  toolUse.params = { ...toolUse.params, ...updatedParams };
648
665
  }
649
- let toolResult = await opts.tools.invoke(
650
- toolUse.name,
651
- JSON.stringify(toolUse.params),
652
- toolUse.callId,
653
- );
654
- if (opts.onToolResult) {
655
- toolResult = await opts.onToolResult(toolUse, toolResult, approved);
656
- }
657
- toolResults.push({
658
- toolCallId: toolUse.callId,
659
- toolName: toolUse.name,
660
- input: toolUse.params,
661
- result: toolResult,
662
- });
663
- // Prevent normal turns from being terminated due to exceeding the limit
664
- turnsCount--;
666
+ approvedToolUses.push(toolUse);
665
667
  } else {
666
668
  let message = 'Error: Tool execution was denied by user.';
667
669
  if (denyReason) {
@@ -672,7 +674,7 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
672
674
  isError: true,
673
675
  };
674
676
  if (opts.onToolResult) {
675
- toolResult = await opts.onToolResult(toolUse, toolResult, approved);
677
+ toolResult = await opts.onToolResult(toolUse, toolResult, false);
676
678
  }
677
679
  toolResults.push({
678
680
  toolCallId: toolUse.callId,
@@ -681,10 +683,8 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
681
683
  result: toolResult,
682
684
  });
683
685
 
684
- // Add denied results for remaining unprocessed tools
685
- await addDeniedResultsForRemainingTools();
686
-
687
686
  if (!denyReason) {
687
+ await addDeniedResultsForRemainingTools();
688
688
  await history.addMessage({
689
689
  role: 'tool',
690
690
  content: toolResults.map((tr) =>
@@ -696,7 +696,7 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
696
696
  ),
697
697
  ),
698
698
  });
699
- return {
699
+ earlyReturn = {
700
700
  success: false,
701
701
  error: {
702
702
  type: 'tool_denied',
@@ -708,11 +708,65 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
708
708
  },
709
709
  },
710
710
  };
711
- } else {
712
- // When denyReason is provided, we should break out of the tool loop
713
- // to let the model react to the rejection before continuing
714
711
  break;
715
712
  }
713
+ await addDeniedResultsForRemainingTools();
714
+ break;
715
+ }
716
+ }
717
+
718
+ if (earlyReturn) {
719
+ return earlyReturn;
720
+ }
721
+
722
+ // Execute approved tools in parallel
723
+ if (approvedToolUses.length > 0) {
724
+ const executionResults = await Promise.allSettled(
725
+ approvedToolUses.map(async (toolUse) => {
726
+ let toolResult = await opts.tools.invoke(
727
+ toolUse.name,
728
+ JSON.stringify(toolUse.params),
729
+ toolUse.callId,
730
+ );
731
+ if (opts.onToolResult) {
732
+ toolResult = await opts.onToolResult(toolUse, toolResult, true);
733
+ }
734
+ return {
735
+ toolCallId: toolUse.callId,
736
+ toolName: toolUse.name,
737
+ input: toolUse.params,
738
+ result: toolResult,
739
+ };
740
+ }),
741
+ );
742
+
743
+ toolCallsCount += approvedToolUses.length;
744
+ turnsCount -= approvedToolUses.length;
745
+
746
+ for (let i = 0; i < executionResults.length; i++) {
747
+ const settledResult = executionResults[i];
748
+ if (settledResult.status === 'fulfilled') {
749
+ toolResults.push(settledResult.value);
750
+ } else {
751
+ const failedToolUse = approvedToolUses[i];
752
+ let errorResult: ToolResult = {
753
+ llmContent: `Tool execution error: ${settledResult.reason instanceof Error ? settledResult.reason.message : String(settledResult.reason)}`,
754
+ isError: true,
755
+ };
756
+ if (opts.onToolResult) {
757
+ errorResult = await opts.onToolResult(
758
+ failedToolUse,
759
+ errorResult,
760
+ true,
761
+ );
762
+ }
763
+ toolResults.push({
764
+ toolCallId: failedToolUse.callId,
765
+ toolName: failedToolUse.name,
766
+ input: failedToolUse.params,
767
+ result: errorResult,
768
+ });
769
+ }
716
770
  }
717
771
  }
718
772
 
@@ -322,6 +322,22 @@ export const models: ModelMap = {
322
322
  open_weights: false,
323
323
  limit: { context: 200000, output: 65536 },
324
324
  },
325
+ 'gemini-3.1-pro-preview': {
326
+ name: 'Gemini 3.1 Pro Preview',
327
+ attachment: true,
328
+ reasoning: true,
329
+ temperature: true,
330
+ tool_call: true,
331
+ knowledge: '2025-01',
332
+ release_date: '2026-02-19',
333
+ last_updated: '2026-02-19',
334
+ modalities: {
335
+ input: ['text', 'image', 'audio', 'video', 'pdf'],
336
+ output: ['text'],
337
+ },
338
+ open_weights: false,
339
+ limit: { context: 1048576, output: 65536 },
340
+ },
325
341
  'gemini-3-flash-preview': {
326
342
  name: 'Gemini 3 Flash Preview',
327
343
  attachment: true,
@@ -803,6 +819,19 @@ export const models: ModelMap = {
803
819
  open_weights: true,
804
820
  limit: { context: 204800, output: 131072 },
805
821
  },
822
+ 'glm-5': {
823
+ name: 'GLM-5',
824
+ attachment: false,
825
+ reasoning: true,
826
+ temperature: true,
827
+ tool_call: true,
828
+ knowledge: '2025-06',
829
+ release_date: '2026-02-10',
830
+ last_updated: '2026-02-10',
831
+ modalities: { input: ['text'], output: ['text'] },
832
+ open_weights: true,
833
+ limit: { context: 262144, output: 131072 },
834
+ },
806
835
  'sonoma-dusk-alpha': {
807
836
  name: 'Sonoma Dusk Alpha',
808
837
  attachment: true,
@@ -894,6 +923,20 @@ export const models: ModelMap = {
894
923
  open_weights: false,
895
924
  limit: { context: 200000, output: 128000 },
896
925
  },
926
+ 'claude-sonnet-4-6': {
927
+ name: 'Claude Sonnet 4.6',
928
+ shortName: 'Sonnet 4.6',
929
+ attachment: true,
930
+ reasoning: true,
931
+ temperature: true,
932
+ tool_call: true,
933
+ knowledge: '2025-07-31',
934
+ release_date: '2026-02-17',
935
+ last_updated: '2026-02-17',
936
+ modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
937
+ open_weights: false,
938
+ limit: { context: 1000000, output: 64000 },
939
+ },
897
940
  'ling-1t': {
898
941
  name: 'InclusionAI Ling-1T',
899
942
  attachment: true,
@@ -1037,4 +1080,30 @@ export const models: ModelMap = {
1037
1080
  open_weights: true,
1038
1081
  limit: { context: 204800, output: 131072 },
1039
1082
  },
1083
+ 'minimax-m2.5': {
1084
+ name: 'MiniMax M2.5',
1085
+ attachment: false,
1086
+ reasoning: true,
1087
+ temperature: true,
1088
+ tool_call: true,
1089
+ knowledge: '',
1090
+ release_date: '2026-02-13',
1091
+ last_updated: '2026-02-13',
1092
+ modalities: { input: ['text'], output: ['text'] },
1093
+ open_weights: true,
1094
+ limit: { context: 204800, output: 131072 },
1095
+ },
1096
+ 'minimax-m2.7': {
1097
+ name: 'MiniMax M2.7',
1098
+ attachment: false,
1099
+ reasoning: true,
1100
+ temperature: true,
1101
+ tool_call: true,
1102
+ knowledge: '',
1103
+ release_date: '2026-03-18',
1104
+ last_updated: '2026-03-18',
1105
+ modalities: { input: ['text'], output: ['text'] },
1106
+ open_weights: true,
1107
+ limit: { context: 204800, output: 131072 },
1108
+ },
1040
1109
  };
@@ -164,6 +164,7 @@ export const providers: ProvidersMap = {
164
164
  'gemini-2.5-flash-lite': models['gemini-2.5-flash-lite-preview-06-17'],
165
165
  'gemini-2.5-pro': models['gemini-2.5-pro'],
166
166
  'gemini-3-pro-preview': models['gemini-3-pro-preview'],
167
+ 'gemini-3.1-pro-preview': models['gemini-3.1-pro-preview'],
167
168
  'gemini-3-flash-preview': models['gemini-3-flash-preview'],
168
169
  },
169
170
  createModel(name, provider) {
@@ -232,6 +233,7 @@ export const providers: ProvidersMap = {
232
233
  'claude-haiku-4-5': models['claude-haiku-4-5'],
233
234
  'claude-opus-4-5': models['claude-opus-4-5'],
234
235
  'claude-opus-4-6': models['claude-opus-4-6'],
236
+ 'claude-sonnet-4-6': models['claude-sonnet-4-6'],
235
237
  },
236
238
  apiFormat: 'anthropic',
237
239
  headers: {
@@ -301,6 +303,7 @@ export const providers: ProvidersMap = {
301
303
  'anthropic/claude-opus-4': models['claude-4-opus'],
302
304
  'anthropic/claude-opus-4.1': models['claude-4.1-opus'],
303
305
  'anthropic/claude-opus-4.5': models['claude-opus-4-5'],
306
+ 'anthropic/claude-sonnet-4.6': models['claude-sonnet-4-6'],
304
307
  'anthropic/claude-opus-4.6': models['claude-opus-4-6'],
305
308
  'deepseek/deepseek-r1-0528': models['deepseek-r1-0528'],
306
309
  'deepseek/deepseek-chat-v3-0324': models['deepseek-v3-0324'],
@@ -345,7 +348,9 @@ export const providers: ProvidersMap = {
345
348
  'z-ai/glm-4.6': models['glm-4.6'],
346
349
  'z-ai/glm-4.6v': models['glm-4.6v'],
347
350
  'z-ai/glm-4.7': models['glm-4.7'],
351
+ 'z-ai/glm-5': models['glm-5'],
348
352
  'minimax/minimax-m2': models['minimax-m2'],
353
+ 'minimax/minimax-m2.5': models['minimax-m2.5'],
349
354
  'openrouter/sherlock-dash-alpha': models['sherlock-dash-alpha'],
350
355
  'openrouter/sherlock-think-alpha': models['sherlock-think-alpha'],
351
356
  'xiaomi/mimo-v2-flash:free': models['mimo-v2-flash'],
@@ -364,43 +369,6 @@ export const providers: ProvidersMap = {
364
369
  ).chat(name) as unknown as LanguageModelV3;
365
370
  },
366
371
  },
367
- iflow: {
368
- id: 'iflow',
369
- source: 'built-in',
370
- env: ['IFLOW_API_KEY'],
371
- name: 'iFlow',
372
- api: 'https://apis.iflow.cn/v1/',
373
- doc: 'https://iflow.cn/',
374
- models: {
375
- 'qwen3-coder-plus': models['qwen3-coder-plus'],
376
- 'kimi-k2': models['kimi-k2'],
377
- 'kimi-k2-0905': models['kimi-k2-0905'],
378
- 'deepseek-v3': models['deepseek-v3-0324'],
379
- 'deepseek-v3.2': models['deepseek-v3-2-exp'],
380
- 'deepseek-r1': models['deepseek-r1-0528'],
381
- 'glm-4.6': models['glm-4.6'],
382
- 'glm-4.7': models['glm-4.7'],
383
- 'minimax-m2.1': models['minimax-m2.1'],
384
- 'qwen3-max': models['qwen3-max'],
385
- },
386
- createModel: createModelCreatorCompatible({
387
- fetch: (url: string, options: any) => {
388
- return fetch(url, {
389
- ...options,
390
- headers: {
391
- ...options.headers,
392
- 'user-agent': 'iFlow-Cli',
393
- },
394
- });
395
- },
396
- middlewares: [
397
- mergeSystemMessagesMiddleware,
398
- extractReasoningMiddleware({
399
- tagName: 'think',
400
- }) as LanguageModelV3Middleware,
401
- ],
402
- }),
403
- },
404
372
  moonshotai: {
405
373
  id: 'moonshotai',
406
374
  source: 'built-in',
@@ -480,6 +448,11 @@ export const providers: ProvidersMap = {
480
448
  'deepseek-ai/DeepSeek-V3.1': models['deepseek-v3-1'],
481
449
  'deepseek-ai/DeepSeek-V3': models['deepseek-v3-0324'],
482
450
  'zai-org/GLM-4.5': models['glm-4.5'],
451
+ 'Pro/moonshotai/Kimi-K2.5': models['kimi-k2-5'],
452
+ 'Pro/zai-org/GLM-5': models['glm-5'],
453
+ 'Pro/zai-org/GLM-4.7': models['glm-4.7'],
454
+ 'Pro/MiniMaxAI/MiniMax-M2.5': models['minimax-m2.5'],
455
+ 'Pro/deepseek-ai/DeepSeek-V3.2': models['deepseek-v3.2'],
483
456
  },
484
457
  createModel: defaultModelCreator,
485
458
  },
@@ -497,6 +470,7 @@ export const providers: ProvidersMap = {
497
470
  'ZhipuAI/GLM-4.5': models['glm-4.5'],
498
471
  'ZhipuAI/GLM-4.5V': models['glm-4.5v'],
499
472
  'ZhipuAI/GLM-4.6': models['glm-4.6'],
473
+ 'ZhipuAI/GLM-5': models['glm-5'],
500
474
  'deepseek-ai/DeepSeek-V3.2': models['deepseek-v3.2'],
501
475
  'deepseek-ai/DeepSeek-V3.2-Speciale': models['deepseek-v3.2-speciale'],
502
476
  },
@@ -532,6 +506,7 @@ export const providers: ProvidersMap = {
532
506
  'glm-4.6': models['glm-4.6'],
533
507
  'glm-4.6v': models['glm-4.6v'],
534
508
  'glm-4.7': models['glm-4.7'],
509
+ 'glm-5': models['glm-5'],
535
510
  },
536
511
  createModel: defaultModelCreator,
537
512
  },
@@ -550,9 +525,30 @@ export const providers: ProvidersMap = {
550
525
  'glm-4.5-flash': models['glm-4.5-flash'],
551
526
  'glm-4.6v': models['glm-4.6v'],
552
527
  'glm-4.7': models['glm-4.7'],
528
+ 'glm-5': models['glm-5'],
553
529
  },
554
530
  createModel: defaultModelCreator,
555
531
  },
532
+ 'bailian-coding-plan': {
533
+ id: 'bailian-coding-plan',
534
+ source: 'built-in',
535
+ env: ['BAILIAN_CODING_API_KEY'],
536
+ name: 'BaiLian Coding Plan',
537
+ api: 'https://coding.dashscope.aliyuncs.com/apps/anthropic/v1',
538
+ doc: 'https://www.aliyun.com/benefit/scene/codingplan',
539
+ apiFormat: 'anthropic',
540
+ models: {
541
+ 'qwen3.5-plus': models['qwen3-5-plus'],
542
+ 'qwen3-max-2026-01-23': models['qwen3-max'],
543
+ 'qwen3-coder-next': models['qwen3-coder-plus'],
544
+ 'qwen3-coder-plus': models['qwen3-coder-plus'],
545
+ 'MiniMax-M2.5': models['minimax-m2.5'],
546
+ 'glm-5': models['glm-5'],
547
+ 'glm-4.7': models['glm-4.7'],
548
+ 'kimi-k2.5': models['kimi-k2-5'],
549
+ },
550
+ createModel: defaultAnthropicModelCreator,
551
+ },
556
552
  zhipuai: {
557
553
  id: 'zhipuai',
558
554
  source: 'built-in',
@@ -568,6 +564,7 @@ export const providers: ProvidersMap = {
568
564
  'glm-4.5-flash': models['glm-4.5-flash'],
569
565
  'glm-4.6v': models['glm-4.6v'],
570
566
  'glm-4.7': models['glm-4.7'],
567
+ 'glm-5': models['glm-5'],
571
568
  },
572
569
  createModel: defaultModelCreator,
573
570
  },
@@ -601,9 +598,12 @@ export const providers: ProvidersMap = {
601
598
  'z-ai/glm-4.6': models['glm-4.6'],
602
599
  'z-ai/glm-4.6v': models['glm-4.6v'],
603
600
  'z-ai/glm-4.6v-flash': models['glm-4.6v'],
601
+ 'z-ai/glm-4.7': models['glm-4.7'],
602
+ 'z-ai/glm-5': models['glm-5'],
604
603
  'deepseek/deepseek-v3.2-speciale': models['deepseek-v3.2-speciale'],
605
604
  'deepseek/deepseek-chat': models['deepseek-v3-2-exp'],
606
605
  'deepseek/deepseek-reasoner': models['deepseek-r1-0528'],
606
+ 'minimax/minimax-m2.5': models['minimax-m2.5'],
607
607
  },
608
608
  headers: {
609
609
  'X-Title': 'OriCore',
@@ -620,6 +620,8 @@ export const providers: ProvidersMap = {
620
620
  models: {
621
621
  'minimax-m2': models['minimax-m2'],
622
622
  'minimax-m2.1': models['minimax-m2.1'],
623
+ 'minimax-m2.5': models['minimax-m2.5'],
624
+ 'minimax-m2.7': models['minimax-m2.7'],
623
625
  },
624
626
  createModel(name, provider) {
625
627
  const baseURL = getProviderBaseURL(provider);
@@ -639,6 +641,8 @@ export const providers: ProvidersMap = {
639
641
  models: {
640
642
  'minimax-m2': models['minimax-m2'],
641
643
  'minimax-m2.1': models['minimax-m2.1'],
644
+ 'minimax-m2.5': models['minimax-m2.5'],
645
+ 'minimax-m2.7': models['minimax-m2.7'],
642
646
  },
643
647
  createModel(name, provider) {
644
648
  const baseURL = getProviderBaseURL(provider);
@@ -770,6 +774,8 @@ export const providers: ProvidersMap = {
770
774
  'moonshotai/kimi-k2-thinking': models['kimi-k2-thinking'],
771
775
  'moonshotai/kimi-k2.5': models['kimi-k2.5'],
772
776
  'deepseek/deepseek-chat-v3.2': models['deepseek-v3-2-exp'],
777
+ 'openai/gpt-oss-120b': models['gpt-oss-120b'],
778
+ 'xiaomimimo/mimo-v2-flash': models['mimo-v2-flash'],
773
779
  },
774
780
  createModel: defaultModelCreator,
775
781
  },
@@ -789,6 +795,7 @@ export const providers: ProvidersMap = {
789
795
  'claude-4-5-sonnet': models['claude-4-5-sonnet'],
790
796
  'claude-haiku-4-5': models['claude-haiku-4-5'],
791
797
  'claude-opus-4-5': models['claude-opus-4-5'],
798
+ 'claude-sonnet-4-6': models['claude-sonnet-4-6'],
792
799
  'claude-opus-4-6': models['claude-opus-4-6'],
793
800
  'gpt-5.1': models['gpt-5.1'],
794
801
  'gpt-5.1-codex-max': models['gpt-5.1-codex-max'],
@@ -808,4 +815,36 @@ export const providers: ProvidersMap = {
808
815
  return defaultModelCreator(name, provider);
809
816
  },
810
817
  },
818
+ kilo: {
819
+ id: 'kilo',
820
+ source: 'built-in',
821
+ env: ['KILO_API_KEY'],
822
+ name: 'Kilo',
823
+ api: 'https://api.kilo.ai/api/gateway',
824
+ doc: 'https://kilo.ai',
825
+ apiFormat: 'openai',
826
+ models: {
827
+ 'z-ai/glm-5': models['glm-5'],
828
+ 'z-ai/glm-5:free': models['glm-5'],
829
+ 'z-ai/glm-4.7': models['glm-4.7'],
830
+ 'anthropic/claude-opus-4.6': models['claude-opus-4-6'],
831
+ 'anthropic/claude-sonnet-4.6': models['claude-sonnet-4-6'],
832
+ 'anthropic/claude-haiku-4.5': models['claude-haiku-4-5'],
833
+ 'anthropic/claude-sonnet-4.5': models['claude-4-5-sonnet'],
834
+ 'google/gemini-3-flash-preview': models['gemini-3-flash-preview'],
835
+ 'google/gemini-3-pro-preview': models['gemini-3-pro-preview'],
836
+ 'minimax/minimax-m2.5:free': models['minimax-m2.5'],
837
+ 'minimax/minimax-m2.5': models['minimax-m2.5'],
838
+ 'moonshotai/kimi-k2.5': models['kimi-k2-5'],
839
+ },
840
+ createModel: (name, provider) => {
841
+ if (name.includes('claude-')) {
842
+ return defaultAnthropicModelCreator(name, provider);
843
+ }
844
+ if (name.includes('gemini-')) {
845
+ return defaultAnthropicModelCreator(name, provider);
846
+ }
847
+ return defaultModelCreator(name, provider);
848
+ },
849
+ },
811
850
  };
@@ -25,6 +25,19 @@ function transformVariants(model: Model, provider: Provider): Record<string, any
25
25
  const id = (model.id || '').toLowerCase();
26
26
 
27
27
  // These models use their own reasoning mechanism without variants
28
+ // bailian-coding-plan handles kimi/minimax/glm with special on/off toggle
29
+ if (provider.id === 'bailian-coding-plan') {
30
+ if (id.includes('kimi') || id.includes('minimax') || id.includes('glm')) {
31
+ return {
32
+ on: {
33
+ thinking: {
34
+ type: 'enabled' as const,
35
+ },
36
+ },
37
+ };
38
+ }
39
+ }
40
+
28
41
  if (
29
42
  id.includes('deepseek') ||
30
43
  id.includes('minimax') ||
package/src/mcp/mcp.ts CHANGED
@@ -459,8 +459,11 @@ export class MCPManager {
459
459
  serverName: string,
460
460
  config: MCPConfig,
461
461
  ): Tool {
462
+ const safeServerName = serverName.replace(/[^a-zA-Z0-9_-]/g, '');
463
+ const safeToolName = toolName.replace(/[^a-zA-Z0-9_-]/g, '_');
464
+
462
465
  return {
463
- name: `mcp__${serverName.replace(/[^a-zA-Z0-9_-]/g, '')}__${toolName}`,
466
+ name: `mcp__${safeServerName}__${safeToolName}`,
464
467
  description: toolDef.description,
465
468
  getDescription: ({ params }) => {
466
469
  return formatParamsDescription(params as Record<string, any>);
@@ -7,6 +7,21 @@ import type { Paths } from '../core/paths';
7
7
  import { PluginHookType } from '../core/plugin';
8
8
  import { safeFrontMatter } from '../utils/safeFrontMatter';
9
9
 
10
+ /**
11
+ * Check if a directory entry is a directory or a symlink pointing to a directory.
12
+ */
13
+ function isDirOrSymlinkToDir(parentDir: string, entry: fs.Dirent): boolean {
14
+ if (entry.isDirectory()) return true;
15
+ if (entry.isSymbolicLink()) {
16
+ try {
17
+ return fs.statSync(path.join(parentDir, entry.name)).isDirectory();
18
+ } catch {
19
+ // broken symlink, skip
20
+ }
21
+ }
22
+ return false;
23
+ }
24
+
10
25
  export enum SkillSource {
11
26
  Plugin = 'plugin',
12
27
  GlobalClaude = 'global-claude',
@@ -165,7 +180,7 @@ export class SkillManager {
165
180
  const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
166
181
 
167
182
  for (const entry of entries) {
168
- if (entry.isDirectory()) {
183
+ if (isDirOrSymlinkToDir(skillsDir, entry)) {
169
184
  const skillPath = path.join(skillsDir, entry.name, 'SKILL.md');
170
185
  if (fs.existsSync(skillPath)) {
171
186
  this.loadSkillFile(skillPath, source);
@@ -457,7 +472,7 @@ export class SkillManager {
457
472
  if (fs.existsSync(skillsDir) && fs.statSync(skillsDir).isDirectory()) {
458
473
  const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
459
474
  for (const entry of entries) {
460
- if (entry.isDirectory()) {
475
+ if (isDirOrSymlinkToDir(skillsDir, entry)) {
461
476
  const skillPath = path.join(skillsDir, entry.name, 'SKILL.md');
462
477
  if (fs.existsSync(skillPath)) {
463
478
  skills.push(skillPath);
@@ -471,7 +486,7 @@ export class SkillManager {
471
486
 
472
487
  const entries = fs.readdirSync(dir, { withFileTypes: true });
473
488
  for (const entry of entries) {
474
- if (entry.isDirectory()) {
489
+ if (isDirOrSymlinkToDir(dir, entry)) {
475
490
  const skillPath = path.join(dir, entry.name, 'SKILL.md');
476
491
  if (fs.existsSync(skillPath)) {
477
492
  skills.push(skillPath);
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ImagePart,
2
3
  NormalizedMessage,
3
4
  ReasoningPart,
4
5
  TextPart,
@@ -154,6 +155,23 @@ export function normalizeMessagesForCompact(
154
155
  };
155
156
  }
156
157
 
158
+ // Filter out image parts from user messages in compact mode
159
+ // Images consume too many tokens and are not needed for summarization
160
+ if (message.role === 'user') {
161
+ if (Array.isArray(message.content)) {
162
+ const filteredContent = message.content.filter(
163
+ (part): part is TextPart => part.type === 'text',
164
+ );
165
+ return {
166
+ ...message,
167
+ content: (filteredContent.length > 0
168
+ ? filteredContent
169
+ : message.content) as Array<TextPart | ImagePart>,
170
+ };
171
+ }
172
+ return message;
173
+ }
174
+
157
175
  return message;
158
176
  })
159
177
  .filter((message) => {