oricore 1.4.0 → 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-WDEZLYBN.js";
7
- import "./chunk-COJ5APNY.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-ISSOYGYB.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.0",
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,
@@ -608,6 +624,19 @@ export const models: ModelMap = {
608
624
  open_weights: false,
609
625
  limit: { context: 400000, output: 128000 },
610
626
  },
627
+ 'gpt-5.3-codex': {
628
+ name: 'GPT-5.3 Codex',
629
+ attachment: true,
630
+ reasoning: true,
631
+ temperature: false,
632
+ tool_call: true,
633
+ knowledge: '2025-08-31',
634
+ release_date: '2026-02-05',
635
+ last_updated: '2026-02-05',
636
+ modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
637
+ open_weights: false,
638
+ limit: { context: 400000, output: 128000 },
639
+ },
611
640
  'gpt-4.1': {
612
641
  name: 'GPT-4.1',
613
642
  attachment: true,
@@ -790,6 +819,19 @@ export const models: ModelMap = {
790
819
  open_weights: true,
791
820
  limit: { context: 204800, output: 131072 },
792
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
+ },
793
835
  'sonoma-dusk-alpha': {
794
836
  name: 'Sonoma Dusk Alpha',
795
837
  attachment: true,
@@ -868,6 +910,33 @@ export const models: ModelMap = {
868
910
  open_weights: false,
869
911
  limit: { context: 200000, output: 64000 },
870
912
  },
913
+ 'claude-opus-4-6': {
914
+ name: 'Claude Opus 4.6',
915
+ attachment: true,
916
+ reasoning: true,
917
+ temperature: true,
918
+ tool_call: true,
919
+ knowledge: '2025-05-31',
920
+ release_date: '2026-02-05',
921
+ last_updated: '2026-02-05',
922
+ modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
923
+ open_weights: false,
924
+ limit: { context: 200000, output: 128000 },
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
+ },
871
940
  'ling-1t': {
872
941
  name: 'InclusionAI Ling-1T',
873
942
  attachment: true,
@@ -1011,4 +1080,30 @@ export const models: ModelMap = {
1011
1080
  open_weights: true,
1012
1081
  limit: { context: 204800, output: 131072 },
1013
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
+ },
1014
1109
  };
@@ -146,6 +146,7 @@ export const providers: ProvidersMap = {
146
146
  'gpt-5.2': models['gpt-5.2'],
147
147
  'gpt-5.2-pro': models['gpt-5.2-pro'],
148
148
  'gpt-5.2-codex': models['gpt-5.2-codex'],
149
+ 'gpt-5.3-codex': models['gpt-5.3-codex'],
149
150
  },
150
151
  createModel: openaiModelCreator,
151
152
  },
@@ -163,6 +164,7 @@ export const providers: ProvidersMap = {
163
164
  'gemini-2.5-flash-lite': models['gemini-2.5-flash-lite-preview-06-17'],
164
165
  'gemini-2.5-pro': models['gemini-2.5-pro'],
165
166
  'gemini-3-pro-preview': models['gemini-3-pro-preview'],
167
+ 'gemini-3.1-pro-preview': models['gemini-3.1-pro-preview'],
166
168
  'gemini-3-flash-preview': models['gemini-3-flash-preview'],
167
169
  },
168
170
  createModel(name, provider) {
@@ -230,6 +232,8 @@ export const providers: ProvidersMap = {
230
232
  'claude-3-5-sonnet-20241022': models['claude-3-5-sonnet-20241022'],
231
233
  'claude-haiku-4-5': models['claude-haiku-4-5'],
232
234
  'claude-opus-4-5': models['claude-opus-4-5'],
235
+ 'claude-opus-4-6': models['claude-opus-4-6'],
236
+ 'claude-sonnet-4-6': models['claude-sonnet-4-6'],
233
237
  },
234
238
  apiFormat: 'anthropic',
235
239
  headers: {
@@ -299,6 +303,8 @@ export const providers: ProvidersMap = {
299
303
  'anthropic/claude-opus-4': models['claude-4-opus'],
300
304
  'anthropic/claude-opus-4.1': models['claude-4.1-opus'],
301
305
  'anthropic/claude-opus-4.5': models['claude-opus-4-5'],
306
+ 'anthropic/claude-sonnet-4.6': models['claude-sonnet-4-6'],
307
+ 'anthropic/claude-opus-4.6': models['claude-opus-4-6'],
302
308
  'deepseek/deepseek-r1-0528': models['deepseek-r1-0528'],
303
309
  'deepseek/deepseek-chat-v3-0324': models['deepseek-v3-0324'],
304
310
  'deepseek/deepseek-chat-v3.1': models['deepseek-v3-1'],
@@ -324,6 +330,7 @@ export const providers: ProvidersMap = {
324
330
  'openai/gpt-5.2': models['gpt-5.2'],
325
331
  'openai/gpt-5.2-pro': models['gpt-5.2-pro'],
326
332
  'openai/gpt-5.2-codex': models['gpt-5.2-codex'],
333
+ 'openai/gpt-5.3-codex': models['gpt-5.3-codex'],
327
334
  'google/gemini-3-flash-preview': models['gemini-3-flash-preview'],
328
335
  'google/gemini-3-pro-preview': models['gemini-3-pro-preview'],
329
336
  'moonshotai/kimi-k2': models['kimi-k2'],
@@ -341,7 +348,9 @@ export const providers: ProvidersMap = {
341
348
  'z-ai/glm-4.6': models['glm-4.6'],
342
349
  'z-ai/glm-4.6v': models['glm-4.6v'],
343
350
  'z-ai/glm-4.7': models['glm-4.7'],
351
+ 'z-ai/glm-5': models['glm-5'],
344
352
  'minimax/minimax-m2': models['minimax-m2'],
353
+ 'minimax/minimax-m2.5': models['minimax-m2.5'],
345
354
  'openrouter/sherlock-dash-alpha': models['sherlock-dash-alpha'],
346
355
  'openrouter/sherlock-think-alpha': models['sherlock-think-alpha'],
347
356
  'xiaomi/mimo-v2-flash:free': models['mimo-v2-flash'],
@@ -360,43 +369,6 @@ export const providers: ProvidersMap = {
360
369
  ).chat(name) as unknown as LanguageModelV3;
361
370
  },
362
371
  },
363
- iflow: {
364
- id: 'iflow',
365
- source: 'built-in',
366
- env: ['IFLOW_API_KEY'],
367
- name: 'iFlow',
368
- api: 'https://apis.iflow.cn/v1/',
369
- doc: 'https://iflow.cn/',
370
- models: {
371
- 'qwen3-coder-plus': models['qwen3-coder-plus'],
372
- 'kimi-k2': models['kimi-k2'],
373
- 'kimi-k2-0905': models['kimi-k2-0905'],
374
- 'deepseek-v3': models['deepseek-v3-0324'],
375
- 'deepseek-v3.2': models['deepseek-v3-2-exp'],
376
- 'deepseek-r1': models['deepseek-r1-0528'],
377
- 'glm-4.6': models['glm-4.6'],
378
- 'glm-4.7': models['glm-4.7'],
379
- 'minimax-m2.1': models['minimax-m2.1'],
380
- 'qwen3-max': models['qwen3-max'],
381
- },
382
- createModel: createModelCreatorCompatible({
383
- fetch: (url: string, options: any) => {
384
- return fetch(url, {
385
- ...options,
386
- headers: {
387
- ...options.headers,
388
- 'user-agent': 'iFlow-Cli',
389
- },
390
- });
391
- },
392
- middlewares: [
393
- mergeSystemMessagesMiddleware,
394
- extractReasoningMiddleware({
395
- tagName: 'think',
396
- }) as LanguageModelV3Middleware,
397
- ],
398
- }),
399
- },
400
372
  moonshotai: {
401
373
  id: 'moonshotai',
402
374
  source: 'built-in',
@@ -476,6 +448,11 @@ export const providers: ProvidersMap = {
476
448
  'deepseek-ai/DeepSeek-V3.1': models['deepseek-v3-1'],
477
449
  'deepseek-ai/DeepSeek-V3': models['deepseek-v3-0324'],
478
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'],
479
456
  },
480
457
  createModel: defaultModelCreator,
481
458
  },
@@ -493,6 +470,7 @@ export const providers: ProvidersMap = {
493
470
  'ZhipuAI/GLM-4.5': models['glm-4.5'],
494
471
  'ZhipuAI/GLM-4.5V': models['glm-4.5v'],
495
472
  'ZhipuAI/GLM-4.6': models['glm-4.6'],
473
+ 'ZhipuAI/GLM-5': models['glm-5'],
496
474
  'deepseek-ai/DeepSeek-V3.2': models['deepseek-v3.2'],
497
475
  'deepseek-ai/DeepSeek-V3.2-Speciale': models['deepseek-v3.2-speciale'],
498
476
  },
@@ -528,6 +506,7 @@ export const providers: ProvidersMap = {
528
506
  'glm-4.6': models['glm-4.6'],
529
507
  'glm-4.6v': models['glm-4.6v'],
530
508
  'glm-4.7': models['glm-4.7'],
509
+ 'glm-5': models['glm-5'],
531
510
  },
532
511
  createModel: defaultModelCreator,
533
512
  },
@@ -546,9 +525,30 @@ export const providers: ProvidersMap = {
546
525
  'glm-4.5-flash': models['glm-4.5-flash'],
547
526
  'glm-4.6v': models['glm-4.6v'],
548
527
  'glm-4.7': models['glm-4.7'],
528
+ 'glm-5': models['glm-5'],
549
529
  },
550
530
  createModel: defaultModelCreator,
551
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
+ },
552
552
  zhipuai: {
553
553
  id: 'zhipuai',
554
554
  source: 'built-in',
@@ -564,6 +564,7 @@ export const providers: ProvidersMap = {
564
564
  'glm-4.5-flash': models['glm-4.5-flash'],
565
565
  'glm-4.6v': models['glm-4.6v'],
566
566
  'glm-4.7': models['glm-4.7'],
567
+ 'glm-5': models['glm-5'],
567
568
  },
568
569
  createModel: defaultModelCreator,
569
570
  },
@@ -588,15 +589,21 @@ export const providers: ProvidersMap = {
588
589
  'openai/gpt-5.1-codex-mini': models['gpt-5.1-codex-mini'],
589
590
  'openai/gpt-5.2': models['gpt-5.2'],
590
591
  'openai/gpt-5.2-pro': models['gpt-5.2-pro'],
592
+ 'openai/gpt-5.2-codex': models['gpt-5.2-codex'],
593
+ 'openai/gpt-5.3-codex': models['gpt-5.3-codex'],
591
594
  'anthropic/claude-sonnet-4.5': models['claude-4-5-sonnet'],
592
595
  'anthropic/claude-opus-4.1': models['claude-4.1-opus'],
593
596
  'anthropic/claude-opus-4.5': models['claude-opus-4-5'],
597
+ 'anthropic/claude-opus-4.6': models['claude-opus-4-6'],
594
598
  'z-ai/glm-4.6': models['glm-4.6'],
595
599
  'z-ai/glm-4.6v': models['glm-4.6v'],
596
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'],
597
603
  'deepseek/deepseek-v3.2-speciale': models['deepseek-v3.2-speciale'],
598
604
  'deepseek/deepseek-chat': models['deepseek-v3-2-exp'],
599
605
  'deepseek/deepseek-reasoner': models['deepseek-r1-0528'],
606
+ 'minimax/minimax-m2.5': models['minimax-m2.5'],
600
607
  },
601
608
  headers: {
602
609
  'X-Title': 'OriCore',
@@ -613,6 +620,8 @@ export const providers: ProvidersMap = {
613
620
  models: {
614
621
  'minimax-m2': models['minimax-m2'],
615
622
  'minimax-m2.1': models['minimax-m2.1'],
623
+ 'minimax-m2.5': models['minimax-m2.5'],
624
+ 'minimax-m2.7': models['minimax-m2.7'],
616
625
  },
617
626
  createModel(name, provider) {
618
627
  const baseURL = getProviderBaseURL(provider);
@@ -632,6 +641,8 @@ export const providers: ProvidersMap = {
632
641
  models: {
633
642
  'minimax-m2': models['minimax-m2'],
634
643
  'minimax-m2.1': models['minimax-m2.1'],
644
+ 'minimax-m2.5': models['minimax-m2.5'],
645
+ 'minimax-m2.7': models['minimax-m2.7'],
635
646
  },
636
647
  createModel(name, provider) {
637
648
  const baseURL = getProviderBaseURL(provider);
@@ -763,6 +774,8 @@ export const providers: ProvidersMap = {
763
774
  'moonshotai/kimi-k2-thinking': models['kimi-k2-thinking'],
764
775
  'moonshotai/kimi-k2.5': models['kimi-k2.5'],
765
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'],
766
779
  },
767
780
  createModel: defaultModelCreator,
768
781
  },
@@ -782,12 +795,15 @@ export const providers: ProvidersMap = {
782
795
  'claude-4-5-sonnet': models['claude-4-5-sonnet'],
783
796
  'claude-haiku-4-5': models['claude-haiku-4-5'],
784
797
  'claude-opus-4-5': models['claude-opus-4-5'],
798
+ 'claude-sonnet-4-6': models['claude-sonnet-4-6'],
799
+ 'claude-opus-4-6': models['claude-opus-4-6'],
785
800
  'gpt-5.1': models['gpt-5.1'],
786
801
  'gpt-5.1-codex-max': models['gpt-5.1-codex-max'],
787
802
  'gpt-5.1-codex': models['gpt-5.1-codex'],
788
803
  'gpt-5.1-codex-mini': models['gpt-5.1-codex-mini'],
789
804
  'gpt-5.2': models['gpt-5.2'],
790
805
  'gpt-5.2-codex': models['gpt-5.2-codex'],
806
+ 'gpt-5.3-codex': models['gpt-5.3-codex'],
791
807
  },
792
808
  createModel: (name, provider) => {
793
809
  if (name.startsWith('claude-') || name.startsWith('gemini-')) {
@@ -799,4 +815,36 @@ export const providers: ProvidersMap = {
799
815
  return defaultModelCreator(name, provider);
800
816
  },
801
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
+ },
802
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);