@@ -2,6 +2,7 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
22import { tmpdir } from 'node:os' ;
33import { join } from 'pathe' ;
44
5+ import type { Kaos } from '@moonshot-ai/kaos' ;
56import { afterEach , describe , expect , it , vi } from 'vitest' ;
67
78import {
@@ -10,6 +11,7 @@ import {
1011 createRPC ,
1112 ErrorCodes ,
1213 KimiCore ,
14+ KimiError ,
1315 type ApprovalResponse ,
1416 type CoreAPI ,
1517 type SDKAPI ,
@@ -21,6 +23,7 @@ import {
2123} from '../../src/logging/logger' ;
2224import { resolveLoggingConfig } from '../../src/logging/resolve-config' ;
2325import type { OAuthTokenProviderResolver } from '../../src/session/provider-manager' ;
26+ import { testKaos } from '../fixtures/test-kaos' ;
2427
2528function requiredFlagEnv ( id : string ) : string {
2629 const def = FLAG_DEFINITIONS . find ( ( item ) => item . id === id ) ;
@@ -39,6 +42,16 @@ function experimentalFeatureEnabled(core: KimiCore, id: string): boolean | undef
3942 return core . getExperimentalFeatures ( ) . find ( ( feature ) => feature . id === id ) ?. enabled ;
4043}
4144
45+ function setCoreKaos ( core : KimiCore , kaos : Promise < Kaos > ) : void {
46+ ( core as unknown as { kaos ?: Promise < Kaos > } ) . kaos = kaos ;
47+ }
48+
49+ function rejectedKaos ( error : Error ) : Promise < Kaos > {
50+ const promise = Promise . reject ( error ) as Promise < Kaos > ;
51+ promise . catch ( ( ) => undefined ) ;
52+ return promise ;
53+ }
54+
4255describe ( 'KimiCore runtime config' , ( ) => {
4356 let tmp : string ;
4457
@@ -284,6 +297,75 @@ max_context_size = 100000
284297 expect ( mainAgent ?. config . modelAlias ) . toBe ( 'default-mock' ) ;
285298 } ) ;
286299
300+ it ( 'rejects createSession when shell runtime initialization fails' , async ( ) => {
301+ tmp = await mkdtemp ( join ( tmpdir ( ) , 'kimi-core-runtime-' ) ) ;
302+ const homeDir = join ( tmp , 'home' ) ;
303+ const workDir = join ( tmp , 'work' ) ;
304+ await mkdir ( homeDir , { recursive : true } ) ;
305+ await mkdir ( workDir , { recursive : true } ) ;
306+ await writeFile ( join ( homeDir , 'config.toml' ) , baseModelConfig ( ) ) ;
307+
308+ const [ coreRpc , sdkRpc ] = createRPC < CoreAPI , SDKAPI > ( ) ;
309+ const core = new KimiCore ( coreRpc , { homeDir } ) ;
310+ const rpc = await sdkRpc ( {
311+ emitEvent : vi . fn ( ) ,
312+ requestApproval : vi . fn ( async ( ) : Promise < ApprovalResponse > => ( { decision : 'rejected' } ) ) ,
313+ requestQuestion : vi . fn ( async ( ) => null ) ,
314+ toolCall : vi . fn ( async ( ) => ( { output : '' } ) ) ,
315+ } ) ;
316+ setCoreKaos (
317+ core ,
318+ rejectedKaos (
319+ new KimiError ( ErrorCodes . SHELL_GIT_BASH_NOT_FOUND , 'Git Bash missing' ) ,
320+ ) ,
321+ ) ;
322+
323+ await expect (
324+ rpc . createSession ( {
325+ id : 'ses_runtime_shell_missing_create' ,
326+ workDir,
327+ model : 'default-mock' ,
328+ } ) ,
329+ ) . rejects . toMatchObject ( { code : ErrorCodes . SHELL_GIT_BASH_NOT_FOUND } ) ;
330+ expect ( core . sessions . has ( 'ses_runtime_shell_missing_create' ) ) . toBe ( false ) ;
331+ } ) ;
332+
333+ it ( 'rejects resumeSession when shell runtime initialization fails' , async ( ) => {
334+ tmp = await mkdtemp ( join ( tmpdir ( ) , 'kimi-core-runtime-' ) ) ;
335+ const homeDir = join ( tmp , 'home' ) ;
336+ const workDir = join ( tmp , 'work' ) ;
337+ await mkdir ( homeDir , { recursive : true } ) ;
338+ await mkdir ( workDir , { recursive : true } ) ;
339+ await writeFile ( join ( homeDir , 'config.toml' ) , baseModelConfig ( ) ) ;
340+
341+ const [ coreRpc , sdkRpc ] = createRPC < CoreAPI , SDKAPI > ( ) ;
342+ const core = new KimiCore ( coreRpc , { homeDir } ) ;
343+ const rpc = await sdkRpc ( {
344+ emitEvent : vi . fn ( ) ,
345+ requestApproval : vi . fn ( async ( ) : Promise < ApprovalResponse > => ( { decision : 'rejected' } ) ) ,
346+ requestQuestion : vi . fn ( async ( ) => null ) ,
347+ toolCall : vi . fn ( async ( ) => ( { output : '' } ) ) ,
348+ } ) ;
349+ setCoreKaos ( core , Promise . resolve ( testKaos ) ) ;
350+ const created = await rpc . createSession ( {
351+ id : 'ses_runtime_shell_missing_resume' ,
352+ workDir,
353+ model : 'default-mock' ,
354+ } ) ;
355+ await rpc . closeSession ( { sessionId : created . id } ) ;
356+ setCoreKaos (
357+ core ,
358+ rejectedKaos (
359+ new KimiError ( ErrorCodes . SHELL_GIT_BASH_NOT_FOUND , 'Git Bash missing' ) ,
360+ ) ,
361+ ) ;
362+
363+ await expect ( rpc . resumeSession ( { sessionId : created . id } ) ) . rejects . toMatchObject ( {
364+ code : ErrorCodes . SHELL_GIT_BASH_NOT_FOUND ,
365+ } ) ;
366+ expect ( core . sessions . has ( created . id ) ) . toBe ( false ) ;
367+ } ) ;
368+
287369 it ( 'reloads an active session with fresh runtime services from config.toml' , async ( ) => {
288370 tmp = await mkdtemp ( join ( tmpdir ( ) , 'kimi-core-runtime-' ) ) ;
289371 const homeDir = join ( tmp , 'home' ) ;
0 commit comments